package org.gcube.informationsystem.resourceregistry.instances.model.entities; import java.util.ArrayList; 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.databind.JsonNode; import org.gcube.com.fasterxml.jackson.databind.node.ArrayNode; import org.gcube.informationsystem.base.reference.AccessType; import org.gcube.informationsystem.base.reference.Element; import org.gcube.informationsystem.base.reference.IdentifiableElement; import org.gcube.informationsystem.context.reference.entities.Context; import org.gcube.informationsystem.model.reference.entities.Facet; import org.gcube.informationsystem.model.reference.entities.Resource; import org.gcube.informationsystem.model.reference.properties.Header; import org.gcube.informationsystem.model.reference.relations.ConsistsOf; import org.gcube.informationsystem.model.reference.relations.IsRelatedTo; 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.entity.resource.ResourceAlreadyPresentException; import org.gcube.informationsystem.resourceregistry.api.exceptions.entity.resource.ResourceAvailableInAnotherContextException; import org.gcube.informationsystem.resourceregistry.api.exceptions.entity.resource.ResourceNotFoundException; import org.gcube.informationsystem.resourceregistry.contexts.security.SecurityContext; import org.gcube.informationsystem.resourceregistry.contexts.security.SecurityContext.PermissionMode; import org.gcube.informationsystem.resourceregistry.instances.model.relations.ConsistsOfManagement; import org.gcube.informationsystem.resourceregistry.instances.model.relations.IsRelatedToManagement; import org.gcube.informationsystem.resourceregistry.instances.model.relations.RelationManagement; import org.gcube.informationsystem.resourceregistry.types.entities.ResourceTypeDefinitionManagement; import org.gcube.informationsystem.resourceregistry.utils.Utility; import org.gcube.informationsystem.types.TypeMapper; import org.gcube.informationsystem.types.reference.entities.ResourceType; import org.gcube.informationsystem.types.reference.properties.LinkedEntity; import com.orientechnologies.orient.core.db.document.ODatabaseDocument; import com.orientechnologies.orient.core.metadata.schema.OClass; import com.orientechnologies.orient.core.record.ODirection; import com.orientechnologies.orient.core.record.OEdge; import com.orientechnologies.orient.core.record.OVertex; /** * @author Luca Frosini (ISTI - CNR) */ public class ResourceManagement extends EntityManagement { public ResourceManagement() { super(AccessType.RESOURCE); } public ResourceManagement(SecurityContext workingContext, ODatabaseDocument oDatabaseDocument) { super(AccessType.RESOURCE, workingContext, oDatabaseDocument); } @Override protected ResourceNotFoundException getSpecificElementNotFoundException(NotFoundException e) { return new ResourceNotFoundException(e.getMessage(), e.getCause()); } @Override public ResourceAvailableInAnotherContextException getSpecificERAvailableInAnotherContextException( String message) { return new ResourceAvailableInAnotherContextException(message); } @Override protected ResourceAlreadyPresentException getSpecificERAlreadyPresentException(String message) { return new ResourceAlreadyPresentException(message); } @Override public JsonNode serializeAsJson() throws ResourceRegistryException { JsonNode sourceResource = serializeSelfOnly(); /* * Cannot get ConsistsOf edge only because is not polymorphic for a * com.tinkerpop.blueprints.Vertex vertex.getEdges(Direction.OUT, * ConsistsOf.NAME); TODO Looks for a different query */ Iterable edges = getElement().getEdges(ODirection.OUT); for(OEdge edge : edges) { @SuppressWarnings("rawtypes") RelationManagement relationManagement = getRelationManagement(edge); relationManagement.setReload(reload); if(relationManagement.giveMeSourceEntityManagementAsIs() == null) { relationManagement.setSourceEntityManagement(this); } if(relationManagement.giveMeSourceEntityManagementAsIs() != this) { StringBuilder errorMessage = new StringBuilder(); errorMessage.append("SourceEntityManagement for "); errorMessage.append(relationManagement.getClass().getSimpleName()); errorMessage.append(" is not the one expected. "); errorMessage.append(Utility.SHOULD_NOT_OCCUR_ERROR_MESSAGE); throw new ResourceRegistryException(errorMessage.toString()); } if(relationManagement instanceof ConsistsOfManagement) { try { JsonNode consistsOf = relationManagement.serializeAsJson(true, true); sourceResource = addConsistsOf(sourceResource, consistsOf); } catch(ResourceRegistryException e) { logger.error("Unable to correctly serialize {}. {}", edge, Utility.SHOULD_NOT_OCCUR_ERROR_MESSAGE); throw e; } catch(Exception e) { logger.error("Unable to correctly serialize {}. {}", edge, Utility.SHOULD_NOT_OCCUR_ERROR_MESSAGE); throw new ResourceRegistryException(e); } } /* * This comment is just to show that IsRelatedTo is not serialized by default as * design choice and not because forget * * else if(orientEdgeType.isSubClassOf(IsRelatedTo.NAME)){ JsonNode * isRelatedTo = relationManagement.serializeAsJson(true, true); sourceResource * = addIsRelatedTo(sourceResource, isRelatedTo); } */ } return sourceResource; } public static JsonNode addConsistsOf(JsonNode sourceResource, JsonNode consistsOf) throws ResourceRegistryException { return addRelation(sourceResource, consistsOf, AccessType.CONSISTS_OF.lowerCaseFirstCharacter()); } public static JsonNode addIsRelatedTo(JsonNode sourceResource, JsonNode isRelatedTo) throws ResourceRegistryException { return addRelation(sourceResource, isRelatedTo, AccessType.IS_RELATED_TO.lowerCaseFirstCharacter()); } @Override protected OVertex reallyCreate() throws ResourceAlreadyPresentException, ResourceRegistryException { createVertex(); String property = AccessType.CONSISTS_OF.lowerCaseFirstCharacter(); if(jsonNode.has(property)) { JsonNode jsonNodeArray = jsonNode.get(property); for(JsonNode consistOfJsonNode : jsonNodeArray) { ConsistsOfManagement com = new ConsistsOfManagement(getWorkingContext(), oDatabaseDocument); com.setJsonNode(consistOfJsonNode); com.setSourceEntityManagement(this); com.internalCreate(); addToRelationManagement(com); } } property = AccessType.IS_RELATED_TO.lowerCaseFirstCharacter(); if(jsonNode.has(property)) { JsonNode jsonNodeArray = jsonNode.get(property); for(JsonNode relationJsonNode : jsonNodeArray) { IsRelatedToManagement irtm = new IsRelatedToManagement(getWorkingContext(), oDatabaseDocument); irtm.setJsonNode(relationJsonNode); irtm.setSourceEntityManagement(this); irtm.internalCreate(); addToRelationManagement(irtm); } } return element; } @Override protected OVertex reallyUpdate() throws ResourceNotFoundException, ResourceRegistryException { getElement(); String property = AccessType.CONSISTS_OF.lowerCaseFirstCharacter(); if(jsonNode.has(property)) { JsonNode jsonNodeArray = jsonNode.get(property); for(JsonNode relationJsonNode : jsonNodeArray) { ConsistsOfManagement com = new ConsistsOfManagement(getWorkingContext(), oDatabaseDocument); com.setJsonNode(relationJsonNode); com.internalCreateOrUdate(); addToRelationManagement(com); } } property = AccessType.IS_RELATED_TO.lowerCaseFirstCharacter(); if(jsonNode.has(property)) { JsonNode jsonNodeArray = jsonNode.get(property); for(JsonNode relationJsonNode : jsonNodeArray) { IsRelatedToManagement irtm = new IsRelatedToManagement(getWorkingContext(), oDatabaseDocument); irtm.setJsonNode(relationJsonNode); irtm.internalUpdate(); addToRelationManagement(irtm); } } return element; } @SuppressWarnings("unchecked") @Override protected boolean reallyDelete() throws ResourceNotFoundException, ResourceRegistryException { // internalDeleteResource(orientGraph, uuid, null); getElement(); Iterable iterable = element.getEdges(ODirection.OUT); Iterator iterator = iterable.iterator(); while(iterator.hasNext()) { OEdge edge = iterator.next(); OClass oClass = getOClass(edge); @SuppressWarnings("rawtypes") RelationManagement relationManagement = null; if(oClass.isSubClassOf(IsRelatedTo.NAME)) { relationManagement = new IsRelatedToManagement(getWorkingContext(), oDatabaseDocument); } else if(oClass.isSubClassOf(ConsistsOf.NAME)) { relationManagement = new ConsistsOfManagement(getWorkingContext(), oDatabaseDocument); } else { logger.warn("{} is not a {} nor a {}. {}", Utility.toJsonString(edge, true), IsRelatedTo.NAME, ConsistsOf.NAME, Utility.SHOULD_NOT_OCCUR_ERROR_MESSAGE); } if(relationManagement != null) { relationManagement.setElement(edge); relationManagement.internalDelete(); } } element.delete(); return true; } @Override protected Map reallyAddToContext(SecurityContext targetSecurityContext) throws ContextException, ResourceRegistryException { Map affectedInstances = new HashMap<>(); if(!dryRunContextSharing) { targetSecurityContext.addElement(getElement(), oDatabaseDocument); } /* * DO NOT UNCOMMENT * // affectedInstances.put(uuid, serializeSelfOnly()); * the instance is added in internalAddToContext() function after * the update of Header metadata i.e. modifiedBy, lastUpdateTime */ if(honourPropagationConstraintsInContextSharing) { Iterable edges = getElement().getEdges(ODirection.OUT); int facetCounter = 0; for(OEdge edge : edges) { RelationManagement relationManagement = getRelationManagement(edge); relationManagement.setDryRunContextSharing(dryRunContextSharing); relationManagement.setHonourPropagationConstraintsInContextSharing(honourPropagationConstraintsInContextSharing); Map resourceCharacterisationInstances = relationManagement.internalAddToContext(targetSecurityContext); if(relationManagement instanceof ConsistsOfManagement) { facetCounter = facetCounter + resourceCharacterisationInstances.size(); } affectedInstances.putAll(resourceCharacterisationInstances); } if(facetCounter == 0) { StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append("Any "); stringBuffer.append(Resource.NAME); stringBuffer.append(" must "); stringBuffer.append(ConsistsOf.NAME); stringBuffer.append(" at least of one "); stringBuffer.append(Facet.NAME); stringBuffer.append(" in any "); stringBuffer.append(Context.NAME); throw new ResourceRegistryException(stringBuffer.toString()); } } return affectedInstances; } public String all(boolean polymorphic) throws ResourceRegistryException { try { oDatabaseDocument = getWorkingContext().getDatabaseDocument(PermissionMode.READER); return reallyGetAll(polymorphic); } catch(ResourceRegistryException e) { throw e; } catch(Exception e) { throw new ResourceRegistryException(e); } finally { if(oDatabaseDocument != null) { oDatabaseDocument.close(); } } } @Override public void sanityCheck(Map expectedInstances) throws ResourceRegistryException { /* * Actually this function only check the Resource constraint, i.e., resource has at least a Facet * in any context. * * We need to load the Resource definition and validate it. * This task is not very easy because we cannot check just the type but * me must take in account that a specialisation instance is valid. * */ read(); ResourceTypeDefinitionManagement resourceTypeDefinitionManagement = new ResourceTypeDefinitionManagement(); resourceTypeDefinitionManagement.setName(elementType); String stringType = resourceTypeDefinitionManagement.read().toString(); ResourceType resourceType = null; try { resourceType = (ResourceType) TypeMapper.deserializeTypeDefinition(stringType); }catch (Exception e) { throw new ResourceRegistryException(e); } // TODO Create something like SchemaDefinitionNotRespected o ConstaintNotRespected Exception List constraint = new ArrayList<>(); List linkedEntities = resourceType.getFacets(); for(LinkedEntity linkedEntity : linkedEntities) { if(linkedEntity.getMin()>0 || linkedEntity.getMax()>0) { constraint.add(linkedEntity); } } boolean found = false; ArrayNode consistsOfArrayNode = (ArrayNode) jsonNode.get(Resource.CONSISTS_OF_PROPERTY); for(JsonNode consistsOfJsonNode : consistsOfArrayNode) { String consistsOfUUIDString = consistsOfJsonNode.get(IdentifiableElement.HEADER_PROPERTY).get(Header.UUID_PROPERTY).asText(); UUID consistsOfUUID = UUID.fromString(consistsOfUUIDString); if(expectedInstances.containsKey(consistsOfUUID)) { // we need to check that also the facets is present JsonNode facetJsonNode = consistsOfJsonNode.get(Relation.TARGET_PROPERTY); String facetUUIDString = facetJsonNode.get(IdentifiableElement.HEADER_PROPERTY).get(Header.UUID_PROPERTY).asText(); UUID facetUUID = UUID.fromString(facetUUIDString); if(!expectedInstances.containsKey(facetUUID)) { StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append("To avoid to have an incosistent graph, add to context no follows cannot add a "); stringBuffer.append(ConsistsOf.NAME); stringBuffer.append(" relation (i.e. "); stringBuffer.append(consistsOfJsonNode.get(Element.CLASS_PROPERTY).asText()); stringBuffer.append(" with UUID "); stringBuffer.append(consistsOfUUIDString); stringBuffer.append(") without indicating the target "); stringBuffer.append(Facet.NAME); stringBuffer.append(" (i.e. "); stringBuffer.append(facetJsonNode.get(Element.CLASS_PROPERTY).asText()); stringBuffer.append(" with UUID "); stringBuffer.append(facetUUIDString); stringBuffer.append(")."); logger.debug(stringBuffer.toString()); throw new ResourceRegistryException(stringBuffer.toString()); } found = true; break; } } // TODO check Resource definition for mandatory ConsistsOf/Facet if(!found) { StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append("To avoid to have an incosistent graph, add to context no follows cannot add a "); stringBuffer.append(Resource.NAME); stringBuffer.append(" (i.e. "); stringBuffer.append(elementType); stringBuffer.append(" with UUID "); stringBuffer.append(uuid.toString()); stringBuffer.append(") without adding at least a "); stringBuffer.append(Facet.NAME); logger.debug(stringBuffer.toString()); throw new ResourceRegistryException(stringBuffer.toString()); } } }