/*  * The contents of this file are subject to the terms of the Common Development and  * Distribution License (the License). You may not use this file except in compliance with the  * License.  *  * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the  * specific language governing permission and limitations under the License.  *  * When distributing Covered Software, include this CDDL Header Notice in each file and include  * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL  * Header, with the fields enclosed by brackets [] replaced by your own identifying  * information: "Portions copyright [year] [name of copyright owner]".  *  * Copyright 2015 ForgeRock AS.  */ package org.forgerock.openidm.managed; import static java.text.MessageFormat.format; import static org.forgerock.openidm.util.RelationshipUtil.REFERENCE_ID; import org.forgerock.json.JsonValue; import org.forgerock.json.resource.BadRequestException; import org.forgerock.json.resource.ReadRequest; import org.forgerock.json.resource.Requests; import org.forgerock.json.resource.ResourceResponse; import org.forgerock.openidm.util.RelationshipUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Validator for reverse (aka bi-directional) relationships. * If the relationship is not found then the relationshipField is invalid, otherwise then the reverse field's * property either needs to be null as a singleton or a collection to accept the new relationship link. */ public class ReverseRelationshipValidator extends RelationshipValidator { private static final Logger logger = LoggerFactory.getLogger(ReverseRelationshipValidator.class); /** * Constructs the validator to validate reverse relationships. * * @param relationshipProvider the provider that owns this validator. */ public ReverseRelationshipValidator(RelationshipProvider relationshipProvider) { super(relationshipProvider); } /** * The read request will lookup the relationshipField and ask for the reverse property to validate its setting * is compatible with the requested relationship field. * * @param relationshipField the field to validate. * @return the request to invoke for validation. */ protected ReadRequest newValidateRequest(JsonValue relationshipField) { return Requests.newReadRequest(relationshipField.get(REFERENCE_ID).asString()) .addField(getRelationshipProvider().getSchemaField().getReversePropertyName()); } /** * The reverse field's property either needs to be null as a singleton or a collection to accept the new * relationship link. * * @param relationshipField the field to be validated. * @param resourceResponse the response from the validation read request. * @throws BadRequestException when the relationship is invalid */ protected void validateSuccessfulReadResponse(JsonValue relationshipField, ResourceResponse resourceResponse) throws BadRequestException { String reversePropertyName = getRelationshipProvider().getSchemaField().getReversePropertyName(); JsonValue reverseProperty = resourceResponse.getContent().get(reversePropertyName); if (reverseProperty.isNotNull() && !reverseProperty.isCollection()) { String ref = relationshipField.get(REFERENCE_ID).asString(); logger.debug(format( "Reference ''{0}''->''{1}'' is in conflict with existing reverse reference ''{1}/{2}''->''{3}''", relationshipField.getPointer(), ref, reversePropertyName, reverseProperty.get(RelationshipUtil.REFERENCE_ID).asString())); // The message is different for the log vs the exception to avoid leaking the reverseProperty's value throw new BadRequestException(format( "Reference ''{0}''->''{1}'' is in conflict with existing reverse reference ''{1}/{2}''", relationshipField.getPointer(), ref, reversePropertyName)); } } }