package org.gcube.informationsystem.resourceregistry.instances.base.entities; import java.util.HashMap; import java.util.Map; 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.ObjectNode; import org.gcube.informationsystem.base.reference.AccessType; import org.gcube.informationsystem.base.reference.entities.EntityElement; import org.gcube.informationsystem.base.reference.relations.RelationElement; 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.entities.EntityAlreadyPresentException; import org.gcube.informationsystem.resourceregistry.contexts.security.SecurityContext; 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.utils.DBUtility; import org.gcube.informationsystem.types.reference.entities.EntityType; import com.arcadedb.database.Document; import com.arcadedb.graph.Edge; import com.arcadedb.graph.MutableVertex; import com.arcadedb.graph.Vertex; import com.arcadedb.remote.RemoteDatabase; /** * @author Luca Frosini (ISTI - CNR) */ public abstract class EntityElementManagement extends ElementManagement { public final static String IN_PREFIX = "in_"; public final static String OUT_PREFIX = "out_"; /** * Provide a cache edge-internal-id -> RelationManagement * this avoid to recreate the relationManagement of already visited edges */ protected Map> relationManagements; protected EntityElementManagement(AccessType accessType) { super(accessType); this.ignoreStartWithKeys.add(IN_PREFIX.toLowerCase()); this.ignoreStartWithKeys.add(OUT_PREFIX.toLowerCase()); this.ignoreStartWithKeys.add(IN_PREFIX.toUpperCase()); this.ignoreStartWithKeys.add(OUT_PREFIX.toUpperCase()); this.relationManagements = new HashMap<>(); } protected EntityElementManagement(AccessType accessType, SecurityContext workingContext, RemoteDatabase database) { this(accessType); this.database = database; setWorkingContext(workingContext); } /* * It works perfectly in case of any kind of update. In case of use from create * the cache does not work by using the ID because until commit the edge has a * fake id starting with - (minus) sign. This not imply any collateral effect * but a better solution is a desiderata. */ protected RelationElementManagement getBaseRelationManagement(Edge edge) throws ResourceRegistryException { String id = edge.getIdentity().toString(); RelationElementManagement relationManagement = relationManagements.get(id); if(relationManagement == null) { relationManagement = ElementManagementUtility.getRelationManagement(getWorkingContext(), database, edge); relationManagements.put(id, relationManagement); } return relationManagement; } protected void addToRelationManagement(RelationElementManagement baseRelationManagement) throws ResourceRegistryException { Document elem = baseRelationManagement.getElement(); String id = elem.getIdentity().toString(); if(relationManagements.get(id) != null && relationManagements.get(id) != baseRelationManagement) { StringBuilder errorMessage = new StringBuilder(); errorMessage.append("Two different instance of "); errorMessage.append(baseRelationManagement.getClass().getSimpleName()); errorMessage.append(" point to the same "); errorMessage.append(elem.getClass().getSimpleName()); errorMessage.append(". "); errorMessage.append(DBUtility.SHOULD_NOT_OCCUR_ERROR_MESSAGE); throw new ResourceRegistryException(errorMessage.toString()); } relationManagements.put(id, baseRelationManagement); } protected static JsonNode addRelation(JsonNode sourceResource, JsonNode relation, String arrayKey) throws ResourceRegistryException { ObjectMapper objectMapper = new ObjectMapper(); ArrayNode relationArray = objectMapper.createArrayNode(); try { if(sourceResource.has(arrayKey)) { relationArray = (ArrayNode) sourceResource.get(arrayKey); } relationArray.add(relation); ((ObjectNode) sourceResource).replace(arrayKey, relationArray); } catch(Exception e) { throw new ResourceRegistryException(e); } return sourceResource; } protected Vertex createVertex() throws EntityAlreadyPresentException, ResourceRegistryException { logger.trace("Going to create {} for {} ({}) using {}", Vertex.class.getSimpleName(), accessType.getName(), typeName, jsonNode); try { // TODO // if(documentType.isAbstract()) { // String error = String.format( // "Trying to create an instance of %s of type %s which is abstract. The operation will be aborted.", // accessType.getName(), typeName); // throw new ResourceRegistryException(error); // } try { if(uuid != null) { Vertex v = getElement(); if(v != null) { String error = String.format("A %s with UUID %s already exist", typeName, uuid.toString()); throw getSpecificAlreadyPresentException(error); } } } catch(NotFoundException e) { try { Document el = ElementManagementUtility.getAnyElementByUUID(uuid); String error = String.format("UUID %s is already used by another Element. This is not allowed.", uuid.toString(), (el instanceof Vertex) ? EntityElement.NAME : RelationElement.NAME); throw new ResourceRegistryException(error); } catch(NotFoundException e1) { // OK the UUID is not already used. } } catch(AvailableInAnotherContextException e) { throw e; } MutableVertex vertexEntity = database.newVertex(typeName); this.element = vertexEntity; if(accessType == AccessType.RESOURCE) { // Facet and relation are created in calling method } else { updateProperties(documentType, element, jsonNode, ignoreKeys, ignoreStartWithKeys); } logger.info("Created {} is {}", Vertex.class.getSimpleName(), DBUtility.getAsStringForLogging(element)); return element; } catch(ResourceRegistryException e) { throw e; } catch(Exception e) { logger.trace("Error while creating {} for {} ({}) using {}", Vertex.class.getSimpleName(), accessType.getName(), typeName, jsonNode, e); throw new ResourceRegistryException("Error Creating " + typeName + " with " + jsonNode, e.getCause()); } } }