package org.gcube.informationsystem.resourceregistry.contexts.entities; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.UUID; import org.gcube.com.fasterxml.jackson.core.JsonProcessingException; 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.com.fasterxml.jackson.databind.node.NullNode; import org.gcube.com.fasterxml.jackson.databind.node.ObjectNode; import org.gcube.informationsystem.base.reference.AccessType; import org.gcube.informationsystem.base.reference.IdentifiableElement; import org.gcube.informationsystem.contexts.reference.entities.Context; import org.gcube.informationsystem.contexts.reference.relations.IsParentOf; import org.gcube.informationsystem.model.reference.relations.Relation; import org.gcube.informationsystem.resourceregistry.api.exceptions.AlreadyPresentException; 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.ContextAlreadyPresentException; import org.gcube.informationsystem.resourceregistry.api.exceptions.contexts.ContextException; import org.gcube.informationsystem.resourceregistry.api.exceptions.contexts.ContextNotFoundException; 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.relations.IsParentOfManagement; import org.gcube.informationsystem.resourceregistry.contexts.security.ContextSecurityContext; import org.gcube.informationsystem.resourceregistry.contexts.security.SecurityContext; import org.gcube.informationsystem.resourceregistry.instances.base.entities.EntityElementManagement; import org.gcube.informationsystem.resourceregistry.queries.operators.QueryConditionalOperator; import org.gcube.informationsystem.resourceregistry.queries.operators.QueryLogicalOperator; import org.gcube.informationsystem.resourceregistry.utils.DBUtility; import org.gcube.informationsystem.serialization.ElementMapper; import org.gcube.informationsystem.types.reference.entities.EntityType; import org.gcube.informationsystem.utils.UUIDManager; import org.gcube.informationsystem.utils.UUIDUtility; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.arcadedb.database.Document; import com.arcadedb.graph.Edge; import com.arcadedb.graph.Vertex; import com.arcadedb.graph.Vertex.DIRECTION; import com.arcadedb.query.sql.executor.ResultSet; import com.arcadedb.remote.RemoteDatabase; /** * @author Luca Frosini (ISTI - CNR) */ public class ContextManagement extends EntityElementManagement { private static Logger logger = LoggerFactory.getLogger(ContextManagement.class); protected String name; private void init() { this.ignoreStartWithKeys.add(Context.PARENT_PROPERTY); this.ignoreStartWithKeys.add(Context.CHILDREN_PROPERTY); this.typeName = Context.NAME; this.forceIncludeMeta = true; this.forceIncludeAllMeta = true; } public ContextManagement() { super(AccessType.CONTEXT); init(); } public ContextManagement(RemoteDatabase database) throws ResourceRegistryException { this(); this.database = database; getWorkingContext(); } @Override public Map getAffectedInstances() { throw new UnsupportedOperationException(); } public String getName() { if (name == null) { if (element == null) { if (jsonNode != null) { name = jsonNode.get(Context.NAME_PROPERTY).asText(); } } else { name = element.getString(Context.NAME_PROPERTY); } } return name; } @Override protected SecurityContext getWorkingContext() throws ResourceRegistryException { if (workingContext == null) { workingContext = ContextSecurityContext.getInstance(); } return workingContext; } @Override protected ContextNotFoundException getSpecificNotFoundException(NotFoundException e) { return new ContextNotFoundException(e.getMessage(), e.getCause()); } @Override protected ContextAlreadyPresentException getSpecificAlreadyPresentException(String message) { return new ContextAlreadyPresentException(message); } protected void checkContext(ContextManagement parentContext) throws ContextNotFoundException, ContextAlreadyPresentException, ResourceRegistryException { StringBuffer select = new StringBuffer(); StringBuffer errorMessage = new StringBuffer(); if (parentContext != null) { String parentId = parentContext.getElement().getIdentity().toString(); select.append("SELECT FROM (TRAVERSE out("); select.append(IsParentOf.NAME); select.append(") FROM "); select.append(parentId); select.append(" MAXDEPTH 1) WHERE "); select.append(Context.NAME_PROPERTY); select.append(QueryConditionalOperator.EQ.getConditionalOperator()); select.append("\""); select.append(getName()); select.append("\""); select.append(QueryLogicalOperator.AND.getLogicalOperator()); select.append(IdentifiableElement.ID_PROPERTY); select.append(QueryConditionalOperator.NE.getConditionalOperator()); select.append("\""); select.append(parentContext.uuid); select.append("\""); errorMessage.append("A "); errorMessage.append(Context.NAME); errorMessage.append(" with "); errorMessage.append(this.getName()); errorMessage.append(" has been already created as child of "); errorMessage.append(parentContext.getElement().toString()); } else { select.append("SELECT FROM "); select.append(Context.NAME); select.append(" WHERE "); select.append(Context.NAME_PROPERTY); select.append(QueryConditionalOperator.EQ.getConditionalOperator()); select.append("\""); select.append(getName()); select.append("\""); select.append(QueryLogicalOperator.AND.getLogicalOperator()); select.append("in(\""); select.append(IsParentOf.NAME); select.append("\").size() = 0"); errorMessage.append("A root "); errorMessage.append(Context.NAME); errorMessage.append(" with "); errorMessage.append(this.getName()); errorMessage.append(" already exist"); } logger.trace("Checking if {} -> {}", errorMessage, select); ResultSet resultSet = database.command("sql", select.toString(), new HashMap<>()); if (resultSet != null && resultSet.hasNext()) { throw new ContextAlreadyPresentException(errorMessage.toString()); } } @Override protected JsonNode createCompleteJsonNode() throws ResourceRegistryException { JsonNode context = serializeSelfAsJsonNode(); int count = 0; Iterable parents = getElement().getEdges(DIRECTION.IN); for (Edge edge : parents) { if (++count > 1) { throw new ContextException("A " + Context.NAME + " can not have more than one parent"); } try { IsParentOfManagement isParentOfManagement = new IsParentOfManagement(database); isParentOfManagement.setElement(edge); isParentOfManagement.includeSource(true); isParentOfManagement.includeTarget(false); JsonNode isParentOf = isParentOfManagement.createCompleteJsonNode(); if (isParentOf != null) { ((ObjectNode) context).replace(Context.PARENT_PROPERTY, isParentOf); } } catch (Exception e) { logger.error("Unable to correctly serialize {}. {}", edge, DBUtility.SHOULD_NOT_OCCUR_ERROR_MESSAGE); throw new ContextException(""); } } Iterable childrenEdges = getElement().getEdges(DIRECTION.OUT); for (Edge edge : childrenEdges) { IsParentOfManagement isParentOfManagement = new IsParentOfManagement(database); isParentOfManagement.setElement(edge); try { JsonNode isParentOf = isParentOfManagement.serializeAsJsonNode(); context = addRelation(context, isParentOf, Context.CHILDREN_PROPERTY); } catch (ResourceRegistryException e) { logger.error("Unable to correctly serialize {}. {}", edge, DBUtility.SHOULD_NOT_OCCUR_ERROR_MESSAGE); throw e; } catch (Exception e) { logger.error("Unable to correctly serialize {}. {}", edge, DBUtility.SHOULD_NOT_OCCUR_ERROR_MESSAGE); throw new ResourceRegistryException(e); } } return context; } @Override protected Vertex reallyCreate() throws AlreadyPresentException, ResourceRegistryException { SecurityContext securityContext = null; SecurityContext parentSecurityContext = null; try { JsonNode isParentOfJsonNode = jsonNode.get(Context.PARENT_PROPERTY); if (isParentOfJsonNode != null && !(isParentOfJsonNode instanceof NullNode)) { JsonNode parentJsonNode = isParentOfJsonNode.get(Relation.SOURCE_PROPERTY); ContextManagement parentContextManagement = new ContextManagement(database); parentContextManagement.setJsonNode(parentJsonNode); UUID parentUUID = parentContextManagement.uuid; parentSecurityContext = ContextUtility.getInstance().getSecurityContextByUUID(parentUUID); checkContext(parentContextManagement); if (uuid == null) { uuid = UUIDManager.getInstance().generateValidUUID(); } createVertex(); IsParentOfManagement isParentOfManagement = new IsParentOfManagement(database); isParentOfManagement.setJsonNode(isParentOfJsonNode); isParentOfManagement.setSourceEntityManagement(parentContextManagement); isParentOfManagement.setTargetEntityManagement(this); isParentOfManagement.internalCreate(); } else { checkContext(null); createVertex(); } securityContext = new SecurityContext(uuid); securityContext.setParentSecurityContext(parentSecurityContext); securityContext.create(database); ContextUtility.getInstance().addSecurityContext(securityContext); return getElement(); } catch (Exception e) { database.rollback(); if (securityContext != null) { securityContext.delete(database); if (parentSecurityContext != null && securityContext != null) { parentSecurityContext.getChildren().remove(securityContext); } ServerContextCache.getInstance().cleanCache(); } throw e; } } @Override protected Vertex reallyUpdate() throws NotFoundException, ResourceRegistryException { boolean parentChanged = false; boolean nameChanged = false; Vertex parent = null; boolean found = false; Iterable iterable = getElement().getVertices(DIRECTION.IN, IsParentOf.NAME); for (Vertex p : iterable) { if (found) { String message = String.format("{} has more than one parent. {}", Context.NAME, DBUtility.SHOULD_NOT_OCCUR_ERROR_MESSAGE); throw new ResourceRegistryException(message.toString()); } parent = p; found = true; } ContextManagement actualParentContextManagement = null; if (parent != null) { actualParentContextManagement = new ContextManagement(database); actualParentContextManagement.setElement(parent); } ContextManagement newParentContextManagement = actualParentContextManagement; JsonNode isParentOfJsonNode = jsonNode.get(Context.PARENT_PROPERTY); JsonNode parentContextJsonNode = null; if (isParentOfJsonNode != null && !(isParentOfJsonNode instanceof NullNode)) { parentContextJsonNode = isParentOfJsonNode.get(Relation.SOURCE_PROPERTY); } if (parentContextJsonNode != null && !(parentContextJsonNode instanceof NullNode)) { UUID parentUUID = UUIDUtility.getUUID(parentContextJsonNode); if (actualParentContextManagement != null) { if (parentUUID.compareTo(actualParentContextManagement.uuid) != 0) { parentChanged = true; } } else { parentChanged = true; } if (parentChanged) { newParentContextManagement = new ContextManagement(database); newParentContextManagement.setJsonNode(parentContextJsonNode); } } else { if (actualParentContextManagement != null) { parentChanged = true; newParentContextManagement = null; } } String oldName = getElement().getString(Context.NAME_PROPERTY); String newName = jsonNode.get(Context.NAME_PROPERTY).asText(); if (oldName.compareTo(newName) != 0) { nameChanged = true; name = newName; } if (parentChanged || nameChanged) { checkContext(newParentContextManagement); } if (parentChanged) { move(newParentContextManagement, false); } element = (Vertex) updateProperties(documentType, getElement(), jsonNode, ignoreKeys, ignoreStartWithKeys); ServerContextCache.getInstance().cleanCache(); return element; } private void move(ContextManagement newParentContextManagement, boolean check) throws ContextNotFoundException, ContextAlreadyPresentException, ResourceRegistryException { if (check) { checkContext(newParentContextManagement); } SecurityContext newParentSecurityContext = null; // Removing the old parent relationship if any Iterable edges = getElement().getEdges(DIRECTION.IN, IsParentOf.NAME); if (edges != null && edges.iterator().hasNext()) { Iterator edgeIterator = edges.iterator(); Edge edge = edgeIterator.next(); IsParentOfManagement isParentOfManagement = new IsParentOfManagement(); isParentOfManagement.setElement(edge); isParentOfManagement.internalDelete(); if (edgeIterator.hasNext()) { throw new ContextException( "Seems that the Context has more than one Parent. " + DBUtility.SHOULD_NOT_OCCUR_ERROR_MESSAGE); } } if (newParentContextManagement != null) { JsonNode isParentOfJsonNode = jsonNode.get(Context.PARENT_PROPERTY); IsParentOfManagement isParentOfManagement = new IsParentOfManagement(database); isParentOfManagement.setJsonNode(isParentOfJsonNode); isParentOfManagement.setSourceEntityManagement(newParentContextManagement); isParentOfManagement.setTargetEntityManagement(this); isParentOfManagement.internalCreate(); newParentSecurityContext = ContextUtility.getInstance() .getSecurityContextByUUID(newParentContextManagement.uuid); } SecurityContext thisSecurityContext = ContextUtility.getInstance().getSecurityContextByUUID(uuid); thisSecurityContext.changeParentSecurityContext(newParentSecurityContext, database); } @Override protected void reallyDelete() throws NotFoundException, ResourceRegistryException { Iterable iterable = getElement().getEdges(DIRECTION.OUT); Iterator iterator = iterable.iterator(); while (iterator.hasNext()) { throw new ContextException("Cannot remove a " + Context.NAME + " having children"); } element.delete(); ContextUtility contextUtility = ContextUtility.getInstance(); SecurityContext securityContext = contextUtility.getSecurityContextByUUID(uuid); securityContext.delete(database); ServerContextCache.getInstance().cleanCache(); } @Override public String reallyGetAll(boolean polymorphic) throws ResourceRegistryException { ObjectMapper objectMapper = new ObjectMapper(); ArrayNode arrayNode = objectMapper.createArrayNode(); Iterable iterable = database.browseClass(typeName, polymorphic); for (Document vertex : iterable) { ContextManagement contextManagement = new ContextManagement(); contextManagement.setForceIncludeMeta(forceIncludeMeta); contextManagement.setForceIncludeAllMeta(forceIncludeAllMeta); contextManagement.setElement((Vertex) vertex); try { JsonNode jsonObject = contextManagement.serializeAsJsonNode(); arrayNode.add(jsonObject); } catch (ResourceRegistryException e) { logger.error("Unable to correctly serialize {}. It will be excluded from results. {}", vertex.toString(), DBUtility.SHOULD_NOT_OCCUR_ERROR_MESSAGE); } } try { return objectMapper.writeValueAsString(arrayNode); } catch (JsonProcessingException e) { throw new ResourceRegistryException(e); } } public String allFromServer(boolean polymorphic) throws ResourceRegistryException { return super.all(polymorphic); } @Override public String all(boolean polymorphic) throws ResourceRegistryException { try { ServerContextCache contextCache = ServerContextCache.getInstance(); List contexts = contextCache.getContexts(); return ElementMapper.marshal(contexts); } catch (JsonProcessingException | ResourceRegistryException e) { return allFromServer(polymorphic); } } public String readFromServer() throws NotFoundException, AvailableInAnotherContextException, ResourceRegistryException { return super.read().toString(); } public String readAsString() throws NotFoundException, AvailableInAnotherContextException, ResourceRegistryException { try { ServerContextCache contextCache = ServerContextCache.getInstance(); return ElementMapper.marshal(contextCache.getContextByUUID(uuid)); } catch (JsonProcessingException | ResourceRegistryException e) { return readFromServer(); } } @Override public void sanityCheck() throws SchemaViolationException, ResourceRegistryException { // Nothing to do } }