529 lines
20 KiB
Java
529 lines
20 KiB
Java
package org.gcube.informationsystem.resourceregistry.instances.model.relation;
|
|
|
|
import java.util.Collection;
|
|
import java.util.HashMap;
|
|
import java.util.Iterator;
|
|
import java.util.Map;
|
|
import java.util.UUID;
|
|
|
|
import org.gcube.informationsystem.base.reference.AccessType;
|
|
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.Relation;
|
|
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.instances.base.ERManagement;
|
|
import org.gcube.informationsystem.resourceregistry.instances.base.ERManagementUtility;
|
|
import org.gcube.informationsystem.resourceregistry.instances.base.relations.BaseRelationManagement;
|
|
import org.gcube.informationsystem.resourceregistry.instances.context.ContextUtility;
|
|
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.security.SecurityContext;
|
|
import org.gcube.informationsystem.resourceregistry.security.SecurityContext.PermissionMode;
|
|
import org.gcube.informationsystem.resourceregistry.utils.PropagationConstraintOrient;
|
|
import org.gcube.informationsystem.resourceregistry.utils.Utility;
|
|
import org.gcube.informationsystem.utils.ISMapper;
|
|
|
|
import com.fasterxml.jackson.databind.JsonNode;
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
import com.fasterxml.jackson.databind.node.ArrayNode;
|
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
|
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<R extends Relation<Resource, TE>, T extends EntityManagement<TE>, TE extends Entity>
|
|
extends BaseRelationManagement<R, ResourceManagement, T, Resource, TE>{
|
|
|
|
public final PropagationConstraint defaultPropagationConstraint;
|
|
|
|
protected RelationManagement(AccessType accessType, Class<TE> targetEntityClass, PropagationConstraint defaultPropagationConstraint) {
|
|
super(accessType, Resource.class, targetEntityClass);
|
|
this.defaultPropagationConstraint = defaultPropagationConstraint;
|
|
}
|
|
|
|
protected RelationManagement(AccessType accessType, Class<TE> targetEntityClass, SecurityContext workingContext, ODatabaseDocument orientGraph,
|
|
PropagationConstraint defaultPropagationConstraint) {
|
|
this(accessType, targetEntityClass, defaultPropagationConstraint);
|
|
this.oDatabaseDocument = orientGraph;
|
|
setWorkingContext(workingContext);
|
|
}
|
|
|
|
/*
|
|
* Needed for ResourceManagement.serializeAsJson() function to check that
|
|
* sourceEntityManagement is the same of the instance is creating this
|
|
* RelationManagement. TODO Look for a workaround
|
|
*/
|
|
public ResourceManagement giveMeSourceEntityManagementAsIs() throws ResourceRegistryException {
|
|
return sourceEntityManagement;
|
|
}
|
|
|
|
public ResourceManagement getSourceEntityManagement() throws ResourceRegistryException {
|
|
if(sourceEntityManagement == null) {
|
|
OVertex source = getElement().getVertex(ODirection.OUT);
|
|
sourceEntityManagement = newSourceEntityManagement();
|
|
sourceEntityManagement.setElement(source);
|
|
}
|
|
sourceEntityManagement.setReload(reload);
|
|
return sourceEntityManagement;
|
|
}
|
|
|
|
public T getTargetEntityManagement() throws ResourceRegistryException {
|
|
if(targetEntityManagement == null) {
|
|
OVertex target = getElement().getVertex(ODirection.IN);
|
|
targetEntityManagement = newTargetEntityManagement();
|
|
targetEntityManagement.setElement(target);
|
|
}
|
|
targetEntityManagement.setReload(reload);
|
|
return targetEntityManagement;
|
|
}
|
|
|
|
public void setSourceEntityManagement(ResourceManagement resourceManagement) {
|
|
this.sourceEntityManagement = resourceManagement;
|
|
}
|
|
|
|
public void setTargetEntityManagement(T targetEntityManagement) {
|
|
this.targetEntityManagement = targetEntityManagement;
|
|
}
|
|
|
|
@Override
|
|
public String serialize() throws ResourceRegistryException {
|
|
return serializeAsJson().toString();
|
|
}
|
|
|
|
@Override
|
|
public JsonNode serializeAsJson() throws ResourceRegistryException {
|
|
return serializeAsJson(true, true);
|
|
}
|
|
|
|
public JsonNode serializeAsJson(boolean includeSource, boolean includeTarget) throws ResourceRegistryException {
|
|
JsonNode relation = serializeSelfOnly();
|
|
|
|
try {
|
|
if(includeSource) {
|
|
EntityManagement<? extends Resource> sourceEntityManagement = getSourceEntityManagement();
|
|
((ObjectNode) relation).replace(Relation.SOURCE_PROPERTY, sourceEntityManagement.serializeSelfOnly());
|
|
}
|
|
|
|
if(includeTarget) {
|
|
EntityManagement<TE> targetEntityManagement = getTargetEntityManagement();
|
|
((ObjectNode) relation).replace(Relation.TARGET_PROPERTY, targetEntityManagement.serializeAsJson());
|
|
}
|
|
|
|
} catch(ResourceRegistryException e) {
|
|
logger.error("Unable to correctly serialize {}. {}", element, Utility.SHOULD_NOT_OCCUR_ERROR_MESSAGE, e);
|
|
throw e;
|
|
} catch(Exception e) {
|
|
logger.error("Unable to correctly serialize {}. {}", element, Utility.SHOULD_NOT_OCCUR_ERROR_MESSAGE, e);
|
|
throw new ResourceRegistryException(e);
|
|
}
|
|
|
|
return relation;
|
|
}
|
|
|
|
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) ERManagementUtility.getEntityManagement(getWorkingContext(),
|
|
oDatabaseDocument, source);
|
|
if(this instanceof IsRelatedToManagement) {
|
|
sourceResource = resourceManagement.serializeAsJson();
|
|
} else if(this instanceof ConsistsOfManagement) {
|
|
sourceResource = resourceManagement.serializeSelfOnly();
|
|
} 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, serializeAsJson());
|
|
} else if(this instanceof ConsistsOfManagement) {
|
|
sourceResource = ResourceManagement.addConsistsOf(sourceResource, serializeAsJson());
|
|
} 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 = ISMapper.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.warn("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.warn("Unable to get {}. Default value ({}) will be used", RemoveConstraint.class.getSimpleName(),
|
|
removeConstraint);
|
|
}
|
|
propagationConstraintOrient.setRemoveConstraint(removeConstraint);
|
|
|
|
return propagationConstraintOrient;
|
|
}
|
|
|
|
protected void checkPropagationConstraint() throws ResourceRegistryException {
|
|
Object object = getElement().getProperty(Relation.PROPAGATION_CONSTRAINT);
|
|
PropagationConstraintOrient pc = getPropagationConstraint((ODocument) object);
|
|
getElement().setProperty(Relation.PROPAGATION_CONSTRAINT, pc, OType.EMBEDDED);
|
|
}
|
|
|
|
@Override
|
|
protected OEdge reallyCreate() throws ResourceRegistryException {
|
|
element = super.reallyCreate();
|
|
|
|
checkPropagationConstraint();
|
|
|
|
logger.info("{} successfully created", elementType);
|
|
|
|
return element;
|
|
}
|
|
|
|
protected ResourceManagement newSourceEntityManagement() throws ResourceRegistryException {
|
|
return new ResourceManagement(getWorkingContext(), oDatabaseDocument);
|
|
}
|
|
|
|
protected abstract T newTargetEntityManagement() throws ResourceRegistryException;
|
|
|
|
@Override
|
|
protected OEdge reallyUpdate() throws ResourceRegistryException {
|
|
|
|
logger.debug("Trying to update {} : {}", elementType, jsonNode);
|
|
|
|
OEdge edge = getElement();
|
|
ERManagement.updateProperties(oClass, edge, jsonNode, ignoreKeys, ignoreStartWithKeys);
|
|
|
|
if(accessType.compareTo(AccessType.CONSISTS_OF) == 0) {
|
|
JsonNode target = jsonNode.get(Relation.TARGET_PROPERTY);
|
|
if(target != null) {
|
|
FacetManagement fm = new FacetManagement(getWorkingContext(), oDatabaseDocument);
|
|
fm.setJsonNode(target);
|
|
fm.internalUpdate();
|
|
}
|
|
}
|
|
|
|
logger.info("{} {} successfully updated", elementType, jsonNode);
|
|
|
|
return edge;
|
|
|
|
}
|
|
|
|
@Override
|
|
protected boolean reallyAddToContext(SecurityContext targetSecurityContext)
|
|
throws ContextException, ResourceRegistryException {
|
|
getElement();
|
|
|
|
AddConstraint addConstraint = AddConstraint.unpropagate;
|
|
|
|
try {
|
|
PropagationConstraint propagationConstraint = Utility.getPropertyDocument(PropagationConstraint.class, element,
|
|
Relation.PROPAGATION_CONSTRAINT);
|
|
if(propagationConstraint.getAddConstraint() != null) {
|
|
addConstraint = propagationConstraint.getAddConstraint();
|
|
} else {
|
|
String error = String.format("%s.%s in %s is null. %s", Relation.PROPAGATION_CONSTRAINT,
|
|
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, 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().internalAddToContext(targetSecurityContext);
|
|
|
|
targetSecurityContext.addElement(getElement(), oDatabaseDocument);
|
|
|
|
break;
|
|
|
|
case unpropagate:
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public boolean forcedAddToContext(SecurityContext targetSecurityContext)
|
|
throws ContextException, ResourceRegistryException {
|
|
|
|
getElement();
|
|
|
|
/* Adding source to Context */
|
|
getSourceEntityManagement().internalAddToContext(targetSecurityContext);
|
|
|
|
/* Adding target to Context */
|
|
getTargetEntityManagement().internalAddToContext(targetSecurityContext);
|
|
|
|
targetSecurityContext.addElement(getElement(), oDatabaseDocument);
|
|
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
protected boolean reallyRemoveFromContext(SecurityContext targetSecurityContext)
|
|
throws ContextException, ResourceRegistryException {
|
|
getElement();
|
|
|
|
RemoveConstraint removeConstraint = RemoveConstraint.keep;
|
|
|
|
try {
|
|
PropagationConstraint propagationConstraint = Utility.getPropertyDocument(PropagationConstraint.class, element,
|
|
Relation.PROPAGATION_CONSTRAINT);
|
|
if(propagationConstraint.getRemoveConstraint() != null) {
|
|
removeConstraint = propagationConstraint.getRemoveConstraint();
|
|
} else {
|
|
String error = String.format("%s.%s in %s is null. %s", Relation.PROPAGATION_CONSTRAINT,
|
|
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, 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);
|
|
|
|
switch(removeConstraint) {
|
|
case cascade:
|
|
getTargetEntityManagement().internalRemoveFromContext(targetSecurityContext);
|
|
break;
|
|
|
|
case cascadeWhenOrphan:
|
|
OVertex target = (OVertex) getTargetEntityManagement().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 {
|
|
getTargetEntityManagement().internalRemoveFromContext(targetSecurityContext);
|
|
}
|
|
break;
|
|
|
|
case keep:
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
protected boolean reallyDelete() throws RelationNotFoundException, ResourceRegistryException {
|
|
logger.debug("Going to remove {} with UUID {}. Related {}s will be detached.", accessType.getName(), uuid,
|
|
targetEntityClass.getSimpleName());
|
|
|
|
getElement();
|
|
|
|
RemoveConstraint removeConstraint = RemoveConstraint.keep;
|
|
|
|
try {
|
|
PropagationConstraint propagationConstraint = Utility.getPropertyDocument(PropagationConstraint.class,
|
|
element, Relation.PROPAGATION_CONSTRAINT);
|
|
if(propagationConstraint.getRemoveConstraint() != null) {
|
|
removeConstraint = propagationConstraint.getRemoveConstraint();
|
|
} else {
|
|
String error = String.format("%s.%s in %s is null. %s", Relation.PROPAGATION_CONSTRAINT,
|
|
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,
|
|
Utility.toJsonString(element, true), removeConstraint, Utility.SHOULD_NOT_OCCUR_ERROR_MESSAGE);
|
|
}
|
|
|
|
OVertex target = getTargetEntityManagement().getElement();
|
|
element.delete();
|
|
|
|
switch(removeConstraint) {
|
|
case cascade:
|
|
getTargetEntityManagement().internalDelete();
|
|
break;
|
|
|
|
case cascadeWhenOrphan:
|
|
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, removeConstraint);
|
|
} else {
|
|
getTargetEntityManagement().internalDelete();
|
|
}
|
|
break;
|
|
|
|
case keep:
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
protected Collection<JsonNode> serializeEdges(Iterable<ODocument> edges, boolean postFilterPolymorphic)
|
|
throws ResourceRegistryException {
|
|
Map<String,JsonNode> visitedSourceResources = new HashMap<>();
|
|
for(ODocument d : edges) {
|
|
OEdge edge = (OEdge) d;
|
|
|
|
// TODO check because it was using compare
|
|
if(postFilterPolymorphic && getOClass().isSubClassOf(elementType)) {
|
|
continue;
|
|
}
|
|
|
|
RelationManagement<R, T, TE> relationManagement = ERManagementUtility.getRelationManagement(getWorkingContext(),
|
|
oDatabaseDocument, edge);
|
|
visitedSourceResources = relationManagement.fullSerialize(visitedSourceResources);
|
|
}
|
|
return visitedSourceResources.values();
|
|
}
|
|
|
|
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(elementType, polymorphic);
|
|
Collection<JsonNode> collection = serializeEdges(edges, false);
|
|
return serializeJsonNodeCollectionAsString(collection);
|
|
}
|
|
|
|
@Override
|
|
public boolean addToContext(UUID contextUUID) throws NotFoundException, ContextException {
|
|
logger.debug("Going to add {} with UUID {} to Context with UUID {}", accessType.getName(), uuid, contextUUID);
|
|
|
|
try {
|
|
oDatabaseDocument = ContextUtility.getAdminSecurityContext().getDatabaseDocument(PermissionMode.WRITER);
|
|
|
|
SecurityContext targetSecurityContext = ContextUtility.getInstance().getSecurityContextByUUID(contextUUID);
|
|
|
|
boolean added = forcedAddToContext(targetSecurityContext);
|
|
|
|
oDatabaseDocument.commit();
|
|
logger.info("{} with UUID {} successfully added to Context with UUID {}", accessType.getName(), uuid,
|
|
contextUUID);
|
|
|
|
return added;
|
|
} catch(Exception e) {
|
|
logger.error("Unable to add {} with UUID {} to Context with UUID {}", accessType.getName(), uuid,
|
|
contextUUID, e);
|
|
if(oDatabaseDocument != null) {
|
|
oDatabaseDocument.rollback();
|
|
}
|
|
throw new ContextException(e);
|
|
} finally {
|
|
if(oDatabaseDocument != null) {
|
|
oDatabaseDocument.close();
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|