839 lines
32 KiB
Java
839 lines
32 KiB
Java
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.contexts.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.DeleteConstraint;
|
|
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.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.contexts.ContextException;
|
|
import org.gcube.informationsystem.resourceregistry.api.exceptions.relations.RelationNotFoundException;
|
|
import org.gcube.informationsystem.resourceregistry.api.exceptions.types.SchemaViolationException;
|
|
import org.gcube.informationsystem.resourceregistry.contexts.ContextUtility;
|
|
import org.gcube.informationsystem.resourceregistry.contexts.ServerContextCache;
|
|
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.requests.RequestUtility;
|
|
import org.gcube.informationsystem.resourceregistry.requests.ServerRequestInfo;
|
|
import org.gcube.informationsystem.resourceregistry.types.TypesCache;
|
|
import org.gcube.informationsystem.resourceregistry.utils.MetadataUtility;
|
|
import org.gcube.informationsystem.resourceregistry.utils.OrientDBUtility;
|
|
import org.gcube.informationsystem.resourceregistry.utils.PropagationConstraintOrient;
|
|
import org.gcube.informationsystem.serialization.ElementMapper;
|
|
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 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<T extends EntityManagement<? extends Entity, TET>, TET extends EntityType>
|
|
extends RelationElementManagement<ResourceManagement, T, ResourceType, TET> 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<? extends Entity> 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;
|
|
|
|
/* The instance is added to the context even is not in source context */
|
|
protected boolean forceAddToContext;
|
|
|
|
/* Indicate that AddToContext skipped the instance 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<String,JsonNode> fullSerialize(Map<String,JsonNode> 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(),
|
|
OrientDBUtility.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(),
|
|
OrientDBUtility.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, OrientDBUtility.toJsonString(oDocument));
|
|
} catch(Exception e) {
|
|
logger.warn("Unable to recreate {}. {}", PropagationConstraint.NAME,
|
|
OrientDBUtility.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);
|
|
|
|
DeleteConstraint deleteConstraint = propagationConstraint.getDeleteConstraint();
|
|
if(deleteConstraint == null) {
|
|
deleteConstraint = defaultPropagationConstraint.getDeleteConstraint();
|
|
logger.debug("Unable to get {}. Default value ({}) will be used", DeleteConstraint.class.getSimpleName(),
|
|
deleteConstraint);
|
|
}else {
|
|
if (this instanceof ConsistsOfManagement && deleteConstraint == DeleteConstraint.keep) {
|
|
deleteConstraint = defaultPropagationConstraint.getDeleteConstraint();
|
|
logger.warn("A {} cannot use {}.{}. Default value ({}) will be used", ConsistsOf.NAME,
|
|
DeleteConstraint.class.getSimpleName(), DeleteConstraint.keep, deleteConstraint);
|
|
|
|
}
|
|
}
|
|
propagationConstraintOrient.setDeleteConstraint(deleteConstraint);
|
|
|
|
|
|
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<ResourceType, TET> 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(!forceAddToContext && !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 = OrientDBUtility.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, OrientDBUtility.getAsStringForException(element),
|
|
OrientDBUtility.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, OrientDBUtility.getAsStringForException(element),
|
|
OrientDBUtility.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 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 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) {
|
|
MetadataUtility.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 = ServerContextCache.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<UUID,JsonNode> 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 = OrientDBUtility.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, OrientDBUtility.getAsStringForException(element),
|
|
OrientDBUtility.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, OrientDBUtility.getAsStringForException(element),
|
|
OrientDBUtility.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<OEdge> iterable = target.getEdges(ODirection.IN);
|
|
Iterator<OEdge> 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();
|
|
MetadataUtility.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();
|
|
|
|
DeleteConstraint deleteConstraint = DeleteConstraint.keep;
|
|
|
|
try {
|
|
PropagationConstraint propagationConstraint = OrientDBUtility.getPropertyDocument(PropagationConstraint.class,
|
|
element, Relation.PROPAGATION_CONSTRAINT_PROPERTY);
|
|
if(propagationConstraint.getDeleteConstraint() != null) {
|
|
deleteConstraint = propagationConstraint.getDeleteConstraint();
|
|
} else {
|
|
String error = String.format("%s.%s in %s is null. %s", Relation.PROPAGATION_CONSTRAINT_PROPERTY,
|
|
PropagationConstraint.DELETE_PROPERTY, OrientDBUtility.getAsStringForException(element),
|
|
OrientDBUtility.SHOULD_NOT_OCCUR_ERROR_MESSAGE);
|
|
|
|
deleteConstraint = DeleteConstraint.values()[propagationConstraint.getRemoveConstraint().ordinal()];
|
|
throw new ResourceRegistryException(error);
|
|
}
|
|
|
|
} catch(Exception e) {
|
|
logger.warn("Error while getting {} from {}. Assuming {}. {}", Relation.PROPAGATION_CONSTRAINT_PROPERTY,
|
|
OrientDBUtility.getAsStringForException(element), deleteConstraint, OrientDBUtility.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(deleteConstraint) {
|
|
case cascade:
|
|
t.internalDelete();
|
|
break;
|
|
|
|
case cascadeWhenOrphan:
|
|
OVertex target = t.getElement();
|
|
Iterable<OEdge> iterable = target.getEdges(ODirection.IN);
|
|
Iterator<OEdge> iterator = iterable.iterator();
|
|
if(iterator.hasNext()) {
|
|
logger.trace("{} point to {} which is not orphan. Giving {} directive, it will be keep.", element,
|
|
target, deleteConstraint);
|
|
} else {
|
|
getTargetEntityManagement().internalDelete();
|
|
}
|
|
break;
|
|
|
|
case keep:
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
affectedInstances.putAll(t.getAffectedInstances());
|
|
|
|
}
|
|
|
|
private Collection<JsonNode> serializeEdges(Iterable<ODocument> edges, boolean postFilterPolymorphic)
|
|
throws ResourceRegistryException {
|
|
|
|
ServerRequestInfo requestInfo = RequestUtility.getRequestInfo().get();
|
|
int limit = requestInfo.getLimit();
|
|
int offset = requestInfo.getOffset();
|
|
|
|
int position = -1;
|
|
int count = 0;
|
|
|
|
// Map<String,JsonNode> visitedSourceResources = new HashMap<>();
|
|
List<JsonNode> serilizedEdges = new ArrayList<>();
|
|
for(ODocument d : edges) {
|
|
if(++position < offset) {
|
|
continue;
|
|
}
|
|
|
|
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());
|
|
if(limit > 0 && ++count >= limit) {
|
|
break;
|
|
}
|
|
}
|
|
return serilizedEdges;
|
|
}
|
|
|
|
protected String serializeJsonNodeCollectionAsString(Collection<JsonNode> 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<ODocument> edges = oDatabaseDocument.browseClass(typeName, polymorphic);
|
|
Collection<JsonNode> collection = serializeEdges(edges, false);
|
|
return serializeJsonNodeCollectionAsString(collection);
|
|
}
|
|
|
|
@Override
|
|
public void setForceAddToContext(Boolean forceAddToContext) {
|
|
this.forceAddToContext = forceAddToContext;
|
|
}
|
|
|
|
}
|