package org.gcube.informationsystem.resourceregistry.instances.model.relations; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.UUID; import org.gcube.com.fasterxml.jackson.databind.JsonNode; import org.gcube.com.fasterxml.jackson.databind.ObjectMapper; import org.gcube.com.fasterxml.jackson.databind.node.ArrayNode; import org.gcube.informationsystem.base.reference.AccessType; import org.gcube.informationsystem.base.reference.relations.RelationElement; import org.gcube.informationsystem.context.reference.entities.Context; import org.gcube.informationsystem.model.reference.entities.Entity; import org.gcube.informationsystem.model.reference.entities.Resource; import org.gcube.informationsystem.model.reference.properties.PropagationConstraint; import org.gcube.informationsystem.model.reference.properties.PropagationConstraint.AddConstraint; import org.gcube.informationsystem.model.reference.properties.PropagationConstraint.RemoveConstraint; import org.gcube.informationsystem.model.reference.relations.ConsistsOf; import org.gcube.informationsystem.model.reference.relations.Relation; import org.gcube.informationsystem.resourceregistry.api.contexts.ContextCache; import org.gcube.informationsystem.resourceregistry.api.exceptions.AvailableInAnotherContextException; import org.gcube.informationsystem.resourceregistry.api.exceptions.NotFoundException; import org.gcube.informationsystem.resourceregistry.api.exceptions.ResourceRegistryException; import org.gcube.informationsystem.resourceregistry.api.exceptions.context.ContextException; import org.gcube.informationsystem.resourceregistry.api.exceptions.relation.RelationNotFoundException; import org.gcube.informationsystem.resourceregistry.api.exceptions.schema.SchemaViolationException; import org.gcube.informationsystem.resourceregistry.contexts.ContextUtility; import org.gcube.informationsystem.resourceregistry.contexts.security.SecurityContext; import org.gcube.informationsystem.resourceregistry.contexts.security.SecurityContext.PermissionMode; import org.gcube.informationsystem.resourceregistry.instances.base.ElementManagement; import org.gcube.informationsystem.resourceregistry.instances.base.ElementManagementUtility; import org.gcube.informationsystem.resourceregistry.instances.base.relations.RelationElementManagement; import org.gcube.informationsystem.resourceregistry.instances.model.ERManagement; import org.gcube.informationsystem.resourceregistry.instances.model.Operation; import org.gcube.informationsystem.resourceregistry.instances.model.entities.EntityManagement; import org.gcube.informationsystem.resourceregistry.instances.model.entities.FacetManagement; import org.gcube.informationsystem.resourceregistry.instances.model.entities.ResourceManagement; import org.gcube.informationsystem.resourceregistry.types.TypesCache; import org.gcube.informationsystem.resourceregistry.utils.HeaderUtility; import org.gcube.informationsystem.resourceregistry.utils.PropagationConstraintOrient; import org.gcube.informationsystem.resourceregistry.utils.Utility; import org.gcube.informationsystem.types.reference.entities.EntityType; import org.gcube.informationsystem.types.reference.entities.ResourceType; import org.gcube.informationsystem.types.reference.relations.RelationType; import org.gcube.informationsystem.utils.ElementMapper; import com.orientechnologies.orient.core.db.document.ODatabaseDocument; import com.orientechnologies.orient.core.metadata.schema.OType; import com.orientechnologies.orient.core.record.ODirection; import com.orientechnologies.orient.core.record.OEdge; import com.orientechnologies.orient.core.record.OVertex; import com.orientechnologies.orient.core.record.impl.ODocument; /** * @author Luca Frosini (ISTI - CNR) */ public abstract class RelationManagement, TET extends EntityType> extends RelationElementManagement implements ERManagement { /** * The source context of an addToContex */ protected SecurityContext sourceSecurityContext; /** * The target context of an addToContex/RemoveFromContext */ protected SecurityContext targetSecurityContext; /** * By the default the system honour the propagation constraints * so this variable is initialised as true. * * To revert a previous operation or for particular a maintenance * we could request to the service do not honour the propagation * constraints but under certain conditions and with some limitation * only. * These limitation are required to keep the system in a consistent * state. * In fact, this directive is only valid for Resources and IsRelatedTo * relations. We need to take in account that to add an * IsRelatedTo to a context always the source and target * Resources must be in such a Context. * Please also take into account that adding a Resource * to a context always imply to honour the propagation constraints * of ConsistsOf relations. In fact, a resource must be characterised * least by one facet in any context it belongs. Giving that we * cannot made assumption on which facets must be used. * A way could be to consider just the facets are mandatory for such a * Resource Type, but the type could not have mandatory facets * (even every Resource Type in the gCube Model has one mandatory facet). * As counterpart, when a Resource is removed from a Context all the facets * charactering it must be removed. * * This option can also be used in conjunction with * {@link ElementManagement#dryRun}=true. * This allow to simulate a sharing operation which requires * do not honour the propagation constraints. */ protected boolean honourPropagationConstraintsInContextSharing; @Override public void setSourceSecurityContext(SecurityContext sourceSecurityContext) { this.sourceSecurityContext = sourceSecurityContext; } @Override public SecurityContext getSourceSecurityContext() { return sourceSecurityContext; } @Override public void setTargetSecurityContext(SecurityContext targetSecurityContext) { this.targetSecurityContext = targetSecurityContext; } @Override public SecurityContext getTargetSecurityContext() { return sourceSecurityContext; } @Override public boolean isHonourPropagationConstraintsInContextSharing() { return honourPropagationConstraintsInContextSharing; } @Override public void setHonourPropagationConstraintsInContextSharing(boolean honourPropagationConstraintsInContextSharing) { this.honourPropagationConstraintsInContextSharing = honourPropagationConstraintsInContextSharing; } public final PropagationConstraint defaultPropagationConstraint; public boolean isAvailableOnContext(SecurityContext securityContext) { try { return securityContext.isElementInContext(element); } catch (ResourceRegistryException e) { return false; } } protected RelationManagement(AccessType accessType, Class targetEntityClass, PropagationConstraint defaultPropagationConstraint) { super(accessType, Resource.class, targetEntityClass); this.defaultPropagationConstraint = defaultPropagationConstraint; /* * By the default the system honour the propagation constraints * so this variable is initialised as true. */ this.honourPropagationConstraintsInContextSharing = true; this.skipped = false; } protected PropagationConstraint propagationConstraint; /* Indicate that AddToContext skipped the isntance because it was not the source context */ protected boolean skipped; @Override public OEdge getElement() throws NotFoundException, AvailableInAnotherContextException, ResourceRegistryException { try { element = super.getElement(); } catch(NotFoundException e) { try { retrieveElementFromAnyContext(); throw getSpecificAvailableInAnotherContextException(typeName == null ? accessType.getName() : typeName + " with UUID " + uuid + " is available in another " + Context.class.getSimpleName()); } catch(AvailableInAnotherContextException e1) { throw e1; } catch(Exception e1) { throw e; } } catch(ResourceRegistryException e) { throw e; } catch(Exception e) { throw new ResourceRegistryException(e); } return element; } /* * Needed for ResourceManagement.serializeAsJson() function to check that * sourceEntityManagement is the same of the instance is creating this * RelationManagement. */ public ResourceManagement giveMeSourceEntityManagementAsIs() throws ResourceRegistryException { return sourceEntityManagement; } protected Map fullSerialize(Map visitedSourceResources) throws ResourceRegistryException { OVertex source = getElement().getVertex(ODirection.OUT); String id = source.getIdentity().toString(); JsonNode sourceResource = visitedSourceResources.get(id); ResourceManagement resourceManagement = null; if(sourceResource == null) { resourceManagement = (ResourceManagement) ElementManagementUtility.getEntityManagement(getWorkingContext(), oDatabaseDocument, source); if(this instanceof IsRelatedToManagement) { sourceResource = resourceManagement.createCompleteJsonNode(); } else if(this instanceof ConsistsOfManagement) { if(includeSource) { sourceResource = resourceManagement.serializeSelfAsJsonNode(); } } else { String error = String.format("{%s is not a %s nor a %s. %s", this, IsRelatedToManagement.class.getSimpleName(), ConsistsOfManagement.class.getSimpleName(), Utility.SHOULD_NOT_OCCUR_ERROR_MESSAGE); throw new ResourceRegistryException(error); } } if(this instanceof IsRelatedToManagement) { sourceResource = ResourceManagement.addIsRelatedTo(sourceResource, serializeAsJsonNode()); } else if(this instanceof ConsistsOfManagement) { sourceResource = ResourceManagement.addConsistsOf(sourceResource, serializeAsJsonNode()); } else { String error = String.format("{%s is not a %s nor a %s. %s", this, IsRelatedToManagement.class.getSimpleName(), ConsistsOfManagement.class.getSimpleName(), Utility.SHOULD_NOT_OCCUR_ERROR_MESSAGE); throw new ResourceRegistryException(error); } visitedSourceResources.put(id, sourceResource); return visitedSourceResources; } protected PropagationConstraintOrient getPropagationConstraint(ODocument oDocument) throws ResourceRegistryException { PropagationConstraintOrient propagationConstraintOrient = new PropagationConstraintOrient(); PropagationConstraint propagationConstraint = null; if(oDocument == null) { propagationConstraint = defaultPropagationConstraint; } else if(oDocument instanceof PropagationConstraintOrient) { propagationConstraint = (PropagationConstraint) oDocument; } else { try { propagationConstraint = ElementMapper.unmarshal(PropagationConstraint.class, oDocument.toJSON()); } catch(Exception e) { logger.warn("Unable to recreate {}. {}", PropagationConstraint.NAME, Utility.SHOULD_NOT_OCCUR_ERROR_MESSAGE); } } AddConstraint addConstraint = propagationConstraint.getAddConstraint(); if(addConstraint == null) { addConstraint = defaultPropagationConstraint.getAddConstraint(); logger.debug("Unable to get {}. Default value ({}) will be used", AddConstraint.class.getSimpleName(), addConstraint); } propagationConstraintOrient.setAddConstraint(addConstraint); RemoveConstraint removeConstraint = propagationConstraint.getRemoveConstraint(); if(removeConstraint == null) { removeConstraint = defaultPropagationConstraint.getRemoveConstraint(); logger.debug("Unable to get {}. Default value ({}) will be used", RemoveConstraint.class.getSimpleName(), removeConstraint); }else { if (this instanceof ConsistsOfManagement && removeConstraint == RemoveConstraint.keep) { removeConstraint = defaultPropagationConstraint.getRemoveConstraint(); logger.warn("A {} cannot use {}.{}. Default value ({}) will be used", ConsistsOf.NAME, RemoveConstraint.class.getSimpleName(), RemoveConstraint.keep, removeConstraint); } } propagationConstraintOrient.setRemoveConstraint(removeConstraint); return propagationConstraintOrient; } protected void checkPropagationConstraint() throws ResourceRegistryException { Object object = getElement().getProperty(Relation.PROPAGATION_CONSTRAINT_PROPERTY); PropagationConstraintOrient pc = getPropagationConstraint((ODocument) object); getElement().setProperty(Relation.PROPAGATION_CONSTRAINT_PROPERTY, pc, OType.EMBEDDED); } @Override protected OEdge reallyCreate() throws ResourceRegistryException { element = super.reallyCreate(); checkPropagationConstraint(); logger.trace("{} successfully created", typeName); return element; } @Override protected ResourceManagement newSourceEntityManagement() throws ResourceRegistryException { ResourceManagement resourceManagement = new ResourceManagement(); resourceManagement.setWorkingContext(getWorkingContext()); resourceManagement.setODatabaseDocument(oDatabaseDocument); return resourceManagement; } protected String getEntityTypeNotValidErrroMessage(String entityPosition, String requiredType, String effectiveType) { StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append("The "); stringBuffer.append(entityPosition); stringBuffer.append(" instance of the relation "); stringBuffer.append(typeName); stringBuffer.append(" is of type "); stringBuffer.append(effectiveType); stringBuffer.append(" which is not a specialisation of "); stringBuffer.append(requiredType); stringBuffer.append("."); return stringBuffer.toString(); } @Override protected void checksourceAndTargetEntityCompliancy() throws NotFoundException, AvailableInAnotherContextException, SchemaViolationException, ResourceRegistryException { String sourceType = sourceEntityManagement.getTypeName(); String targetType = targetEntityManagement.getTypeName(); RelationType relationType = getCachedType().getType(); ResourceType resourceType = relationType.getSource(); TET tet = relationType.getTarget(); String requiredSourceType = resourceType.getName(); String requiredTargetType = tet.getName(); TypesCache typesCache = TypesCache.getInstance(); if(!typeSatified(typesCache, requiredSourceType, sourceType)) { String message = getEntityTypeNotValidErrroMessage(RelationElement.SOURCE_PROPERTY, requiredSourceType, sourceType); throw new SchemaViolationException(message); } if(!typeSatified(typesCache, requiredTargetType, targetType)) { String message = getEntityTypeNotValidErrroMessage(RelationElement.TARGET_PROPERTY, requiredTargetType, targetType); throw new SchemaViolationException(message); } } @Override protected OEdge reallyUpdate() throws ResourceRegistryException { logger.trace("Trying to update {} with UUID {}", typeName, uuid); logger.trace("Trying to update {} : {}", typeName, jsonNode); OEdge edge = getElement(); updateProperties(oClass, edge, jsonNode, ignoreKeys, ignoreStartWithKeys); if(accessType.compareTo(AccessType.CONSISTS_OF) == 0) { JsonNode target = jsonNode.get(Relation.TARGET_PROPERTY); if(target != null) { FacetManagement facetManagement = new FacetManagement(); facetManagement.setWorkingContext(getWorkingContext()); facetManagement.setODatabaseDocument(oDatabaseDocument); facetManagement.setJsonNode(target); facetManagement.internalUpdate(); } } logger.info("{} with UUID {} successfully updated", typeName, uuid); logger.trace("{} {} successfully updated", typeName, jsonNode); return edge; } protected void reallyAddToContext() throws ContextException, ResourceRegistryException { if(!sourceSecurityContext.isElementInContext(getElement())) { // The element in not in the source security context. It will be skipped skipped = true; return; } if(honourPropagationConstraintsInContextSharing) { AddConstraint addConstraint = AddConstraint.unpropagate; try { propagationConstraint = Utility.getPropertyDocument(PropagationConstraint.class, element, Relation.PROPAGATION_CONSTRAINT_PROPERTY); if(propagationConstraint.getAddConstraint() != null) { addConstraint = propagationConstraint.getAddConstraint(); } else { String error = String.format("%s.%s in %s is null. %s", Relation.PROPAGATION_CONSTRAINT_PROPERTY, PropagationConstraint.ADD_PROPERTY, Utility.toJsonString(element, true), Utility.SHOULD_NOT_OCCUR_ERROR_MESSAGE); logger.error(error); throw new ResourceRegistryException(error); } } catch(Exception e) { String error = String.format("Error while getting %s from %s while performing AddToContext. %s", Relation.PROPAGATION_CONSTRAINT_PROPERTY, Utility.toJsonString(element, true), Utility.SHOULD_NOT_OCCUR_ERROR_MESSAGE); logger.warn(error); throw new ResourceRegistryException(error, e); } switch(addConstraint) { case propagate: /* * The relation must be added only in the case the target vertex must be added. * Otherwise we have a relation which point to an entity outside of the context. */ getTargetEntityManagement().setDryRun(dryRun); targetEntityManagement.setHonourPropagationConstraintsInContextSharing(honourPropagationConstraintsInContextSharing); targetEntityManagement.setSourceSecurityContext(sourceSecurityContext); targetEntityManagement.setTargetSecurityContext(targetSecurityContext); targetEntityManagement.internalAddToContext(); affectedInstances.putAll(targetEntityManagement.getAffectedInstances()); targetSecurityContext.addElement(getElement(), oDatabaseDocument); /* * DO NOT UNCOMMENT * // affectedInstances.put(uuid, serializeSelfOnly()); * the relation instance is added in internalAddToContext() function after * the update of Header metadata i.e. modifiedBy, lastUpdateTime */ break; case unpropagate: break; default: break; } }else { targetSecurityContext.addElement(getElement(), oDatabaseDocument); /* * DO NOT UNCOMMENT * // affectedInstances.put(uuid, serializeSelfOnly()); * the relation instance is added in internalAddToContext() function after * the update of Header metadata i.e. modifiedBy, lastUpdateTime */ } } @Override public void internalAddToContext() throws ContextException, ResourceRegistryException { try { operation = Operation.ADD_TO_CONTEXT; reallyAddToContext(); if(!skipped && propagationConstraint.getAddConstraint()==PropagationConstraint.AddConstraint.propagate) { HeaderUtility.updateModifiedByAndLastUpdate(element); element.save(); affectedInstances.put(uuid, serializeAsAffectedInstance()); } } catch(ResourceRegistryException e) { throw e; } catch(Exception e) { throw new ResourceRegistryException( "Error Adding " + typeName + " to " + targetSecurityContext.toString(), e.getCause()); } } public void forcedAddToContext() throws ContextException, ResourceRegistryException { setOperation(Operation.ADD_TO_CONTEXT); /* Adding source to Context */ getSourceEntityManagement().setDryRun(dryRun); sourceEntityManagement.setHonourPropagationConstraintsInContextSharing(honourPropagationConstraintsInContextSharing); sourceEntityManagement.setTargetSecurityContext(targetSecurityContext); sourceEntityManagement.internalAddToContext(); affectedInstances.putAll(sourceEntityManagement.getAffectedInstances()); /* Adding target to Context */ getTargetEntityManagement().setDryRun(dryRun); targetEntityManagement.setHonourPropagationConstraintsInContextSharing(honourPropagationConstraintsInContextSharing); targetEntityManagement.setTargetSecurityContext(targetSecurityContext); targetEntityManagement.internalAddToContext(); affectedInstances.putAll(targetEntityManagement.getAffectedInstances()); targetSecurityContext.addElement(getElement(), oDatabaseDocument); affectedInstances.put(uuid, serializeAsAffectedInstance()); } @Override public void addToContext(UUID contextUUID) throws SchemaViolationException, NotFoundException, ContextException, ResourceRegistryException { String contextFullName = ContextCache.getInstance().getContextFullNameByUUID(contextUUID); logger.debug("Going to add {} with UUID {} to Context with UUID {} (i.e {})", accessType.getName(), uuid, contextUUID, contextFullName); ODatabaseDocument current = ContextUtility.getCurrentODatabaseDocumentFromThreadLocal(); try { workingContext = ContextUtility.getAdminSecurityContext(); oDatabaseDocument = workingContext.getDatabaseDocument(PermissionMode.WRITER); setAsEntryPoint(); sourceSecurityContext = ContextUtility.getCurrentSecurityContext(); targetSecurityContext = ContextUtility.getInstance().getSecurityContextByUUID(contextUUID); forcedAddToContext(); sanityCheck(); if(!dryRun) { oDatabaseDocument.commit(); }else { oDatabaseDocument.rollback(); } logger.info("{} with UUID {} successfully added to Context with UUID {} (i.e {})", accessType.getName(), uuid, contextUUID, contextFullName); } catch(Exception e) { logger.error("Unable to add {} with UUID {} to Context with UUID {} (i.e. {})", accessType.getName(), uuid, contextUUID, contextFullName, e); if(oDatabaseDocument != null) { oDatabaseDocument.rollback(); } throw new ContextException(e); } finally { if(oDatabaseDocument != null) { oDatabaseDocument.close(); } if(current!=null) { current.activateOnCurrentThread(); } } } protected Map reallyRemoveFromContext() throws ContextException, ResourceRegistryException { if(!targetSecurityContext.isElementInContext(getElement())) { // The element in not in the source security context. It will be skipped return affectedInstances; } getSourceEntityManagement().getElement(); RemoveConstraint removeConstraint = RemoveConstraint.keep; try { propagationConstraint = Utility.getPropertyDocument(PropagationConstraint.class, element, Relation.PROPAGATION_CONSTRAINT_PROPERTY); if(propagationConstraint.getRemoveConstraint() != null) { removeConstraint = propagationConstraint.getRemoveConstraint(); } else { String error = String.format("%s.%s in %s is null. %s", Relation.PROPAGATION_CONSTRAINT_PROPERTY, PropagationConstraint.REMOVE_PROPERTY, Utility.toJsonString(element, true), Utility.SHOULD_NOT_OCCUR_ERROR_MESSAGE); logger.error(error); throw new ResourceRegistryException(error); } } catch(Exception e) { String error = String.format("Error while getting %s from %s while performing RemoveFromContext. %s", Relation.PROPAGATION_CONSTRAINT_PROPERTY, Utility.toJsonString(element, true), Utility.SHOULD_NOT_OCCUR_ERROR_MESSAGE); logger.error(error); throw new ResourceRegistryException(error, e); } /* * In any removeConstraint value the relation MUST be removed from context to * avoid to have edge having a source outside of the context. */ targetSecurityContext.removeElement(getElement(), oDatabaseDocument); affectedInstances.put(uuid, serializeAsAffectedInstance()); T targetEntityManagement = getTargetEntityManagement(); targetEntityManagement.setDryRun(dryRun); targetEntityManagement.setHonourPropagationConstraintsInContextSharing(honourPropagationConstraintsInContextSharing); // Not needed targetEntityManagement.setSourceSecurityContext(sourceSecurityContext); targetEntityManagement.setTargetSecurityContext(targetSecurityContext); if(targetEntityManagement instanceof ResourceManagement) { ((ResourceManagement) targetEntityManagement).setSanityCheckNotRequired(); } switch(removeConstraint) { case cascade: targetEntityManagement.internalRemoveFromContext(); affectedInstances.putAll(targetEntityManagement.getAffectedInstances()); break; case cascadeWhenOrphan: OVertex target = (OVertex) targetEntityManagement.getElement(); Iterable iterable = target.getEdges(ODirection.IN); Iterator iterator = iterable.iterator(); int count = 0; OEdge edge = null; while(iterator.hasNext()) { edge = (OEdge) iterator.next(); OEdge thisOEdge = (OEdge) getElement(); if(edge.compareTo(thisOEdge) != 0) { if(thisOEdge.getVertex(ODirection.OUT).compareTo(edge.getVertex(ODirection.OUT)) != 0) { count++; break; } /* * else{ ContextUtility.removeFromActualContext(orientGraph, edge); } */ } } if(count > 0) { logger.trace( "{} point to {} which is not orphan ({} exists). Giving {} directive, it will be not remove from {}.", element, target, edge, removeConstraint, targetSecurityContext); } else { targetEntityManagement.internalRemoveFromContext(); affectedInstances.putAll(targetEntityManagement.getAffectedInstances()); } break; case keep: break; default: break; } return affectedInstances; } @Override public void internalRemoveFromContext() throws ContextException, ResourceRegistryException { try { setOperation(Operation.REMOVE_FROM_CONTEXT); reallyRemoveFromContext(); HeaderUtility.updateModifiedByAndLastUpdate(element); element.save(); affectedInstances.put(uuid, serializeAsAffectedInstance()); } catch(ResourceRegistryException e) { throw e; } catch(Exception e) { throw new ResourceRegistryException( "Error Removing " + typeName + " from " + targetSecurityContext.toString(), e.getCause()); } } @Override public void removeFromContext(UUID contextUUID) throws NotFoundException, ContextException, ResourceRegistryException { logger.debug("Going to remove {} with UUID {} from Context with UUID {}", typeName, uuid, contextUUID); ODatabaseDocument current = ContextUtility.getCurrentODatabaseDocumentFromThreadLocal(); try { workingContext = ContextUtility.getAdminSecurityContext(); oDatabaseDocument = workingContext.getDatabaseDocument(PermissionMode.WRITER); oDatabaseDocument.begin(); setAsEntryPoint(); // Not needed sourceSecurityContext = ContextUtility.getCurrentSecurityContext(); targetSecurityContext = ContextUtility.getInstance().getSecurityContextByUUID(contextUUID); internalRemoveFromContext(); sanityCheck(); if(!dryRun) { oDatabaseDocument.commit(); }else { oDatabaseDocument.rollback(); } logger.info("{} with UUID {} successfully removed from Context with UUID {}", typeName, uuid, contextUUID); } catch(ResourceRegistryException e) { logger.error("Unable to remove {} with UUID {} from Context with UUID {}", typeName, uuid, contextUUID); if(oDatabaseDocument != null) { oDatabaseDocument.rollback(); } throw e; } catch(Exception e) { logger.error("Unable to remove {} with UUID {} from Context with UUID {}", typeName, uuid, contextUUID, e); if(oDatabaseDocument != null) { oDatabaseDocument.rollback(); } throw new ContextException(e); } finally { if(oDatabaseDocument != null) { oDatabaseDocument.close(); } if(current!=null) { current.activateOnCurrentThread(); } } } @Override protected void reallyDelete() throws RelationNotFoundException, ResourceRegistryException { logger.debug("Going to remove {} with UUID {}. Related {}s will be detached.", accessType.getName(), uuid, targetEntityClass.getSimpleName()); getElement(); getSourceEntityManagement().getElement(); RemoveConstraint removeConstraint = RemoveConstraint.keep; try { PropagationConstraint propagationConstraint = Utility.getPropertyDocument(PropagationConstraint.class, element, Relation.PROPAGATION_CONSTRAINT_PROPERTY); if(propagationConstraint.getRemoveConstraint() != null) { removeConstraint = propagationConstraint.getRemoveConstraint(); } else { String error = String.format("%s.%s in %s is null. %s", Relation.PROPAGATION_CONSTRAINT_PROPERTY, PropagationConstraint.REMOVE_PROPERTY, Utility.toJsonString(element, true), Utility.SHOULD_NOT_OCCUR_ERROR_MESSAGE); logger.error(error); throw new ResourceRegistryException(error); } } catch(Exception e) { logger.warn("Error while getting {} from {}. Assuming {}. {}", Relation.PROPAGATION_CONSTRAINT_PROPERTY, Utility.toJsonString(element, true), removeConstraint, Utility.SHOULD_NOT_OCCUR_ERROR_MESSAGE); } // pre-loading target entity because after deleting the relation we will not be able to get it T t = getTargetEntityManagement(); if(targetEntityManagement instanceof ResourceManagement) { ((ResourceManagement) targetEntityManagement).setSanityCheckNotRequired(); } affectedInstances.put(uuid, serializeAsAffectedInstance()); element.delete(); switch(removeConstraint) { case cascade: t.internalDelete(); break; case cascadeWhenOrphan: OVertex target = t.getElement(); Iterable iterable = target.getEdges(ODirection.IN); Iterator iterator = iterable.iterator(); if(iterator.hasNext()) { logger.trace("{} point to {} which is not orphan. Giving {} directive, it will be keep.", element, target, removeConstraint); } else { getTargetEntityManagement().internalDelete(); } break; case keep: break; default: break; } affectedInstances.putAll(t.getAffectedInstances()); } protected Collection serializeEdges(Iterable edges, boolean postFilterPolymorphic) throws ResourceRegistryException { // Map visitedSourceResources = new HashMap<>(); List serilizedEdges = new ArrayList<>(); for(ODocument d : edges) { OEdge edge = (OEdge) d; if(postFilterPolymorphic && getOClass().isSubClassOf(typeName)) { continue; } RelationManagement relationManagement = ElementManagementUtility.getRelationManagement(getWorkingContext(), oDatabaseDocument, edge); // visitedSourceResources = relationManagement.fullSerialize(visitedSourceResources); serilizedEdges.add(relationManagement.serializeAsJsonNode()); } return serilizedEdges; } protected String serializeJsonNodeCollectionAsString(Collection collection) throws ResourceRegistryException { try { ObjectMapper objectMapper = new ObjectMapper(); ArrayNode arrayNode = objectMapper.valueToTree(collection); return objectMapper.writeValueAsString(arrayNode); } catch(Exception e) { throw new ResourceRegistryException(e); } } @Override public String reallyGetAll(boolean polymorphic) throws ResourceRegistryException { Iterable edges = oDatabaseDocument.browseClass(typeName, polymorphic); Collection collection = serializeEdges(edges, false); return serializeJsonNodeCollectionAsString(collection); } }