/** * */ package org.gcube.informationsystem.resourceregistry.er.entity; import java.io.IOException; import java.util.HashSet; import java.util.Set; import java.util.UUID; import org.codehaus.jettison.json.JSONObject; import org.gcube.informationsystem.model.embedded.Header; 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.resourceregistry.api.exceptions.ResourceRegistryException; import org.gcube.informationsystem.resourceregistry.api.exceptions.context.ContextException; import org.gcube.informationsystem.resourceregistry.api.exceptions.entity.EntityAlreadyPresentException; import org.gcube.informationsystem.resourceregistry.api.exceptions.entity.EntityNotFoundException; import org.gcube.informationsystem.resourceregistry.api.exceptions.entity.facet.FacetNotFoundException; import org.gcube.informationsystem.resourceregistry.api.exceptions.entity.resource.ResourceNotFoundException; 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.relation.RelationManagement; 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.OrientGraph; import com.tinkerpop.blueprints.impls.orient.OrientVertex; import com.tinkerpop.blueprints.impls.orient.OrientVertexType; /** * @author Luca Frosini (ISTI - CNR) */ public abstract class EntityManagement { private static Logger logger = LoggerFactory .getLogger(EntityManagement.class); public final String AT = "@"; public final String UNDERSCORE = "_"; protected final Set ignoreKeys; protected final Set ignoreStartWithKeys; protected final Class entityClass; protected final String baseType; protected OrientGraph orientGraph; protected UUID uuid; protected JsonNode jsonNode; protected String entityType; protected Vertex vertex; protected EntityManagement(Class entityClass) { this.ignoreKeys = new HashSet(); this.ignoreKeys.add(Entity.HEADER_PROPERTY); this.ignoreStartWithKeys = new HashSet(); this.ignoreStartWithKeys.add(OrientVertex.CONNECTION_IN_PREFIX .toLowerCase()); this.ignoreStartWithKeys.add(OrientVertex.CONNECTION_OUT_PREFIX .toLowerCase()); this.ignoreStartWithKeys.add(OrientVertex.CONNECTION_IN_PREFIX .toUpperCase()); this.ignoreStartWithKeys.add(OrientVertex.CONNECTION_OUT_PREFIX .toUpperCase()); this.ignoreStartWithKeys.add(AT); this.ignoreStartWithKeys.add(UNDERSCORE); this.entityClass = entityClass; if (Facet.class.isAssignableFrom(entityClass)) { this.baseType = Facet.NAME; } else if (Resource.class.isAssignableFrom(entityClass)) { this.baseType = Resource.NAME; } else { this.baseType = Entity.NAME; } } protected EntityManagement(Class entityClass, OrientGraph orientGraph) { this(entityClass); this.orientGraph = orientGraph; } public void setVertex(Vertex vertex) throws ResourceRegistryException { if (vertex == null) { throw new ResourceRegistryException("Trying to set null Vertex in " + this); } this.vertex = vertex; this.uuid = HeaderUtility.getHeader(vertex).getUUID(); } public void setUUID(UUID uuid) throws ResourceRegistryException { this.uuid = uuid; if (jsonNode != null) { checkUUIDMatch(); } } 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(); } 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.entityType == null) { this.entityType = ERManagement.getClassProperty(jsonNode); } else { checkEntityMatch(); } } public void setEntityType(String entityType) throws ResourceRegistryException { this.entityType = entityType; if (entityType == null || entityType.compareTo("") == 0) { if (Facet.class.isAssignableFrom(entityClass)) { entityType = Facet.NAME; } if (Resource.class.isAssignableFrom(entityClass)) { entityType = Resource.NAME; } } if (jsonNode != null) { checkEntityMatch(); } } protected void checkEntityMatch() throws ResourceRegistryException { String type = ERManagement.getClassProperty(jsonNode); if (type != null && type.compareTo(entityType) != 0) { String error = String .format("Declared resourceType does not match with json representation %s!=%s", entityType, type); logger.trace(error); throw new ResourceRegistryException(error); } try { SchemaManagementImpl.getTypeSchema(entityType, 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(), entityType); throw new ResourceRegistryException(error); } } } public Vertex getVertex() throws EntityNotFoundException, ResourceRegistryException { try { if (vertex == null) { vertex = Utility.getElementByUUID(orientGraph, entityType == null ? baseType : entityType, uuid, Vertex.class); } return vertex; } catch (ResourceRegistryException e) { if (Resource.class.isAssignableFrom(entityClass)) { throw new ResourceNotFoundException(e); } else if (Facet.class.isAssignableFrom(entityClass)) { throw new FacetNotFoundException(e); } else { throw e; } } } protected Vertex createVertex() throws EntityAlreadyPresentException, ResourceRegistryException { logger.trace("Going to create {} for {} ({}) using {}", Vertex.class.getSimpleName(), baseType, entityType, jsonNode); try { Vertex vertexEntity = orientGraph.addVertex("class:" + entityType); try { Vertex v = getVertex(); if (v != null) { String error = String.format( "A %s with UUID %s already exist", entityType, uuid.toString()); throw new EntityAlreadyPresentException(error); } } catch (EntityAlreadyPresentException e) { throw e; } catch (Exception e) { // no header or no header with uuid is provided and it is fine } this.vertex = vertexEntity; Header entityHeader = HeaderUtility.getHeader(jsonNode, true); if (entityHeader != null) { vertex.setProperty(Entity.HEADER_PROPERTY, entityHeader); } else { entityHeader = HeaderUtility.addHeader(vertex, null); } if (Resource.class.isAssignableFrom(entityClass)) { // Facet and relation are created in calling method } else { ERManagement.updateProperties(vertex, jsonNode, ignoreKeys, ignoreStartWithKeys); } ContextUtility.addToActualContext(orientGraph, vertex); ((OrientVertex) vertex).save(); logger.info("Created {} is {}", Vertex.class.getSimpleName(), Utility.toJsonString((OrientVertex) vertex, true)); return vertex; } catch (ResourceRegistryException e) { throw e; } catch (Exception e) { logger.trace("Error while creating {} for {} ({}) using {}", Vertex.class.getSimpleName(), baseType, entityType, jsonNode, e); throw new ResourceRegistryException("Error Creating " + entityType + " with " + jsonNode, e.getCause()); } } public abstract String serialize() throws ResourceRegistryException; public abstract JSONObject serializeAsJson() throws ResourceRegistryException; public abstract Vertex reallyCreate() throws EntityAlreadyPresentException, ResourceRegistryException; public abstract Vertex reallyUpdate() throws EntityNotFoundException, ResourceRegistryException; public abstract boolean reallyDelete() throws EntityNotFoundException, ResourceRegistryException; public boolean reallyAddToContext() throws ContextException, ResourceRegistryException { ContextUtility.addToActualContext(orientGraph, getVertex()); Iterable edges = vertex.getEdges(Direction.OUT); for (Edge edge : edges) { @SuppressWarnings("rawtypes") RelationManagement relationManagement = RelationManagement .getRelationManagement(orientGraph, edge); relationManagement.reallyAddToContext(); } return true; } public boolean reallyRemoveFromContext() throws ContextException, ResourceRegistryException { ContextUtility.removeFromActualContext(orientGraph, getVertex()); Iterable edges = vertex.getEdges(Direction.OUT); for (Edge edge : edges) { @SuppressWarnings("rawtypes") RelationManagement relationManagement = RelationManagement .getRelationManagement(orientGraph, edge); relationManagement.reallyRemoveFromContext(); } return true; } @SuppressWarnings("rawtypes") public static EntityManagement getEntityManagement(OrientGraph orientGraph, Vertex vertex) throws ResourceRegistryException { OrientVertexType orientVertexType = ((OrientVertex) vertex).getType(); EntityManagement entityManagement = null; if (orientVertexType.isSubClassOf(Resource.NAME)) { entityManagement = new ResourceManagement(orientGraph); } else if (orientVertexType.isSubClassOf(Facet.NAME)) { entityManagement = new FacetManagement(orientGraph); } else { String error = String.format("{%s is not a %s nor a %s. " + "This is really strange and should not occur. " + "Please Investigate it.", vertex, Resource.NAME, Facet.NAME); throw new ResourceRegistryException(error); } entityManagement.setVertex(vertex); return entityManagement; } public String create() throws EntityAlreadyPresentException, ResourceRegistryException { try { orientGraph = ContextUtility .getActualSecurityContextGraph(PermissionMode.WRITER); reallyCreate(); 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 EntityNotFoundException, ResourceRegistryException { try { orientGraph = ContextUtility .getActualSecurityContextGraph(PermissionMode.READER); getVertex(); return serialize(); } catch (ResourceRegistryException e) { throw e; } catch (Exception e) { throw new ResourceRegistryException(e); } finally { if (orientGraph != null) { orientGraph.shutdown(); } } } public String update() throws EntityNotFoundException, ResourceRegistryException { try { orientGraph = ContextUtility .getActualSecurityContextGraph(PermissionMode.WRITER); reallyUpdate(); orientGraph.commit(); return serialize(); } catch (ResourceRegistryException e) { if (orientGraph != null) { orientGraph.rollback(); } throw e; } catch (Exception e) { logger.debug("Unable to update {} with UUID {} usign {}", baseType, uuid, jsonNode, e); if (orientGraph != null) { orientGraph.rollback(); } throw new ResourceRegistryException(e); } finally { if (orientGraph != null) { orientGraph.shutdown(); } } } public boolean delete() throws FacetNotFoundException, ResourceRegistryException { logger.debug("Going to delete {} with UUID {}", baseType, uuid); try { orientGraph = ContextUtility .getActualSecurityContextGraph(PermissionMode.WRITER); boolean deleted = reallyDelete(); orientGraph.commit(); logger.info("{} with UUID {} was successfully deleted.", baseType, uuid); return deleted; } catch (ResourceRegistryException e) { logger.error("Unable to delete {} with UUID {}", baseType, uuid, e); if (orientGraph != null) { orientGraph.rollback(); } throw e; } catch (Exception e) { logger.error("Unable to delete {} with UUID {}", baseType, uuid, e); 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(); } } } }