/** * */ package org.gcube.informationsystem.resourceregistry.er.relation; import java.io.IOException; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.UUID; import org.codehaus.jettison.json.JSONException; import org.codehaus.jettison.json.JSONObject; import org.gcube.informationsystem.model.embedded.Header; import org.gcube.informationsystem.model.embedded.PropagationConstraint; import org.gcube.informationsystem.model.embedded.PropagationConstraint.AddConstraint; import org.gcube.informationsystem.model.embedded.PropagationConstraint.RemoveConstraint; import org.gcube.informationsystem.model.entity.Entity; import org.gcube.informationsystem.model.entity.Facet; import org.gcube.informationsystem.model.entity.Resource; import org.gcube.informationsystem.model.relation.ConsistsOf; import org.gcube.informationsystem.model.relation.IsRelatedTo; import org.gcube.informationsystem.model.relation.Relation; 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.SchemaNotFoundException; import org.gcube.informationsystem.resourceregistry.context.ContextUtility; import org.gcube.informationsystem.resourceregistry.context.SecurityContextMapper; import org.gcube.informationsystem.resourceregistry.context.SecurityContextMapper.PermissionMode; import org.gcube.informationsystem.resourceregistry.er.ERManagement; import org.gcube.informationsystem.resourceregistry.er.entity.EntityManagement; import org.gcube.informationsystem.resourceregistry.er.entity.FacetManagement; import org.gcube.informationsystem.resourceregistry.er.entity.ResourceManagement; import org.gcube.informationsystem.resourceregistry.schema.SchemaManagementImpl; import org.gcube.informationsystem.resourceregistry.utils.HeaderUtility; import org.gcube.informationsystem.resourceregistry.utils.Utility; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.tinkerpop.blueprints.Direction; import com.tinkerpop.blueprints.Edge; import com.tinkerpop.blueprints.Vertex; import com.tinkerpop.blueprints.impls.orient.OrientBaseGraph; import com.tinkerpop.blueprints.impls.orient.OrientEdge; import com.tinkerpop.blueprints.impls.orient.OrientEdgeType; import com.tinkerpop.blueprints.impls.orient.OrientGraph; /** * @author Luca Frosini (ISTI - CNR) * */ @SuppressWarnings("rawtypes") public abstract class RelationManagement { private static Logger logger = LoggerFactory .getLogger(RelationManagement.class); public final Set ignoreKeys; public final Set ignoreStartWithKeys; public static final String AT = "@"; public static final String UNDERSCORE = "_"; protected final Class relationClass; protected final String baseType; protected final Class targetEntityClass; protected OrientGraph orientGraph; protected UUID uuid; protected JsonNode jsonNode; protected String relationType; protected Edge edge; protected RelationManagement(Class relationClass) { this.ignoreKeys = new HashSet(); this.ignoreKeys.add(Relation.HEADER_PROPERTY); this.ignoreKeys.add(Relation.TARGET_PROPERTY); this.ignoreKeys.add(Relation.SOURCE_PROPERTY); this.ignoreKeys.add(OrientBaseGraph.CONNECTION_IN.toLowerCase()); this.ignoreKeys.add(OrientBaseGraph.CONNECTION_OUT.toLowerCase()); this.ignoreKeys.add(OrientBaseGraph.CONNECTION_IN.toUpperCase()); this.ignoreKeys.add(OrientBaseGraph.CONNECTION_OUT.toUpperCase()); this.ignoreStartWithKeys = new HashSet(); this.ignoreStartWithKeys.add(AT); this.ignoreStartWithKeys.add(UNDERSCORE); this.relationClass = relationClass; if (ConsistsOf.class.isAssignableFrom(relationClass)) { this.baseType = ConsistsOf.NAME; this.targetEntityClass = Facet.class; } else if (IsRelatedTo.class.isAssignableFrom(relationClass)) { this.baseType = IsRelatedTo.NAME; this.targetEntityClass = Resource.class; } else { this.baseType = Relation.NAME; this.targetEntityClass = Resource.class; } } protected RelationManagement(Class relationClass, OrientGraph orientGraph) { this(relationClass); this.orientGraph = orientGraph; } public void setEdge(Edge edge) { this.edge = edge; } public void setUUID(UUID uuid) throws ResourceRegistryException { this.uuid = uuid; if (jsonNode != null) { checkUUIDMatch(); } } protected void checkJSON() throws ResourceRegistryException { if (uuid == null) { try { uuid = org.gcube.informationsystem.impl.utils.Utility .getUUIDFromJsonNode(jsonNode); } catch (Exception e) { } } else { checkUUIDMatch(); } if (this.relationType == null) { this.relationType = ERManagement.getClassProperty(jsonNode); } else { checkEntityMatch(); } } public void setJSON(JsonNode jsonNode) throws ResourceRegistryException { this.jsonNode = jsonNode; checkJSON(); } public void setJSON(String jsonRepresentation) throws ResourceRegistryException { ObjectMapper mapper = new ObjectMapper(); try { this.jsonNode = mapper.readTree(jsonRepresentation); } catch (IOException e) { throw new ResourceRegistryException(e); } checkJSON(); } public void setRelationType(String relationType) throws ResourceRegistryException { this.relationType = relationType; if (relationType == null || relationType.compareTo("") == 0) { if (ConsistsOf.class.isAssignableFrom(relationClass)) { this.relationType = ConsistsOf.NAME; } else if (IsRelatedTo.class.isAssignableFrom(relationClass)) { this.relationType = IsRelatedTo.NAME; } } if (jsonNode != null) { checkEntityMatch(); } } protected void checkEntityMatch() throws ResourceRegistryException { String type = ERManagement.getClassProperty(jsonNode); if (type != null && type.compareTo(relationType) != 0) { String error = String .format("Declared resourceType does not match with json representation %s!=%s", relationType, type); logger.trace(error); throw new ResourceRegistryException(error); } try { SchemaManagementImpl.getTypeSchema(relationType, baseType); } catch (SchemaNotFoundException e) { throw e; } } protected void checkUUIDMatch() throws ResourceRegistryException { Header header = null; try { header = HeaderUtility.getHeader(jsonNode, false); } catch (Exception e) { throw new ResourceRegistryException(e); } if (header != null) { UUID resourceUUID = header.getUUID(); if (resourceUUID.compareTo(uuid) != 0) { String error = String .format("UUID provided in header (%s) differs from the one (%s) used to identify the %s instance", resourceUUID.toString(), uuid.toString(), relationType); throw new ResourceRegistryException(error); } } } public Edge getEdge() throws ResourceRegistryException { try { if (edge == null) { edge = Utility.getElementByUUID(orientGraph, relationType == null ? baseType : relationType, uuid, Edge.class); } return edge; } catch (ResourceRegistryException e) { throw new RelationNotFoundException(e); } } public String serialize() throws ResourceRegistryException { return serializeAsJson().toString(); } public JSONObject serializeAsJson() throws ResourceRegistryException { JSONObject ret = Utility.toJsonObject((OrientEdge) getEdge(), false); Vertex vertex = edge.getVertex(Direction.IN); EntityManagement entityManagement = EntityManagement .getEntityManagement(orientGraph, vertex); try { ret.put(Relation.TARGET_PROPERTY, entityManagement.serializeAsJson()); } catch (JSONException e) { new ResourceRegistryException(e); } return ret; } public Edge reallyCreate(UUID sourceUUID, UUID targetUUID) throws ResourceRegistryException { ResourceManagement srmSource = new ResourceManagement(orientGraph); srmSource.setUUID(sourceUUID); Vertex source = srmSource.getVertex(); EntityManagement entityManagement = getEntityManagement(); entityManagement.setUUID(targetUUID); Vertex target = entityManagement.getVertex(); return reallyCreate(source, target); } protected Edge reallyCreate(Vertex source, Vertex target) throws ResourceRegistryException { EntityManagement sourceEntityManagement = EntityManagement .getEntityManagement(orientGraph, source); EntityManagement targetEntityManagement = EntityManagement .getEntityManagement(orientGraph, target); if (!(sourceEntityManagement instanceof ResourceManagement)) { String error = String.format( "Any type of %s can have only a %s as %s. " + "Cannot instatiate %s beetween %s -> %s ", Relation.NAME, Resource.NAME, Relation.SOURCE_PROPERTY, relationType, sourceEntityManagement.serialize(), targetEntityManagement.serialize()); throw new ResourceRegistryException(error); } if (this instanceof IsRelatedToManagement) { if (!(targetEntityManagement instanceof ResourceManagement)) { String error = String.format("A %s can have only a %s as %s. " + "Cannot instatiate %s beetween %s -> %s ", baseType, Resource.NAME, Relation.TARGET_PROPERTY, relationType, sourceEntityManagement.serialize(), targetEntityManagement.serialize()); throw new ResourceRegistryException(error); } } else if (this instanceof ConsistsOfManagement) { if (!(targetEntityManagement instanceof FacetManagement)) { String error = String.format("A %s can have only a %s as %s. " + "Cannot instatiate %s beetween %s -> %s ", baseType, Facet.NAME, Relation.TARGET_PROPERTY, relationType, sourceEntityManagement.serialize(), targetEntityManagement.serialize()); throw new ResourceRegistryException(error); } } else { String error = String.format("{%s is not a %s nor a %s. " + "This is really strange and should not occur. " + "Please Investigate it.", this, IsRelatedToManagement.class.getSimpleName(), ConsistsOfManagement.class.getSimpleName()); throw new ResourceRegistryException(error); } logger.trace("Creating {} beetween {} -> {}", relationType, sourceEntityManagement.serialize(), targetEntityManagement.serialize()); edge = orientGraph.addEdge(null, source, target, relationType); ERManagement.updateProperties(edge, jsonNode, ignoreKeys, ignoreStartWithKeys); HeaderUtility.addHeader(edge, null); ContextUtility.addToActualContext(orientGraph, edge); ((OrientEdge) edge).save(); logger.info("{} successfully created", relationType); return edge; } public Edge reallyCreate(Vertex source) throws ResourceRegistryException { Vertex target = null; EntityManagement entityManagement = getEntityManagement(); if (!jsonNode.has(Relation.TARGET_PROPERTY)) { throw new ResourceRegistryException( "Error while creating relation. No target definition found"); } entityManagement.setJSON(jsonNode.get(Relation.TARGET_PROPERTY)); try { target = entityManagement.getVertex(); } catch (Exception e) { target = entityManagement.reallyCreate(); } return reallyCreate(source, target); } public Edge reallyCreate(UUID sourceUUID) throws ResourceRegistryException { ResourceManagement srmSource = new ResourceManagement(orientGraph); srmSource.setUUID(sourceUUID); Vertex source = srmSource.getVertex(); return reallyCreate(source); } public Edge reallyUpdate() throws ResourceRegistryException { logger.debug("Trying to update {} : {}", relationType, jsonNode); Edge edge = getEdge(); ERManagement.updateProperties(edge, jsonNode, ignoreKeys, ignoreStartWithKeys); if (ConsistsOf.class.isAssignableFrom(relationClass)) { JsonNode target = jsonNode.get(Relation.TARGET_PROPERTY); if (target != null) { FacetManagement fm = new FacetManagement(orientGraph); fm.setJSON(target); fm.reallyUpdate(); } } logger.info("{} {} successfully updated", relationType, jsonNode); return edge; } public boolean reallyAddToContext() throws ContextException, ResourceRegistryException { getEdge(); AddConstraint addConstraint = AddConstraint.unpropagate; try { PropagationConstraint propagationConstraint = Utility.getEmbedded( PropagationConstraint.class, edge, Relation.PROPAGATION_CONSTRAINT); if (propagationConstraint.getAddConstraint() != null) { addConstraint = propagationConstraint.getAddConstraint(); } } catch (Exception e) { logger.warn("Error while getting {} from {}. Assuming {}. " + "This is really strange and should not occur. " + "Please Investigate it.", Relation.PROPAGATION_CONSTRAINT, Utility.toJsonString(edge, true), addConstraint); } Vertex target = edge.getVertex(Direction.IN); 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. */ ContextUtility.addToActualContext(orientGraph, getEdge()); EntityManagement entityManagement = EntityManagement .getEntityManagement(orientGraph, target); entityManagement.reallyAddToContext(); break; case unpropagate: break; default: break; } return true; } protected boolean removeFromContextTargetVertex(Vertex target) throws ResourceRegistryException { EntityManagement entityManagement = EntityManagement .getEntityManagement(orientGraph, target); if (entityManagement != null) { entityManagement.reallyRemoveFromContext(); return true; } else { return false; } } public boolean reallyRemoveFromContext() throws ContextException, ResourceRegistryException { getEdge(); RemoveConstraint removeConstraint = RemoveConstraint.keep; try { PropagationConstraint propagationConstraint = Utility.getEmbedded( PropagationConstraint.class, edge, Relation.PROPAGATION_CONSTRAINT); if (propagationConstraint.getRemoveConstraint() != null) { removeConstraint = propagationConstraint.getRemoveConstraint(); } } catch (Exception e) { logger.warn("Error while getting {} from {}. Assuming {}. " + "This is really strange and should not occur. " + "Please Investigate it.", Relation.PROPAGATION_CONSTRAINT, Utility.toJsonString(edge, true), removeConstraint); } Vertex target = edge.getVertex(Direction.IN); /* * In any removeConstraint value the relation MUSt be removed from * context to avoid to have edge having a source outside of the context. */ ContextUtility.removeFromActualContext(orientGraph, edge); switch (removeConstraint) { case cascade: removeFromContextTargetVertex(target); break; case cascadeWhenOrphan: Iterable iterable = target.getEdges(Direction.IN); Iterator iterator = iterable.iterator(); if (iterator.hasNext()) { logger.trace( "{} point to {} which is not orphan. Giving {} directive, it will be not remove from current context.", edge, target, removeConstraint); } else { removeFromContextTargetVertex(target); } break; case keep: break; default: break; } return true; } protected EntityManagement getEntityManagement() throws ResourceRegistryException { EntityManagement entityManagement; if (ConsistsOf.class.isAssignableFrom(relationClass)) { entityManagement = new FacetManagement(orientGraph); } else if (IsRelatedTo.class.isAssignableFrom(relationClass)) { entityManagement = new ResourceManagement(orientGraph); } else { String error = String.format("{%s is not a %s nor a %s. " + "This is really strange ad should not occur. " + "Please Investigate it.", relationClass, ConsistsOf.NAME, IsRelatedTo.NAME); throw new ResourceRegistryException(error); } return entityManagement; } public static RelationManagement getRelationManagement( OrientGraph orientGraph, Edge edge) throws ResourceRegistryException { OrientEdgeType orientEdgeType = ((OrientEdge) edge).getType(); RelationManagement relationManagement = null; if (orientEdgeType.isSubClassOf(ConsistsOf.NAME)) { relationManagement = new ConsistsOfManagement(orientGraph); } else if (orientEdgeType.isSubClassOf(IsRelatedTo.NAME)) { relationManagement = new IsRelatedToManagement(orientGraph); } else { String error = String.format("{%s is not a %s nor a %s. " + "This is really strange ad should not occur. " + "Please Investigate it.", edge, ConsistsOf.NAME, IsRelatedTo.NAME); throw new ResourceRegistryException(error); } relationManagement.setEdge(edge); return relationManagement; } protected boolean deleteTargetVertex(Vertex target) throws ResourceRegistryException { EntityManagement entityManagement = EntityManagement .getEntityManagement(orientGraph, target); if (entityManagement != null) { entityManagement.reallyDelete(); return true; } else { return false; } } public boolean reallyDelete() throws RelationNotFoundException, ResourceRegistryException { getEdge(); RemoveConstraint removeConstraint = RemoveConstraint.keep; try { PropagationConstraint propagationConstraint = Utility.getEmbedded( PropagationConstraint.class, edge, Relation.PROPAGATION_CONSTRAINT); if (propagationConstraint.getRemoveConstraint() != null) { removeConstraint = propagationConstraint.getRemoveConstraint(); } } catch (Exception e) { logger.warn("Error while getting {} from {}. Assuming {}. " + "This is really strange and should not occur. " + "Please Investigate it.", Relation.PROPAGATION_CONSTRAINT, Utility.toJsonString(edge, true), removeConstraint); } Vertex target = edge.getVertex(Direction.IN); edge.remove(); switch (removeConstraint) { case cascade: deleteTargetVertex(target); break; case cascadeWhenOrphan: Iterable iterable = target.getEdges(Direction.IN); Iterator iterator = iterable.iterator(); if (iterator.hasNext()) { logger.trace( "{} point to {} which is not orphan. Giving {} directive, it will be keep.", edge, target, removeConstraint); } else { deleteTargetVertex(target); } break; case keep: break; default: break; } return true; } public String create(UUID sourceUUID, UUID targetUUID) throws ResourceRegistryException { try { orientGraph = ContextUtility .getActualSecurityContextGraph(PermissionMode.WRITER); edge = reallyCreate(sourceUUID, targetUUID); orientGraph.commit(); return serialize(); } catch (ResourceRegistryException e) { if (orientGraph != null) { orientGraph.rollback(); } throw e; } catch (Exception e) { if (orientGraph != null) { orientGraph.rollback(); } throw new ResourceRegistryException(e); } finally { if (orientGraph != null) { orientGraph.shutdown(); } } } public String read() throws RelationNotFoundException, ResourceRegistryException { try { orientGraph = ContextUtility .getActualSecurityContextGraph(PermissionMode.WRITER); return serialize(); } catch (ResourceRegistryException e) { if (orientGraph != null) { orientGraph.rollback(); } throw e; } catch (Exception e) { if (orientGraph != null) { orientGraph.rollback(); } throw new ResourceRegistryException(e); } finally { if (orientGraph != null) { orientGraph.shutdown(); } } } public String update() throws RelationNotFoundException, ResourceRegistryException { try { orientGraph = ContextUtility .getActualSecurityContextGraph(PermissionMode.WRITER); edge = reallyUpdate(); return serialize(); } catch (ResourceRegistryException e) { if (orientGraph != null) { orientGraph.rollback(); } throw e; } catch (Exception e) { if (orientGraph != null) { orientGraph.rollback(); } throw new ResourceRegistryException(e); } finally { if (orientGraph != null) { orientGraph.shutdown(); } } } public boolean delete() throws RelationNotFoundException, ResourceRegistryException { logger.debug( "Going to remove {} with UUID {}. Related {}s will be detached.", baseType, uuid, targetEntityClass.getSimpleName()); try { orientGraph = ContextUtility .getActualSecurityContextGraph(PermissionMode.WRITER); boolean deleted = reallyDelete(); orientGraph.commit(); logger.info("{} {} with UUID {} successfully removed.", baseType, uuid); return deleted; } catch (ResourceRegistryException e) { logger.error("Unable to remove {} with UUID.", baseType, uuid); if (orientGraph != null) { orientGraph.rollback(); } throw e; } catch (Exception e) { logger.error("Unable to remove {} with UUID {}.", baseType, uuid); if (orientGraph != null) { orientGraph.rollback(); } throw new ResourceRegistryException(e); } finally { if (orientGraph != null) { orientGraph.shutdown(); } } } public boolean addToContext() throws ContextException { logger.debug("Going to add {} with UUID {} to actual Context", baseType, uuid); try { orientGraph = SecurityContextMapper.getSecurityContextFactory( SecurityContextMapper.ADMIN_SECURITY_CONTEXT_UUID, PermissionMode.WRITER).getTx(); boolean added = reallyAddToContext(); orientGraph.commit(); logger.info("{} with UUID {} successfully added to actual Context", baseType, uuid); return added; } catch (Exception e) { logger.error("Unable to add {} with UUID {} to actual Context", baseType, uuid, e); if (orientGraph != null) { orientGraph.rollback(); } throw new ContextException(e); } finally { if (orientGraph != null) { orientGraph.shutdown(); } } } public boolean removeFromContext() throws ContextException { logger.debug("Going to remove {} with UUID {} from actual Context", baseType, uuid); try { orientGraph = SecurityContextMapper.getSecurityContextFactory( SecurityContextMapper.ADMIN_SECURITY_CONTEXT_UUID, PermissionMode.WRITER).getTx(); boolean removed = reallyRemoveFromContext(); orientGraph.commit(); logger.info( "{} with UUID {} successfully removed from actual Context", baseType, uuid); return removed; } catch (Exception e) { logger.error( "Unable to remove {} with UUID {} from actual Context", baseType, uuid, e); if (orientGraph != null) { orientGraph.rollback(); } throw new ContextException(e); } finally { if (orientGraph != null) { orientGraph.shutdown(); } } } }