909 lines
32 KiB
Java
909 lines
32 KiB
Java
package org.gcube.informationsystem.resourceregistry.instances.model.entities;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
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.ObjectNode;
|
|
import org.gcube.informationsystem.base.reference.AccessType;
|
|
import org.gcube.informationsystem.contexts.reference.entities.Context;
|
|
import org.gcube.informationsystem.model.reference.entities.Entity;
|
|
import org.gcube.informationsystem.model.reference.entities.Facet;
|
|
import org.gcube.informationsystem.model.reference.entities.Resource;
|
|
import org.gcube.informationsystem.model.reference.relations.ConsistsOf;
|
|
import org.gcube.informationsystem.model.reference.relations.Relation;
|
|
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.ContextException;
|
|
import org.gcube.informationsystem.resourceregistry.api.exceptions.entities.EntityAlreadyPresentException;
|
|
import org.gcube.informationsystem.resourceregistry.api.exceptions.queries.InvalidQueryException;
|
|
import org.gcube.informationsystem.resourceregistry.api.exceptions.types.SchemaException;
|
|
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.security.SecurityContext;
|
|
import org.gcube.informationsystem.resourceregistry.contexts.security.SecurityContext.PermissionMode;
|
|
import org.gcube.informationsystem.resourceregistry.instances.base.ElementManagement;
|
|
import org.gcube.informationsystem.resourceregistry.instances.base.ElementManagementUtility;
|
|
import org.gcube.informationsystem.resourceregistry.instances.base.entities.EntityElementManagement;
|
|
import org.gcube.informationsystem.resourceregistry.instances.model.ERManagement;
|
|
import org.gcube.informationsystem.resourceregistry.instances.model.Operation;
|
|
import org.gcube.informationsystem.resourceregistry.instances.model.relations.RelationManagement;
|
|
import org.gcube.informationsystem.resourceregistry.types.TypesCache;
|
|
import org.gcube.informationsystem.resourceregistry.utils.MetadataUtility;
|
|
import org.gcube.informationsystem.resourceregistry.utils.DBUtility;
|
|
import org.gcube.informationsystem.types.reference.entities.EntityType;
|
|
|
|
import com.arcadedb.database.Document;
|
|
import com.arcadedb.database.RID;
|
|
import com.arcadedb.graph.Edge;
|
|
import com.arcadedb.graph.MutableVertex;
|
|
import com.arcadedb.graph.Vertex;
|
|
import com.arcadedb.graph.Vertex.DIRECTION;
|
|
import com.arcadedb.query.sql.executor.Result;
|
|
import com.arcadedb.query.sql.executor.ResultSet;
|
|
import com.arcadedb.remote.RemoteDatabase;
|
|
import com.arcadedb.schema.DocumentType;
|
|
|
|
/**
|
|
* @author Luca Frosini (ISTI - CNR)
|
|
*/
|
|
public abstract class EntityManagement<E extends Entity, ET extends EntityType> extends EntityElementManagement<E, ET> implements ERManagement {
|
|
|
|
/**
|
|
* The source context of an addToContex
|
|
*/
|
|
protected SecurityContext sourceSecurityContext;
|
|
|
|
/**
|
|
* The target context of an addToContex/RemoveFromContext
|
|
*/
|
|
protected SecurityContext targetSecurityContext;
|
|
|
|
/**
|
|
* By the default the system honour the propagation constraints
|
|
* so this variable is initialised as true.
|
|
*
|
|
* To revert a previous operation or for particular a maintenance
|
|
* we could request to the service do not honour the propagation
|
|
* constraints but under certain conditions and with some limitation
|
|
* only.
|
|
* These limitation are required to keep the system in a consistent
|
|
* state.
|
|
* In fact, this directive is only valid for Resources and IsRelatedTo
|
|
* relations. We need to take in account that to add an
|
|
* IsRelatedTo to a context always the source and target
|
|
* Resources must be in such a Context.
|
|
* Please also take into account that adding a Resource
|
|
* to a context always imply to honour the propagation constraints
|
|
* of ConsistsOf relations. In fact, a resource must be characterised
|
|
* least by one facet in any context it belongs. Giving that we
|
|
* cannot made assumption on which facets must be used.
|
|
* A way could be to consider just the facets are mandatory for such a
|
|
* Resource Type, but the type could not have mandatory facets
|
|
* (even every Resource Type in the gCube Model has one mandatory facet).
|
|
* As counterpart, when a Resource is removed from a Context all the facets
|
|
* charactering it must be removed.
|
|
*
|
|
* This option can also be used in conjunction with
|
|
* {@link ElementManagement#dryRun}=true.
|
|
* This allow to simulate a sharing operation which requires
|
|
* do not honour the propagation constraints.
|
|
*/
|
|
protected boolean honourPropagationConstraintsInContextSharing;
|
|
|
|
@Override
|
|
public void setSourceSecurityContext(SecurityContext sourceSecurityContext) {
|
|
this.sourceSecurityContext = sourceSecurityContext;
|
|
}
|
|
|
|
@Override
|
|
public SecurityContext getSourceSecurityContext() {
|
|
return sourceSecurityContext;
|
|
}
|
|
|
|
@Override
|
|
public void setTargetSecurityContext(SecurityContext targetSecurityContext) {
|
|
this.targetSecurityContext = targetSecurityContext;
|
|
}
|
|
|
|
@Override
|
|
public SecurityContext getTargetSecurityContext() {
|
|
return sourceSecurityContext;
|
|
}
|
|
|
|
@Override
|
|
public boolean isHonourPropagationConstraintsInContextSharing() {
|
|
return honourPropagationConstraintsInContextSharing;
|
|
}
|
|
|
|
@Override
|
|
public void setHonourPropagationConstraintsInContextSharing(boolean honourPropagationConstraintsInContextSharing) {
|
|
this.honourPropagationConstraintsInContextSharing = honourPropagationConstraintsInContextSharing;
|
|
}
|
|
|
|
/**
|
|
* Provide a cache edge-internal-id -> RelationManagement
|
|
* this avoid to recreate the relationManagement of already visited edges
|
|
*/
|
|
protected Map<String,RelationManagement<?, ?>> relationManagements;
|
|
|
|
/* The instance is added to the context even is not in source context */
|
|
protected boolean forceAddToContext;
|
|
|
|
/* Indicate that AddToContext skipped the instance because it was not the source context */
|
|
protected boolean skipped;
|
|
|
|
protected EntityManagement(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<>();
|
|
|
|
/*
|
|
* By the default the system honour the propagation constraints
|
|
* so this variable is initialised as true.
|
|
*/
|
|
this.honourPropagationConstraintsInContextSharing = true;
|
|
|
|
this.skipped = false;
|
|
}
|
|
|
|
@Override
|
|
public Vertex getElement() throws NotFoundException, AvailableInAnotherContextException, ResourceRegistryException {
|
|
try {
|
|
element = super.getElement();
|
|
} catch(NotFoundException e) {
|
|
try {
|
|
retrieveElementFromAnyContext();
|
|
throw getSpecificAvailableInAnotherContextException(typeName == null ? accessType.getName()
|
|
: typeName + " with ID " + uuid + " is available in another "
|
|
+ Context.class.getSimpleName());
|
|
} catch(AvailableInAnotherContextException e1) {
|
|
throw e1;
|
|
} catch(Exception e1) {
|
|
throw e;
|
|
}
|
|
} catch(ResourceRegistryException e) {
|
|
throw e;
|
|
} catch(Exception e) {
|
|
throw new ResourceRegistryException(e);
|
|
}
|
|
return element;
|
|
}
|
|
|
|
/*
|
|
* 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 RelationManagement<?,?> getRelationManagement(Edge edge) throws ResourceRegistryException {
|
|
String id = edge.getIdentity().toString();
|
|
RelationManagement<?,?> relationManagement = relationManagements.get(id);
|
|
if(relationManagement == null) {
|
|
relationManagement = ElementManagementUtility.getRelationManagement(getWorkingContext(), database, edge);
|
|
relationManagements.put(id, relationManagement);
|
|
}
|
|
return relationManagement;
|
|
}
|
|
|
|
public void addToRelationManagements(RelationManagement<?,?> relationManagement)
|
|
throws ResourceRegistryException {
|
|
Edge elem = relationManagement.getElement();
|
|
String id = elem.getIdentity().toString();
|
|
if(relationManagements.get(id) != null && relationManagements.get(id) != relationManagement) {
|
|
StringBuilder errorMessage = new StringBuilder();
|
|
errorMessage.append("Two different instance of ");
|
|
errorMessage.append(relationManagement.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, relationManagement);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
@Override
|
|
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 SchemaViolationException(error);
|
|
// }
|
|
|
|
MutableVertex vertexEntity = database.newVertex(typeName);
|
|
|
|
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 %s. This is not allowed.",
|
|
uuid.toString(), (el instanceof Vertex) ? Entity.NAME : Relation.NAME);
|
|
throw getSpecificAvailableInAnotherContextException(error);
|
|
|
|
} catch(NotFoundException e1) {
|
|
// OK the UUID is not already used.
|
|
}
|
|
} catch(AvailableInAnotherContextException e) {
|
|
throw e;
|
|
}
|
|
|
|
this.element = vertexEntity;
|
|
|
|
if(accessType == AccessType.RESOURCE) {
|
|
// Facet and relation are created in calling method
|
|
} else {
|
|
updateProperties(documentType, element, jsonNode, ignoreKeys, ignoreStartWithKeys);
|
|
}
|
|
|
|
logger.debug("Created {} is {}", Vertex.class.getSimpleName(),
|
|
DBUtility.getAsStringForLogging((Vertex) 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());
|
|
}
|
|
}
|
|
|
|
protected void reallyAddToContext()
|
|
throws ContextException, ResourceRegistryException {
|
|
if(!forceAddToContext && !sourceSecurityContext.isElementInContext(getElement())) {
|
|
// The element in not in the source security context. It will be skipped
|
|
skipped = true;
|
|
return;
|
|
}
|
|
|
|
|
|
targetSecurityContext.addElement(getElement(), database);
|
|
|
|
/*
|
|
* DO NOT UNCOMMENT
|
|
* // affectedInstances.put(uuid, serializeSelfOnly());
|
|
* the instance is added in internalAddToContext() function after
|
|
* the update of Metadata i.e. modifiedBy, lastUpdateTime
|
|
*/
|
|
if(honourPropagationConstraintsInContextSharing) {
|
|
Iterable<Edge> edges = getElement().getEdges(DIRECTION.OUT);
|
|
|
|
for(Edge edge : edges) {
|
|
RelationManagement<?,?> relationManagement = getRelationManagement(edge);
|
|
relationManagement.setDryRun(dryRun);
|
|
relationManagement.setHonourPropagationConstraintsInContextSharing(honourPropagationConstraintsInContextSharing);
|
|
relationManagement.setSourceSecurityContext(sourceSecurityContext);
|
|
relationManagement.setTargetSecurityContext(targetSecurityContext);
|
|
relationManagement.internalAddToContext();
|
|
affectedInstances.putAll(relationManagement.getAffectedInstances());
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void internalAddToContext()
|
|
throws ContextException, ResourceRegistryException {
|
|
try {
|
|
setOperation(Operation.ADD_TO_CONTEXT);
|
|
reallyAddToContext();
|
|
if(!skipped) {
|
|
MetadataUtility.updateModifiedByAndLastUpdate(element);
|
|
// element.save();
|
|
affectedInstances.put(uuid, serializeAsAffectedInstance());
|
|
sanityCheck();
|
|
}
|
|
} catch(ResourceRegistryException e) {
|
|
throw e;
|
|
} catch(Exception e) {
|
|
throw new ResourceRegistryException(
|
|
"Error Adding " + typeName + " to " + targetSecurityContext.toString(), e.getCause());
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void addToContext(UUID contextUUID) throws SchemaViolationException, NotFoundException, ContextException, ResourceRegistryException {
|
|
String contextFullName = ServerContextCache.getInstance().getContextFullNameByUUID(contextUUID);
|
|
logger.info("Going to add {} with UUID {} to Context with UUID {} (i.e. {})", accessType.getName(), uuid, contextUUID, contextFullName);
|
|
RemoteDatabase current = ContextUtility.getCurrentODatabaseDocumentFromThreadLocal();
|
|
try {
|
|
workingContext = ContextUtility.getAdminSecurityContext();
|
|
database = workingContext.getRemoteDatabase(PermissionMode.WRITER);
|
|
database.begin();
|
|
setAsEntryPoint();
|
|
|
|
sourceSecurityContext = ContextUtility.getCurrentSecurityContext();
|
|
targetSecurityContext = ContextUtility.getInstance().getSecurityContextByUUID(contextUUID);
|
|
|
|
internalAddToContext();
|
|
|
|
if(!dryRun) {
|
|
database.commit();
|
|
}else {
|
|
database.rollback();
|
|
}
|
|
logger.info("{} with UUID {} successfully added to Context with UUID {} (i.e. {})", typeName, uuid, contextUUID, contextFullName);
|
|
} catch(ResourceRegistryException e) {
|
|
logger.error("Unable to add {} with UUID {} to Context with UUID {} (i.e. {}) - Reason is {}", typeName, uuid, contextUUID, contextFullName, e.getMessage());
|
|
if(database != null) {
|
|
database.rollback();
|
|
}
|
|
throw e;
|
|
} catch(Exception e) {
|
|
logger.error("Unable to add {} with UUID {} to Context with UUID {} (i.e. {})", typeName, uuid, contextUUID, contextFullName, e);
|
|
if(database != null) {
|
|
database.rollback();
|
|
}
|
|
throw new ContextException(e);
|
|
} finally {
|
|
if(database != null) {
|
|
database.close();
|
|
}
|
|
|
|
// if(current!=null) {
|
|
// current.activateOnCurrentThread();
|
|
// }
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void internalRemoveFromContext()
|
|
throws ContextException, ResourceRegistryException {
|
|
try {
|
|
setOperation(Operation.REMOVE_FROM_CONTEXT);
|
|
reallyRemoveFromContext();
|
|
MetadataUtility.updateModifiedByAndLastUpdate(element);
|
|
// element.save();
|
|
affectedInstances.put(uuid, serializeAsAffectedInstance());
|
|
sanityCheck();
|
|
} catch(ResourceRegistryException e) {
|
|
throw e;
|
|
} catch(Exception e) {
|
|
throw new ResourceRegistryException(
|
|
"Error Removing " + typeName + " from " + targetSecurityContext.toString(), e.getCause());
|
|
}
|
|
}
|
|
|
|
protected void reallyRemoveFromContext()
|
|
throws ContextException, ResourceRegistryException {
|
|
|
|
if(!targetSecurityContext.isElementInContext(getElement())) {
|
|
// The element in not in the source security context. It will be skipped
|
|
return;
|
|
}
|
|
|
|
if(honourPropagationConstraintsInContextSharing) {
|
|
Iterable<Edge> edges = getElement().getEdges(DIRECTION.OUT);
|
|
|
|
for(Edge edge : edges) {
|
|
RelationManagement<?,?> relationManagement = getRelationManagement(edge);
|
|
relationManagement.setDryRun(dryRun);
|
|
relationManagement.setHonourPropagationConstraintsInContextSharing(honourPropagationConstraintsInContextSharing);
|
|
// Not needed relationManagement.setSourceSecurityContext(sourceSecurityContext);
|
|
relationManagement.setTargetSecurityContext(targetSecurityContext);
|
|
relationManagement.internalRemoveFromContext();
|
|
addToRelationManagements(relationManagement);
|
|
affectedInstances.putAll(relationManagement.getAffectedInstances());
|
|
}
|
|
}
|
|
|
|
targetSecurityContext.removeElement(getElement(), database);
|
|
|
|
/*
|
|
* DO NOT UNCOMMENT
|
|
* the instance is added internalAddToContext() function after
|
|
* the update of Metadata i.e. modifiedBy, lastUpdateTime
|
|
* affectedInstances.put(uuid, serializeSelfOnly());
|
|
*/
|
|
}
|
|
|
|
@Override
|
|
public void removeFromContext(UUID contextUUID)
|
|
throws SchemaViolationException, NotFoundException, ContextException, ResourceRegistryException {
|
|
|
|
logger.debug("Going to remove {} with UUID {} from Context with UUID {}", typeName, uuid, contextUUID);
|
|
RemoteDatabase current = ContextUtility.getCurrentODatabaseDocumentFromThreadLocal();
|
|
try {
|
|
workingContext = ContextUtility.getAdminSecurityContext();
|
|
database = workingContext.getRemoteDatabase(PermissionMode.WRITER);
|
|
database.begin();
|
|
setAsEntryPoint();
|
|
|
|
// Not needed sourceSecurityContext = ContextUtility.getCurrentSecurityContext();
|
|
targetSecurityContext = ContextUtility.getInstance().getSecurityContextByUUID(contextUUID);
|
|
|
|
internalRemoveFromContext();
|
|
|
|
if(!dryRun) {
|
|
database.commit();
|
|
}else {
|
|
database.rollback();
|
|
}
|
|
logger.info("{} with UUID {} successfully removed from Context with UUID {}", typeName, uuid, contextUUID);
|
|
} catch(ResourceRegistryException e) {
|
|
logger.error("Unable to remove {} with UUID {} from Context with UUID {}", typeName, uuid, contextUUID);
|
|
if(database != null) {
|
|
database.rollback();
|
|
}
|
|
throw e;
|
|
} catch(Exception e) {
|
|
logger.error("Unable to remove {} with UUID {} from Context with UUID {}", typeName, uuid, contextUUID,
|
|
e);
|
|
if(database != null) {
|
|
database.rollback();
|
|
}
|
|
throw new ContextException(e);
|
|
} finally {
|
|
if(database != null) {
|
|
database.close();
|
|
}
|
|
|
|
// if(current!=null) {
|
|
// current.activateOnCurrentThread();
|
|
// }
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public String reallyGetAll(boolean polymorphic) throws ResourceRegistryException {
|
|
ObjectMapper objectMapper = new ObjectMapper();
|
|
ArrayNode arrayNode = objectMapper.createArrayNode();
|
|
|
|
Iterable<Document> iterable = database.browseClass(typeName, polymorphic);
|
|
for(Document vertex : iterable) {
|
|
EntityManagement<?,?> entityManagement = ElementManagementUtility.getEntityManagement(getWorkingContext(),
|
|
database, (Vertex) vertex);
|
|
try {
|
|
entityManagement.setAsEntryPoint();
|
|
JsonNode jsonNode = entityManagement.serializeAsJsonNode();
|
|
arrayNode.add(jsonNode);
|
|
} 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 boolean propertyMatchRequestedValue(Vertex v, String key, String requestedValue, Object instanceValue) throws SchemaException, ResourceRegistryException {
|
|
return requestedValue.compareTo(instanceValue.toString())==0;
|
|
|
|
|
|
/*
|
|
OClass documentType = ElementManagement.getOClass(v);
|
|
OProperty oProperty = documentType.getProperty(key);
|
|
if(oProperty==null){
|
|
// It is an additional property
|
|
return requestedValue.compareTo(instanceValue.toString())==0;
|
|
}
|
|
OType oType = oProperty.getType();
|
|
switch (oType) {
|
|
case BOOLEAN:
|
|
Boolean requested = Boolean.valueOf(requestedValue.toLowerCase());
|
|
return requested == (Boolean) instanceValue;
|
|
|
|
case STRING:
|
|
return requestedValue.compareTo((String) instanceValue)==0;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
*/
|
|
}
|
|
|
|
|
|
/*
|
|
public String reallyQuery(String relationType, String referenceType, UUID referenceUUID, DIRECTION direction,
|
|
boolean polymorphic, Map<String,String> constraint, boolean includeRelationInResult) throws ResourceRegistryException {
|
|
*/
|
|
public String reallyQuery(String relationType, String referenceType, UUID referenceUUID, DIRECTION direction,
|
|
boolean polymorphic, Map<String,String> constraint) throws ResourceRegistryException {
|
|
ObjectMapper objectMapper = new ObjectMapper();
|
|
ArrayNode arrayNode = objectMapper.createArrayNode();
|
|
|
|
Iterable<?> references = null;
|
|
|
|
if(referenceUUID != null) {
|
|
Document element = null;
|
|
try {
|
|
element = ElementManagementUtility.getAnyElementByUUID(database, referenceUUID);
|
|
}catch (ResourceRegistryException e) {
|
|
String error = String.format("No instace with UUID %s exists", referenceUUID.toString());
|
|
throw new InvalidQueryException(error);
|
|
}
|
|
|
|
if(element instanceof Vertex) {
|
|
EntityManagement<?, ?> entityManagement = ElementManagementUtility.getEntityManagement(getWorkingContext(),
|
|
database, (Vertex) element);
|
|
|
|
String elementType = entityManagement.getTypeName();
|
|
if(elementType.compareTo(referenceType) != 0) {
|
|
if(polymorphic && getDocumentType().isSubTypeOf(referenceType)) {
|
|
// OK
|
|
} else {
|
|
String error = String.format("Referenced instace with UUID %s is not a %s", referenceUUID, referenceType);
|
|
throw new InvalidQueryException(error);
|
|
}
|
|
}
|
|
|
|
List<Vertex> vertexes = new ArrayList<>();
|
|
vertexes.add((Vertex) element);
|
|
references = vertexes;
|
|
|
|
} else {
|
|
String error = String.format("Referenced instace with UUID %s is not a %s", referenceUUID, referenceType);
|
|
throw new InvalidQueryException(error);
|
|
}
|
|
|
|
} else {
|
|
references = database.browseClass(referenceType, polymorphic);
|
|
}
|
|
|
|
Set<RID> analysed = new HashSet<>();
|
|
|
|
for(Object r : references) {
|
|
Vertex v = (Vertex) r;
|
|
|
|
boolean skip = false;
|
|
// checking if the constraints are satisfied
|
|
for(String key : constraint.keySet()) {
|
|
String value = constraint.get(key);
|
|
Object o = v.get(key);
|
|
if(value==null) {
|
|
if(o==null) {
|
|
//ok
|
|
}else {
|
|
skip = true;
|
|
break;
|
|
}
|
|
}else {
|
|
if(o==null) {
|
|
// The vertex has not a required property to be tested
|
|
// or the property is null
|
|
skip = true;
|
|
break;
|
|
}else {
|
|
skip = !propertyMatchRequestedValue(v, key, value, o);
|
|
if(skip) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(skip) {
|
|
continue;
|
|
}
|
|
|
|
|
|
List<DIRECTION> directions = new ArrayList<>();
|
|
if(direction==DIRECTION.BOTH) {
|
|
directions.add(DIRECTION.IN);
|
|
directions.add(DIRECTION.OUT);
|
|
}else {
|
|
directions.add(direction);
|
|
}
|
|
|
|
for(DIRECTION d : directions) {
|
|
|
|
Iterable<Edge> edges = v.getEdges(ElementManagement.opposite(d), relationType);
|
|
for(Edge edge : edges) {
|
|
Vertex vertex = edge.getVertex(d);
|
|
|
|
RID vertexRID = vertex.getIdentity();
|
|
|
|
if(analysed.contains(vertexRID)) {
|
|
continue;
|
|
}
|
|
analysed.add(vertexRID);
|
|
|
|
if(v.getIdentity().compareTo(vertexRID) == 0) {
|
|
continue;
|
|
}
|
|
|
|
DocumentType documentType = ElementManagementUtility.getDocumentType(vertex);
|
|
|
|
/*
|
|
* If the requested type (i.e. elementType)
|
|
* differs form the resulting type (i.e. documentType.getName())
|
|
* we need to evaluate if polymorphism is requested and
|
|
* if the resulting type is a subclass of the requested type
|
|
*
|
|
*/
|
|
if(documentType.getName().compareTo(typeName)!=0) {
|
|
if(polymorphic && documentType.isSubTypeOf(typeName)) {
|
|
// OK
|
|
} else {
|
|
// excluding from results
|
|
continue;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
EntityManagement<?,?> entityManagement = ElementManagementUtility.getEntityManagement(getWorkingContext(),
|
|
database, vertex);
|
|
|
|
try {
|
|
if(referenceUUID!=null && entityManagement.getUUID().compareTo(referenceUUID) == 0) {
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
JsonNode jsonNode;
|
|
if(includeRelationInResult) {
|
|
RelationManagement<?,?> relationManagement = ElementManagementUtility.getRelationManagement(getWorkingContext(),
|
|
database, edge);
|
|
jsonNode = relationManagement.serializeAsJsonNode();
|
|
}else {
|
|
jsonNode = entityManagement.serializeAsJsonNode();
|
|
}
|
|
*/
|
|
entityManagement.setAsEntryPoint();
|
|
JsonNode node = entityManagement.serializeAsJsonNode();
|
|
|
|
arrayNode.add(node);
|
|
} 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 reallyQueryTraversal(String relationType, String referenceType, UUID referenceUUID,
|
|
DIRECTION direction, boolean polymorphic, Map<String,String> constraint) throws ResourceRegistryException {
|
|
ObjectMapper objectMapper = new ObjectMapper();
|
|
ArrayNode arrayNode = objectMapper.createArrayNode();
|
|
|
|
if(referenceUUID != null) {
|
|
constraint.put(Entity.ID_PROPERTY, referenceUUID.toString());
|
|
}
|
|
|
|
// TODO check types
|
|
|
|
/*
|
|
* SELECT FROM (TRAVERSE inE('isIdentifiedBy'), outV('EService') FROM (SELECT
|
|
* FROM SoftwareFacet WHERE group='VREManagement' AND name='SmartExecutor'))
|
|
*
|
|
* WHERE type='EService' // Only is not polymorphic
|
|
*/
|
|
|
|
StringBuilder selectStringBuilder = new StringBuilder("SELECT FROM (TRAVERSE ");
|
|
selectStringBuilder.append(direction.name().toLowerCase());
|
|
selectStringBuilder.append("E('");
|
|
selectStringBuilder.append(relationType);
|
|
selectStringBuilder.append("'), ");
|
|
selectStringBuilder.append(ElementManagement.opposite(direction).name().toLowerCase());
|
|
selectStringBuilder.append("V('");
|
|
selectStringBuilder.append(typeName);
|
|
selectStringBuilder.append("') FROM (SELECT FROM ");
|
|
selectStringBuilder.append(referenceType);
|
|
boolean first = true;
|
|
for(String key : constraint.keySet()) {
|
|
if(first) {
|
|
selectStringBuilder.append(" WHERE ");
|
|
first = false;
|
|
} else {
|
|
selectStringBuilder.append(" AND ");
|
|
}
|
|
selectStringBuilder.append(key);
|
|
selectStringBuilder.append("=");
|
|
String value = constraint.get(key).trim();
|
|
selectStringBuilder.append("'");
|
|
selectStringBuilder.append(value);
|
|
selectStringBuilder.append("'");
|
|
}
|
|
selectStringBuilder.append(" ))");
|
|
|
|
if(!polymorphic) {
|
|
selectStringBuilder.append(" WHERE type='");
|
|
selectStringBuilder.append(typeName);
|
|
selectStringBuilder.append("'");
|
|
}
|
|
|
|
String select = selectStringBuilder.toString();
|
|
logger.trace(select);
|
|
|
|
ResultSet resultSet = database.command("sql", select);
|
|
|
|
while(resultSet.hasNext()) {
|
|
Result oResult = resultSet.next();
|
|
Document element = ElementManagementUtility.getElementFromOptional(oResult.getElement());
|
|
|
|
if(polymorphic) {
|
|
DocumentType documentType = null;
|
|
try {
|
|
if(element instanceof Edge) {
|
|
continue;
|
|
}
|
|
documentType = ElementManagementUtility.getDocumentType(element);
|
|
} catch(Exception e) {
|
|
String error = String.format("Unable to detect type of %s. %s", element.toString(),
|
|
DBUtility.SHOULD_NOT_OCCUR_ERROR_MESSAGE);
|
|
logger.error(error, e);
|
|
throw new ResourceRegistryException(error);
|
|
}
|
|
|
|
if(documentType.isSubTypeOf(typeName)) {
|
|
continue;
|
|
}
|
|
|
|
}
|
|
|
|
Vertex vertex = (Vertex) element;
|
|
|
|
EntityManagement<?,?> entityManagement = ElementManagementUtility.getEntityManagement(getWorkingContext(),
|
|
database, vertex);
|
|
try {
|
|
if(constraint.containsKey(Entity.ID_PROPERTY)) {
|
|
String uuid = constraint.get(Entity.ID_PROPERTY);
|
|
if(entityManagement.getUUID().compareTo(UUID.fromString(uuid)) == 0) {
|
|
continue;
|
|
}
|
|
}
|
|
entityManagement.setAsEntryPoint();
|
|
JsonNode jsonNode = entityManagement.serializeAsJsonNode();
|
|
arrayNode.add(jsonNode);
|
|
} 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 query(String relationType, String referenceType, UUID referenceUUID, ODirection direction,
|
|
boolean polymorphic, Map<String,String> constraint, boolean includeRelationInResult) throws ResourceRegistryException {
|
|
*/
|
|
public String query(String relationType, String referenceType, UUID referenceUUID, DIRECTION direction,
|
|
boolean polymorphic, Map<String,String> constraint) throws ResourceRegistryException {
|
|
|
|
RemoteDatabase current = ContextUtility.getCurrentODatabaseDocumentFromThreadLocal();
|
|
try {
|
|
workingContext = ContextUtility.getAdminSecurityContext();
|
|
database = workingContext.getRemoteDatabase(PermissionMode.READER);
|
|
|
|
setAsEntryPoint();
|
|
setOperation(Operation.QUERY);
|
|
|
|
TypesCache typesCache = TypesCache.getInstance();
|
|
AccessType relationAccessType = typesCache.getCachedType(relationType).getAccessType();
|
|
if(relationAccessType != AccessType.IS_RELATED_TO && relationAccessType != AccessType.CONSISTS_OF) {
|
|
String error = String.format("%s must be a relation type", relationType);
|
|
throw new ResourceRegistryException(error);
|
|
}
|
|
|
|
AccessType referenceAccessType = typesCache.getCachedType(referenceType).getAccessType();
|
|
if(referenceAccessType != AccessType.RESOURCE && referenceAccessType != AccessType.FACET) {
|
|
String error = String.format("%s must be a en entity type", referenceType);
|
|
throw new ResourceRegistryException(error);
|
|
}
|
|
|
|
if(constraint == null) {
|
|
constraint = new HashMap<>();
|
|
}
|
|
|
|
switch(accessType) {
|
|
case RESOURCE:
|
|
|
|
if(relationAccessType == AccessType.CONSISTS_OF) {
|
|
|
|
if(direction != DIRECTION.OUT) {
|
|
String error = String.format("%s can only goes %s from %s.", relationType,
|
|
DIRECTION.OUT.name(), typeName);
|
|
throw new InvalidQueryException(error);
|
|
} else {
|
|
if(referenceAccessType != AccessType.FACET) {
|
|
String error = String.format("%s can only has as target a %s. Provided instead %s : %s",
|
|
relationType, Facet.NAME, referenceAccessType, referenceType);
|
|
throw new InvalidQueryException(error);
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case FACET:
|
|
if(relationAccessType != AccessType.CONSISTS_OF || direction != DIRECTION.IN
|
|
|| referenceAccessType != AccessType.RESOURCE) {
|
|
String error = String.format("%s can only has %s %s from a %s.", typeName,
|
|
DIRECTION.IN.name(), ConsistsOf.NAME, Resource.NAME);
|
|
throw new InvalidQueryException(error);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// return reallyQuery(relationType, referenceType, referenceUUID, direction, polymorphic, constraint, includeRelationInResult);
|
|
return reallyQuery(relationType, referenceType, referenceUUID, direction, polymorphic, constraint);
|
|
|
|
} catch(ResourceRegistryException e) {
|
|
throw e;
|
|
} catch(Exception e) {
|
|
throw new ResourceRegistryException(e);
|
|
} finally {
|
|
if(database != null) {
|
|
database.close();
|
|
}
|
|
// if(current!=null) {
|
|
// current.activateOnCurrentThread();
|
|
// }
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void setForceAddToContext(Boolean forceAddToContext) {
|
|
this.forceAddToContext = forceAddToContext;
|
|
}
|
|
|
|
}
|