resource-registry/src/main/java/org/gcube/informationsystem/resourceregistry/instances/model/entities/EntityManagement.java

851 lines
31 KiB
Java
Raw Normal View History

package org.gcube.informationsystem.resourceregistry.instances.model.entities;
import java.util.ArrayList;
import java.util.HashMap;
2020-12-10 11:13:14 +01:00
import java.util.HashSet;
import java.util.List;
import java.util.Map;
2020-12-10 11:13:14 +01:00
import java.util.Set;
import java.util.UUID;
2020-07-07 17:15:22 +02:00
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;
2021-02-05 17:50:16 +01:00
import org.gcube.informationsystem.context.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.properties.Header;
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.context.ContextException;
import org.gcube.informationsystem.resourceregistry.api.exceptions.entity.EntityAlreadyPresentException;
import org.gcube.informationsystem.resourceregistry.api.exceptions.query.InvalidQueryException;
2020-12-10 11:13:14 +01:00
import org.gcube.informationsystem.resourceregistry.api.exceptions.schema.SchemaException;
2021-02-05 17:50:16 +01:00
import org.gcube.informationsystem.resourceregistry.contexts.ContextUtility;
2020-01-27 17:07:37 +01:00
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;
2021-02-05 17:50:16 +01:00
import org.gcube.informationsystem.resourceregistry.instances.model.ERManagement;
2019-11-06 12:14:27 +01:00
import org.gcube.informationsystem.resourceregistry.instances.model.relations.RelationManagement;
2021-02-05 17:50:16 +01:00
import org.gcube.informationsystem.resourceregistry.utils.HeaderUtility;
import org.gcube.informationsystem.resourceregistry.utils.Utility;
import com.orientechnologies.orient.core.db.document.ODatabaseDocument;
2020-12-10 11:13:14 +01:00
import com.orientechnologies.orient.core.id.ORID;
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.OElement;
import com.orientechnologies.orient.core.record.OVertex;
import com.orientechnologies.orient.core.record.impl.ODocument;
import com.orientechnologies.orient.core.sql.executor.OResult;
import com.orientechnologies.orient.core.sql.executor.OResultSet;
/**
* @author Luca Frosini (ISTI - CNR)
*/
2021-02-05 17:50:16 +01:00
public abstract class EntityManagement<E extends Entity> extends EntityElementManagement<E> implements ERManagement {
/**
* By the default the system execute the the operation of
* context sharing so this variable is initialised as false.
*
* Setting this variable to false the system just simulate
* the operation of context sharing
* i.e. AddToContext, RemoveFromContext.
*
* This option can also be used in conjunction with
* {@link ElementManagement#honourPropagationConstraintsInContextSharing}=false.
* This allow to simulate a sharing operation which requires
* do not honour the propagation constraints.
*/
protected boolean dryRunContextSharing;
/**
* 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#dryRunContextSharing}=true.
* This allow to simulate a sharing operation which requires
* do not honour the propagation constraints.
*/
protected boolean honourPropagationConstraintsInContextSharing;
public boolean isDryRunContextSharing() {
return dryRunContextSharing;
}
public void setDryRunContextSharing(boolean dryRunContextSharing) {
this.dryRunContextSharing = dryRunContextSharing;
}
public boolean isHonourPropagationConstraintsInContextSharing() {
return honourPropagationConstraintsInContextSharing;
}
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
*/
@SuppressWarnings("rawtypes")
protected Map<String,RelationManagement> relationManagements;
protected EntityManagement(AccessType accessType) {
super(accessType);
this.ignoreKeys.add(Entity.HEADER_PROPERTY);
this.ignoreStartWithKeys.add(com.tinkerpop.blueprints.impls.orient.OrientVertex.CONNECTION_IN_PREFIX.toLowerCase());
this.ignoreStartWithKeys.add(com.tinkerpop.blueprints.impls.orient.OrientVertex.CONNECTION_OUT_PREFIX.toLowerCase());
this.ignoreStartWithKeys.add(com.tinkerpop.blueprints.impls.orient.OrientVertex.CONNECTION_IN_PREFIX.toUpperCase());
this.ignoreStartWithKeys.add(com.tinkerpop.blueprints.impls.orient.OrientVertex.CONNECTION_OUT_PREFIX.toUpperCase());
this.relationManagements = new HashMap<>();
this.superClassesToBeExcluded.add(AccessType.ENTITY_ELEMENT.getName());
this.superClassesToBeExcluded.add(AccessType.ENTITY.getName());
2021-02-05 17:50:16 +01:00
/*
* By the default the system execute the the operation of
* context sharing so this variable is initialised as false.
*/
this.dryRunContextSharing = false;
/*
* By the default the system honour the propagation constraints
* so this variable is initialised as true.
*/
this.honourPropagationConstraintsInContextSharing = true;
}
protected EntityManagement(AccessType accessType, SecurityContext workingContext, ODatabaseDocument orientGraph) {
this(accessType);
this.oDatabaseDocument = orientGraph;
setWorkingContext(workingContext);
}
2021-02-05 17:50:16 +01:00
@Override
public OVertex getElement() throws NotFoundException, AvailableInAnotherContextException, ResourceRegistryException {
try {
element = super.getElement();
} catch(NotFoundException e) {
try {
retrieveElementFromAnyContext();
throw getSpecificERAvailableInAnotherContextException(elementType == null ? accessType.getName()
: elementType + " with UUID " + 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;
}
@SuppressWarnings("rawtypes")
/*
* 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(OEdge edge) throws ResourceRegistryException {
String id = edge.getIdentity().toString();
RelationManagement relationManagement = relationManagements.get(id);
if(relationManagement == null) {
relationManagement = ElementManagementUtility.getRelationManagement(getWorkingContext(), oDatabaseDocument, edge);
relationManagements.put(id, relationManagement);
}
return relationManagement;
}
protected void addToRelationManagement(@SuppressWarnings("rawtypes") RelationManagement relationManagement)
throws ResourceRegistryException {
OElement 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(Utility.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;
}
protected OVertex createVertex() throws EntityAlreadyPresentException, ResourceRegistryException {
logger.trace("Going to create {} for {} ({}) using {}", OVertex.class.getSimpleName(), accessType.getName(),
elementType, jsonNode);
try {
if(oClass.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(), elementType);
throw new ResourceRegistryException(error);
}
OVertex vertexEntity = oDatabaseDocument.newVertex(elementType);
try {
if(uuid != null) {
OVertex v = getElement();
if(v != null) {
String error = String.format("A %s with UUID %s already exist", elementType, uuid.toString());
throw getSpecificERAlreadyPresentException(error);
}
}
} catch(NotFoundException e) {
try {
OElement el = ElementManagementUtility.getAnyElementByUUID(uuid);
String error = String.format("UUID %s is already used by another %s. This is not allowed.",
uuid.toString(), (el instanceof OVertex) ? Entity.NAME : Relation.NAME);
throw getSpecificERAvailableInAnotherContextException(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 {
2021-02-08 16:26:58 +01:00
updateProperties(oClass, element, jsonNode, ignoreKeys, ignoreStartWithKeys);
}
logger.info("Created {} is {}", OVertex.class.getSimpleName(),
Utility.toJsonString((OVertex) element, true));
return element;
} catch(ResourceRegistryException e) {
throw e;
} catch(Exception e) {
logger.trace("Error while creating {} for {} ({}) using {}", OVertex.class.getSimpleName(),
accessType.getName(), elementType, jsonNode, e);
throw new ResourceRegistryException("Error Creating " + elementType + " with " + jsonNode, e.getCause());
}
}
2021-01-25 17:38:19 +01:00
protected Map<UUID,JsonNode> reallyAddToContext(SecurityContext targetSecurityContext)
throws ContextException, ResourceRegistryException {
2021-01-25 17:38:19 +01:00
Map<UUID,JsonNode> affectedInstances = new HashMap<>();
if(!dryRunContextSharing) {
targetSecurityContext.addElement(getElement(), oDatabaseDocument);
}
2021-01-25 17:38:19 +01:00
/*
* DO NOT UNCOMMENT
* // affectedInstances.put(uuid, serializeSelfOnly());
* the instance is added in internalAddToContext() function after
2021-01-25 17:38:19 +01:00
* the update of Header metadata i.e. modifiedBy, lastUpdateTime
*/
if(honourPropagationConstraintsInContextSharing) {
Iterable<OEdge> edges = getElement().getEdges(ODirection.OUT);
for(OEdge edge : edges) {
RelationManagement<?> relationManagement = getRelationManagement(edge);
relationManagement.setDryRunContextSharing(dryRunContextSharing);
relationManagement.setHonourPropagationConstraintsInContextSharing(honourPropagationConstraintsInContextSharing);
affectedInstances.putAll(relationManagement.internalAddToContext(targetSecurityContext));
}
}
2021-01-25 17:38:19 +01:00
return affectedInstances;
}
@Override
2021-02-05 17:50:16 +01:00
public Map<UUID,JsonNode> internalAddToContext(SecurityContext targetSecurityContext)
throws ContextException, ResourceRegistryException {
try {
Map<UUID,JsonNode> affectedInstances = reallyAddToContext(targetSecurityContext);
if(!dryRunContextSharing) {
HeaderUtility.updateModifiedByAndLastUpdate(element);
element.save();
}
affectedInstances.put(uuid, serializeSelfOnly());
return affectedInstances;
} catch(ResourceRegistryException e) {
throw e;
} catch(Exception e) {
throw new ResourceRegistryException(
"Error Adding " + elementType + " to " + targetSecurityContext.toString(), e.getCause());
}
}
public Map<UUID,JsonNode> addToContext(UUID contextUUID) throws NotFoundException, ContextException, ResourceRegistryException {
logger.info("Going to add {} with UUID {} to Context with UUID {}", accessType.getName(), uuid, contextUUID);
ODatabaseDocument current = ContextUtility.getCurrentODatabaseDocumentFromThreadLocal();
try {
oDatabaseDocument = ContextUtility.getAdminSecurityContext().getDatabaseDocument(PermissionMode.WRITER);
oDatabaseDocument.begin();
SecurityContext targetSecurityContext = ContextUtility.getInstance().getSecurityContextByUUID(contextUUID);
Map<UUID,JsonNode> affectedInstances = internalAddToContext(targetSecurityContext);
oDatabaseDocument.commit();
logger.info("{} with UUID {} successfully added to Context with UUID {}", elementType, uuid, contextUUID);
return affectedInstances;
} catch(ResourceRegistryException e) {
logger.error("Unable to add {} with UUID {} to Context with UUID {} - Reason is {}", elementType, uuid, contextUUID, e.getMessage());
if(oDatabaseDocument != null) {
oDatabaseDocument.rollback();
}
throw e;
} catch(Exception e) {
logger.error("Unable to add {} with UUID {} to Context with UUID {}", elementType, uuid, contextUUID, e);
if(oDatabaseDocument != null) {
oDatabaseDocument.rollback();
}
throw new ContextException(e);
} finally {
if(oDatabaseDocument != null) {
oDatabaseDocument.close();
}
if(current!=null) {
current.activateOnCurrentThread();
}
}
}
@Override
public Map<UUID,JsonNode> internalRemoveFromContext(SecurityContext targetSecurityContext)
throws ContextException, ResourceRegistryException {
try {
Map<UUID,JsonNode> affectedInstances = reallyRemoveFromContext(targetSecurityContext);
if(!dryRunContextSharing) {
HeaderUtility.updateModifiedByAndLastUpdate(element);
element.save();
}
affectedInstances.put(uuid, serializeSelfOnly());
return affectedInstances;
} catch(ResourceRegistryException e) {
throw e;
} catch(Exception e) {
throw new ResourceRegistryException(
"Error Removing " + elementType + " from " + targetSecurityContext.toString(), e.getCause());
}
}
2021-01-25 17:38:19 +01:00
protected Map<UUID,JsonNode> reallyRemoveFromContext(SecurityContext targetSecurityContext)
throws ContextException, ResourceRegistryException {
2021-01-25 17:38:19 +01:00
Map<UUID,JsonNode> affectedInstances = new HashMap<>();
if(honourPropagationConstraintsInContextSharing) {
Iterable<OEdge> edges = getElement().getEdges(ODirection.OUT);
for(OEdge edge : edges) {
RelationManagement<?> relationManagement = getRelationManagement(edge);
relationManagement.setDryRunContextSharing(dryRunContextSharing);
relationManagement.setHonourPropagationConstraintsInContextSharing(honourPropagationConstraintsInContextSharing);
affectedInstances.putAll(relationManagement.internalRemoveFromContext(targetSecurityContext));
}
}
if(!dryRunContextSharing) {
targetSecurityContext.removeElement(getElement(), oDatabaseDocument);
}
2021-01-25 17:38:19 +01:00
/*
* DO NOT UNCOMMENT
* the instance is added internalAddToContext() function after
* the update of Header metadata i.e. modifiedBy, lastUpdateTime
* affectedInstances.put(uuid, serializeSelfOnly());
*/
2021-01-25 17:38:19 +01:00
return affectedInstances;
}
2021-02-05 17:50:16 +01:00
public Map<UUID,JsonNode> removeFromContext(UUID contextUUID)
throws NotFoundException, ContextException, ResourceRegistryException {
logger.debug("Going to remove {} with UUID {} from Context with UUID {}", elementType, uuid, contextUUID);
ODatabaseDocument current = ContextUtility.getCurrentODatabaseDocumentFromThreadLocal();
try {
oDatabaseDocument = ContextUtility.getAdminSecurityContext().getDatabaseDocument(PermissionMode.WRITER);
oDatabaseDocument.begin();
SecurityContext targetSecurityContext = ContextUtility.getInstance().getSecurityContextByUUID(contextUUID);
Map<UUID,JsonNode> affectedInstances = internalRemoveFromContext(targetSecurityContext);
oDatabaseDocument.commit();
logger.info("{} with UUID {} successfully removed from Context with UUID {}", elementType, uuid, contextUUID);
return affectedInstances;
} catch(ResourceRegistryException e) {
logger.error("Unable to remove {} with UUID {} from Context with UUID {}", elementType, uuid, contextUUID);
if(oDatabaseDocument != null) {
oDatabaseDocument.rollback();
}
throw e;
} catch(Exception e) {
logger.error("Unable to remove {} with UUID {} from Context with UUID {}", elementType, uuid, contextUUID,
e);
if(oDatabaseDocument != null) {
oDatabaseDocument.rollback();
}
throw new ContextException(e);
} finally {
if(oDatabaseDocument != null) {
oDatabaseDocument.close();
}
if(current!=null) {
current.activateOnCurrentThread();
}
}
}
@Override
public String reallyGetAll(boolean polymorphic) throws ResourceRegistryException {
ObjectMapper objectMapper = new ObjectMapper();
ArrayNode arrayNode = objectMapper.createArrayNode();
Iterable<ODocument> iterable = oDatabaseDocument.browseClass(elementType, polymorphic);
for(ODocument vertex : iterable) {
EntityManagement<?> entityManagement = ElementManagementUtility.getEntityManagement(getWorkingContext(),
oDatabaseDocument, (OVertex) vertex);
try {
JsonNode jsonNode = entityManagement.serializeAsJson();
arrayNode.add(jsonNode);
} catch(ResourceRegistryException e) {
logger.error("Unable to correctly serialize {}. It will be excluded from results. {}",
vertex.toString(), Utility.SHOULD_NOT_OCCUR_ERROR_MESSAGE);
}
}
try {
return objectMapper.writeValueAsString(arrayNode);
} catch(JsonProcessingException e) {
throw new ResourceRegistryException(e);
}
}
2020-12-10 11:13:14 +01:00
public boolean propertyMatchRequestedVlaue(OVertex v, String key, String requestedValue, Object instanceValue) throws SchemaException, ResourceRegistryException {
return requestedValue.compareTo(instanceValue.toString())==0;
/*
OClass oClass = ElementManagement.getOClass(v);
OProperty oProperty = oClass.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, ODirection direction,
2021-01-28 22:53:10 +01:00
boolean polymorphic, Map<String,String> constraint, boolean includeRelationInResult) throws ResourceRegistryException {
ObjectMapper objectMapper = new ObjectMapper();
ArrayNode arrayNode = objectMapper.createArrayNode();
Iterable<?> references = null;
if(referenceUUID != null) {
2021-01-28 22:53:10 +01:00
OElement element = ElementManagementUtility.getAnyElementByUUID(oDatabaseDocument, referenceUUID);
if(element instanceof OVertex) {
@SuppressWarnings("unchecked")
EntityManagement<Entity> entityManagement = ElementManagementUtility.getEntityManagement(getWorkingContext(),
oDatabaseDocument, (OVertex) element);
String elementType = entityManagement.getElementType();
if(elementType.compareTo(referenceType) != 0) {
if(polymorphic && getOClass().isSubClassOf(referenceType)) {
// OK
} else {
String error = String.format("Referenced instace with UUID %s is not a %s", referenceUUID,
referenceType);
throw new InvalidQueryException(error);
}
}
List<OVertex> vertexes = new ArrayList<>();
vertexes.add((OVertex) element);
references = vertexes;
} else {
String error = String.format("Referenced instace with UUID %s is not an %s", referenceUUID, Entity.NAME);
throw new InvalidQueryException(error);
}
} else {
references = oDatabaseDocument.browseClass(referenceType, polymorphic);
}
2020-12-10 11:13:14 +01:00
Set<ORID> analysed = new HashSet<>();
for(Object r : references) {
OVertex v = (OVertex) r;
2020-12-10 11:13:14 +01:00
boolean skip = false;
// checking if the constraints are satisfied
for(String key : constraint.keySet()) {
String value = constraint.get(key);
Object o = v.getProperty(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 = !propertyMatchRequestedVlaue(v, key, value, o);
if(skip) {
break;
}
}
}
}
if(skip) {
continue;
}
List<ODirection> directions = new ArrayList<>();
if(direction==ODirection.BOTH) {
directions.add(ODirection.IN);
directions.add(ODirection.OUT);
}else {
directions.add(direction);
}
for(ODirection d : directions) {
Iterable<OEdge> edges = v.getEdges(d.opposite(), relationType);
for(OEdge edge : edges) {
OVertex vertex = edge.getVertex(d);
2020-12-10 11:13:14 +01:00
ORID vertexORID = vertex.getIdentity();
if(analysed.contains(vertexORID)) {
continue;
}
2020-12-10 11:13:14 +01:00
analysed.add(vertexORID);
2020-12-10 11:13:14 +01:00
if(v.getIdentity().compareTo(vertexORID) == 0) {
continue;
}
2020-12-10 11:13:14 +01:00
OClass oClass = ElementManagement.getOClass(vertex);
/*
* If the requested type (i.e. elementType)
* differs form the resulting type (i.e. oClass.getName())
* we need to evaluate if polymorphism is requested and
* if the resulting type is a subclass of the requested type
*
*/
if(oClass.getName().compareTo(elementType)!=0) {
if(polymorphic && oClass.isSubClassOf(elementType)) {
// OK
} else {
// excluding from results
continue;
}
}
2021-01-28 22:53:10 +01:00
@SuppressWarnings("rawtypes")
EntityManagement entityManagement = ElementManagementUtility.getEntityManagement(getWorkingContext(),
oDatabaseDocument, vertex);
2021-01-28 22:53:10 +01:00
try {
if(referenceUUID!=null && entityManagement.getUUID().compareTo(referenceUUID) == 0) {
continue;
}
2021-01-28 22:53:10 +01:00
JsonNode jsonNode;
if(includeRelationInResult) {
@SuppressWarnings("rawtypes")
RelationManagement relationManagement = ElementManagementUtility.getRelationManagement(getWorkingContext(),
oDatabaseDocument, edge);
jsonNode = relationManagement.serializeAsJson();
}else {
jsonNode = entityManagement.serializeAsJson();
}
arrayNode.add(jsonNode);
} catch(ResourceRegistryException e) {
logger.error("Unable to correctly serialize {}. It will be excluded from results. {}",
vertex.toString(), Utility.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,
ODirection direction, boolean polymorphic, Map<String,String> constraint) throws ResourceRegistryException {
ObjectMapper objectMapper = new ObjectMapper();
ArrayNode arrayNode = objectMapper.createArrayNode();
if(referenceUUID != null) {
constraint.put(Entity.HEADER_PROPERTY + "." + Header.UUID_PROPERTY, referenceUUID.toString());
}
// TODO check types
/*
* SELECT FROM (TRAVERSE inE('isIdentifiedBy'), outV('EService') FROM (SELECT
* FROM SoftwareFacet WHERE group='VREManagement' AND name='SmartExecutor'))
*
* WHERE @class='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(direction.opposite().name().toLowerCase());
selectStringBuilder.append("V('");
selectStringBuilder.append(elementType);
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 @class='");
selectStringBuilder.append(elementType);
selectStringBuilder.append("'");
}
String select = selectStringBuilder.toString();
logger.trace(select);
OResultSet resultSet = oDatabaseDocument.command(select,new HashMap<>());
while(resultSet.hasNext()) {
OResult oResult = resultSet.next();
OElement element = ElementManagement.getElementFromOptional(oResult.getElement());
if(polymorphic) {
OClass oClass = null;
try {
if(element instanceof OEdge) {
continue;
}
oClass = ElementManagement.getOClass(element);
} catch(Exception e) {
String error = String.format("Unable to detect type of %s. %s", element.toString(),
Utility.SHOULD_NOT_OCCUR_ERROR_MESSAGE);
logger.error(error, e);
throw new ResourceRegistryException(error);
}
if(oClass.isSubClassOf(elementType)) {
continue;
}
}
OVertex vertex = (OVertex) element;
@SuppressWarnings("rawtypes")
EntityManagement entityManagement = ElementManagementUtility.getEntityManagement(getWorkingContext(),
oDatabaseDocument, vertex);
try {
if(constraint.containsKey(Entity.HEADER_PROPERTY + "." + Header.UUID_PROPERTY)) {
String uuid = constraint.get(Entity.HEADER_PROPERTY + "." + Header.UUID_PROPERTY);
if(entityManagement.getUUID().compareTo(UUID.fromString(uuid)) == 0) {
continue;
}
}
JsonNode jsonNode = entityManagement.serializeAsJson();
arrayNode.add(jsonNode);
} catch(ResourceRegistryException e) {
logger.error("Unable to correctly serialize {}. It will be excluded from results. {}",
vertex.toString(), Utility.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,
2021-01-28 22:53:10 +01:00
boolean polymorphic, Map<String,String> constraint, boolean includeRelationInResult) throws ResourceRegistryException {
try {
oDatabaseDocument = getWorkingContext().getDatabaseDocument(PermissionMode.READER);
AccessType relationAccessType = ElementManagementUtility.getBaseAccessType(relationType);
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 = ElementManagementUtility.getBaseAccessType(referenceType);
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 != ODirection.OUT) {
String error = String.format("%s can only goes %s from %s.", relationType,
ODirection.OUT.name(), elementType);
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 != ODirection.IN
|| referenceAccessType != AccessType.RESOURCE) {
String error = String.format("%s can only has %s %s from a %s.", elementType,
ODirection.IN.name(), ConsistsOf.NAME, Resource.NAME);
throw new InvalidQueryException(error);
}
break;
default:
break;
}
2021-01-28 22:53:10 +01:00
return reallyQuery(relationType, referenceType, referenceUUID, direction, polymorphic, constraint, includeRelationInResult);
} catch(ResourceRegistryException e) {
throw e;
} catch(Exception e) {
throw new ResourceRegistryException(e);
} finally {
if(oDatabaseDocument != null) {
oDatabaseDocument.close();
}
}
}
}