resource-registry/src/main/java/org/gcube/informationsystem/resourceregistry/ermanagement/relation/RelationManagement.java

636 lines
18 KiB
Java
Raw Normal View History

/**
*
*/
package org.gcube.informationsystem.resourceregistry.ermanagement.relation;
import java.io.IOException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.UUID;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.gcube.informationsystem.model.embedded.Header;
import org.gcube.informationsystem.model.embedded.PropagationConstraint;
import org.gcube.informationsystem.model.embedded.PropagationConstraint.RemoveConstraint;
import org.gcube.informationsystem.model.entity.Entity;
import org.gcube.informationsystem.model.entity.Facet;
import org.gcube.informationsystem.model.entity.Resource;
import org.gcube.informationsystem.model.relation.ConsistsOf;
import org.gcube.informationsystem.model.relation.IsRelatedTo;
import org.gcube.informationsystem.model.relation.Relation;
import org.gcube.informationsystem.resourceregistry.api.exceptions.ResourceRegistryException;
import org.gcube.informationsystem.resourceregistry.api.exceptions.context.ContextException;
import org.gcube.informationsystem.resourceregistry.api.exceptions.relation.RelationNotFoundException;
import org.gcube.informationsystem.resourceregistry.api.exceptions.schema.SchemaNotFoundException;
import org.gcube.informationsystem.resourceregistry.context.SecurityContextMapper;
import org.gcube.informationsystem.resourceregistry.context.SecurityContextMapper.PermissionMode;
import org.gcube.informationsystem.resourceregistry.ermanagement.ERManagement;
import org.gcube.informationsystem.resourceregistry.ermanagement.entity.EntityManagement;
import org.gcube.informationsystem.resourceregistry.ermanagement.entity.FacetManagement;
import org.gcube.informationsystem.resourceregistry.ermanagement.entity.ResourceManagement;
import org.gcube.informationsystem.resourceregistry.resources.impl.SchemaManagementImpl;
import org.gcube.informationsystem.resourceregistry.resources.utils.ContextUtility;
import org.gcube.informationsystem.resourceregistry.resources.utils.HeaderUtility;
import org.gcube.informationsystem.resourceregistry.resources.utils.Utility;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tinkerpop.blueprints.Direction;
import com.tinkerpop.blueprints.Edge;
import com.tinkerpop.blueprints.Vertex;
import com.tinkerpop.blueprints.impls.orient.OrientBaseGraph;
import com.tinkerpop.blueprints.impls.orient.OrientEdge;
import com.tinkerpop.blueprints.impls.orient.OrientEdgeType;
import com.tinkerpop.blueprints.impls.orient.OrientGraph;
/**
* @author Luca Frosini (ISTI - CNR)
*
*/
@SuppressWarnings("rawtypes")
public abstract class RelationManagement<R extends Relation> {
private static Logger logger = LoggerFactory
.getLogger(RelationManagement.class);
public final Set<String> ignoreKeys;
public final Set<String> ignoreStartWithKeys;
public static final String AT = "@";
public static final String UNDERSCORE = "_";
protected final Class<R> relationClass;
protected final String baseType;
protected final Class<? extends Entity> targetEntityClass;
protected OrientGraph orientGraph;
protected UUID uuid;
protected JsonNode jsonNode;
protected String relationType;
protected Edge edge;
protected RelationManagement(Class<R> relationClass) {
this.ignoreKeys = new HashSet<String>();
this.ignoreKeys.add(Relation.HEADER_PROPERTY);
this.ignoreKeys.add(Relation.TARGET_PROPERTY);
this.ignoreKeys.add(Relation.SOURCE_PROPERTY);
this.ignoreKeys.add(OrientBaseGraph.CONNECTION_IN.toLowerCase());
this.ignoreKeys.add(OrientBaseGraph.CONNECTION_OUT.toLowerCase());
this.ignoreKeys.add(OrientBaseGraph.CONNECTION_IN.toUpperCase());
this.ignoreKeys.add(OrientBaseGraph.CONNECTION_OUT.toUpperCase());
this.ignoreStartWithKeys = new HashSet<String>();
this.ignoreStartWithKeys.add(AT);
this.ignoreStartWithKeys.add(UNDERSCORE);
this.relationClass = relationClass;
if (ConsistsOf.class.isAssignableFrom(relationClass)) {
this.baseType = ConsistsOf.NAME;
this.targetEntityClass = Facet.class;
} else if (IsRelatedTo.class.isAssignableFrom(relationClass)) {
this.baseType = IsRelatedTo.NAME;
this.targetEntityClass = Resource.class;
} else {
this.baseType = Relation.NAME;
this.targetEntityClass = Resource.class;
}
}
protected RelationManagement(Class<R> relationClass, OrientGraph orientGraph) {
this(relationClass);
this.orientGraph = orientGraph;
}
public void setEdge(Edge edge) {
this.edge = edge;
}
public void setUUID(UUID uuid) throws ResourceRegistryException {
this.uuid = uuid;
if (jsonNode != null) {
checkUUIDMatch();
}
}
protected void checkJSON() throws ResourceRegistryException {
if (uuid == null) {
try {
uuid = org.gcube.informationsystem.impl.utils.Utility
.getUUIDFromJsonNode(jsonNode);
} catch (Exception e) {
}
} else {
checkUUIDMatch();
}
if (this.relationType == null) {
this.relationType = ERManagement.getClassProperty(jsonNode);
} else {
checkEntityMatch();
}
}
public void setJSON(JsonNode jsonNode) throws ResourceRegistryException {
this.jsonNode = jsonNode;
checkJSON();
}
public void setJSON(String jsonRepresentation)
throws ResourceRegistryException {
ObjectMapper mapper = new ObjectMapper();
try {
this.jsonNode = mapper.readTree(jsonRepresentation);
} catch (IOException e) {
throw new ResourceRegistryException(e);
}
checkJSON();
}
public void setRelationType(String relationType)
throws ResourceRegistryException {
this.relationType = relationType;
if (relationType == null || relationType.compareTo("") == 0) {
if (ConsistsOf.class.isAssignableFrom(relationClass)) {
this.relationType = ConsistsOf.NAME;
} else if (IsRelatedTo.class.isAssignableFrom(relationClass)) {
this.relationType = IsRelatedTo.NAME;
}
}
if (jsonNode != null) {
checkEntityMatch();
}
}
protected void checkEntityMatch() throws ResourceRegistryException {
String type = ERManagement.getClassProperty(jsonNode);
if (type != null && type.compareTo(relationType) != 0) {
String error = String
.format("Declared resourceType does not match with json representation %s!=%s",
relationType, type);
logger.trace(error);
throw new ResourceRegistryException(error);
}
try {
SchemaManagementImpl.getTypeSchema(relationType, baseType);
} catch (SchemaNotFoundException e) {
throw e;
}
}
protected void checkUUIDMatch() throws ResourceRegistryException {
Header header = null;
try {
header = HeaderUtility.getHeader(jsonNode, false);
} catch (Exception e) {
throw new ResourceRegistryException(e);
}
if (header != null) {
UUID resourceUUID = header.getUUID();
if (resourceUUID.compareTo(uuid) != 0) {
String error = String
.format("UUID provided in header (%s) differs from the one (%s) used to identify the %s instance",
resourceUUID.toString(), uuid.toString(),
relationType);
throw new ResourceRegistryException(error);
}
}
}
public Edge getEdge() throws ResourceRegistryException {
try {
if (edge == null) {
edge = Utility.getElementByUUID(orientGraph,
relationType == null ? baseType : relationType, uuid,
Edge.class);
}
return edge;
} catch (ResourceRegistryException e) {
throw new RelationNotFoundException(e);
}
}
public String serialize() throws ResourceRegistryException {
return serializeAsJson().toString();
}
public JSONObject serializeAsJson() throws ResourceRegistryException {
JSONObject ret = Utility.toJsonObject((OrientEdge) getEdge(), false);
Vertex vertex = edge.getVertex(Direction.IN);
EntityManagement entityManagement = EntityManagement
.getEntityManagement(orientGraph, vertex);
try {
ret.put(Relation.TARGET_PROPERTY,
entityManagement.serializeAsJson());
} catch (JSONException e) {
new ResourceRegistryException(e);
}
return ret;
}
public Edge reallyCreate(UUID sourceUUID, UUID targetUUID)
throws ResourceRegistryException {
ResourceManagement srmSource = new ResourceManagement(orientGraph);
srmSource.setUUID(sourceUUID);
Vertex source = srmSource.getVertex();
EntityManagement entityManagement = getEntityManagement();
entityManagement.setUUID(targetUUID);
Vertex target = entityManagement.getVertex();
return reallyCreate(source, target);
}
protected Edge reallyCreate(Vertex source, Vertex target)
throws ResourceRegistryException {
// TODO Check the relation compatibility between source and target
logger.trace("Creating {} ({}) beetween {} -> {}",
Relation.class.getSimpleName(), relationType,
Utility.toJsonString(source, true),
Utility.toJsonString(target, true));
edge = orientGraph.addEdge(null, source, target, relationType);
ERManagement.updateProperties(edge, jsonNode, ignoreKeys,
ignoreStartWithKeys);
HeaderUtility.addHeader(edge, null);
ContextUtility.addToActualContext(orientGraph, edge);
((OrientEdge) edge).save();
logger.info("{} successfully created", relationType);
return edge;
}
public Edge reallyCreate(Vertex source) throws ResourceRegistryException {
Vertex target = null;
EntityManagement entityManagement = getEntityManagement();
if (!jsonNode.has(Relation.TARGET_PROPERTY)) {
throw new ResourceRegistryException(
"Error while creating relation. No target definition found");
}
entityManagement.setJSON(jsonNode.get(Relation.TARGET_PROPERTY));
try {
target = entityManagement.getVertex();
} catch (Exception e) {
target = entityManagement.reallyCreate();
}
return reallyCreate(source, target);
}
public Edge reallyCreate(UUID sourceUUID) throws ResourceRegistryException {
ResourceManagement srmSource = new ResourceManagement(orientGraph);
srmSource.setUUID(sourceUUID);
Vertex source = srmSource.getVertex();
return reallyCreate(source);
}
public Edge reallyUpdate() throws ResourceRegistryException {
logger.debug("Trying to update {} : {}", relationClass.getSimpleName(),
jsonNode);
Edge edge = getEdge();
ERManagement.updateProperties(edge, jsonNode, ignoreKeys,
ignoreStartWithKeys);
if (ConsistsOf.class.isAssignableFrom(relationClass)) {
JsonNode target = jsonNode.get(Relation.TARGET_PROPERTY);
if (target != null) {
FacetManagement fm = new FacetManagement(orientGraph);
fm.setJSON(target);
fm.reallyUpdate();
}
}
logger.info("{} {} successfully updated", relationType, jsonNode);
return edge;
}
public boolean reallyAddToContext() throws ContextException,
ResourceRegistryException {
getEdge();
// TODO check add integrity directive
ContextUtility.addToActualContext(orientGraph, edge);
Vertex vertex = edge.getVertex(Direction.IN);
EntityManagement entityManagement = EntityManagement
.getEntityManagement(orientGraph, vertex);
entityManagement.reallyAddToContext();
return true;
}
public boolean reallyRemoveFromContext() throws ContextException,
ResourceRegistryException {
throw new UnsupportedOperationException();
}
protected EntityManagement getEntityManagement()
throws ResourceRegistryException {
EntityManagement entityManagement;
if (ConsistsOf.class.isAssignableFrom(relationClass)) {
entityManagement = new FacetManagement(orientGraph);
} else if (IsRelatedTo.class.isAssignableFrom(relationClass)) {
entityManagement = new ResourceManagement(orientGraph);
} else {
String error = String.format("{%s is not a %s nor a %s. "
+ "This is really strange ad should not occur. "
+ "Please Investigate it.", relationClass, ConsistsOf.NAME,
IsRelatedTo.NAME);
throw new ResourceRegistryException(error);
}
return entityManagement;
}
public static RelationManagement getRelationManagement(
OrientGraph orientGraph, Edge edge)
throws ResourceRegistryException {
OrientEdgeType orientEdgeType = ((OrientEdge) edge).getType();
RelationManagement relationManagement = null;
if (orientEdgeType.isSubClassOf(ConsistsOf.NAME)) {
relationManagement = new ConsistsOfManagement(orientGraph);
} else if (orientEdgeType.isSubClassOf(IsRelatedTo.NAME)) {
relationManagement = new IsRelatedToManagement(orientGraph);
} else {
String error = String.format("{%s is not a %s nor a %s. "
+ "This is really strange ad should not occur. "
+ "Please Investigate it.", edge, ConsistsOf.NAME,
IsRelatedTo.NAME);
throw new ResourceRegistryException(error);
}
relationManagement.setEdge(edge);
return relationManagement;
}
protected boolean deleteTargetVertex(Vertex target)
throws ResourceRegistryException {
EntityManagement entityManagement = EntityManagement
.getEntityManagement(orientGraph, target);
if (entityManagement != null) {
entityManagement.reallyDelete();
return true;
} else {
return false;
}
}
public boolean reallyDelete() throws RelationNotFoundException,
ResourceRegistryException {
getEdge();
RemoveConstraint removeConstraint = RemoveConstraint.keep;
try {
PropagationConstraint propagationConstraint = Utility.getEmbedded(
PropagationConstraint.class, edge, Relation.PROPAGATION_CONSTRAINT);
if(propagationConstraint.getRemoveConstraint()!=null){
removeConstraint = propagationConstraint.getRemoveConstraint();
}
} catch (Exception e) {
logger.warn("Error while getting {} from {}. Assuming {}. "
+ "This is really strange and should not occur. "
+ "Please Investigate it.",
Relation.PROPAGATION_CONSTRAINT,
Utility.toJsonString(edge, true),
RemoveConstraint.keep);
}
Vertex target = edge.getVertex(Direction.IN);
edge.remove();
switch (removeConstraint) {
case cascade:
deleteTargetVertex(target);
break;
case cascadeWhenOrphan:
Iterable<Edge> iterable = target.getEdges(Direction.IN);
Iterator<Edge> iterator = iterable.iterator();
if (iterator.hasNext()) {
logger.trace(
"{} point to {} which is not orphan. Giving {} directive, it will be keep.",
edge, target, removeConstraint);
} else {
deleteTargetVertex(target);
}
break;
case keep:
break;
default:
break;
}
return true;
}
public String create(UUID sourceUUID, UUID targetUUID)
throws ResourceRegistryException {
try {
orientGraph = ContextUtility
.getActualSecurityContextGraph(PermissionMode.WRITER);
edge = reallyCreate(sourceUUID, targetUUID);
orientGraph.commit();
return serialize();
} catch (ResourceRegistryException e) {
if (orientGraph != null) {
orientGraph.rollback();
}
throw e;
} catch (Exception e) {
if (orientGraph != null) {
orientGraph.rollback();
}
throw new ResourceRegistryException(e);
} finally {
if (orientGraph != null) {
orientGraph.shutdown();
}
}
}
public String read() throws RelationNotFoundException,
ResourceRegistryException {
try {
orientGraph = ContextUtility
.getActualSecurityContextGraph(PermissionMode.WRITER);
return serialize();
} catch (ResourceRegistryException e) {
if (orientGraph != null) {
orientGraph.rollback();
}
throw e;
} catch (Exception e) {
if (orientGraph != null) {
orientGraph.rollback();
}
throw new ResourceRegistryException(e);
} finally {
if (orientGraph != null) {
orientGraph.shutdown();
}
}
}
public String update() throws RelationNotFoundException,
ResourceRegistryException {
try {
orientGraph = ContextUtility
.getActualSecurityContextGraph(PermissionMode.WRITER);
edge = reallyUpdate();
return serialize();
} catch (ResourceRegistryException e) {
if (orientGraph != null) {
orientGraph.rollback();
}
throw e;
} catch (Exception e) {
if (orientGraph != null) {
orientGraph.rollback();
}
throw new ResourceRegistryException(e);
} finally {
if (orientGraph != null) {
orientGraph.shutdown();
}
}
}
public boolean delete() throws RelationNotFoundException,
ResourceRegistryException {
logger.debug(
"Going to remove {} with UUID {}. Related {}s will be detached.",
baseType, uuid, targetEntityClass.getSimpleName());
try {
orientGraph = ContextUtility
.getActualSecurityContextGraph(PermissionMode.WRITER);
boolean deleted = reallyDelete();
orientGraph.commit();
logger.info("{} {} with UUID {} successfully removed.", baseType,
uuid);
return deleted;
} catch (ResourceRegistryException e) {
logger.error("Unable to remove {} with UUID.", baseType, uuid);
if (orientGraph != null) {
orientGraph.rollback();
}
throw e;
} catch (Exception e) {
logger.error("Unable to remove {} with UUID {}.", baseType, uuid);
if (orientGraph != null) {
orientGraph.rollback();
}
throw new ResourceRegistryException(e);
} finally {
if (orientGraph != null) {
orientGraph.shutdown();
}
}
}
public boolean addToContext() throws ContextException {
logger.debug("Going to add {} with UUID {} to actual Context",
baseType, uuid);
try {
orientGraph = SecurityContextMapper.getSecurityContextFactory(
SecurityContextMapper.ADMIN_SECURITY_CONTEXT_UUID,
PermissionMode.WRITER).getTx();
boolean added = reallyAddToContext();
orientGraph.commit();
logger.info("{} with UUID {} successfully added to actual Context",
baseType, uuid);
return added;
} catch (Exception e) {
logger.error("Unable to add {} with UUID {} to actual Context",
baseType, uuid, e);
if (orientGraph != null) {
orientGraph.rollback();
}
throw new ContextException(e);
} finally {
if (orientGraph != null) {
orientGraph.shutdown();
}
}
}
public boolean removeFromContext() throws ContextException {
logger.debug("Going to remove {} with UUID {} from actual Context",
baseType, uuid);
try {
orientGraph = SecurityContextMapper.getSecurityContextFactory(
SecurityContextMapper.ADMIN_SECURITY_CONTEXT_UUID,
PermissionMode.WRITER).getTx();
boolean removed = reallyRemoveFromContext();
orientGraph.commit();
logger.info(
"{} with UUID {} successfully removed from actual Context",
baseType, uuid);
return removed;
} catch (Exception e) {
logger.error(
"Unable to remove {} with UUID {} from actual Context",
baseType, uuid, e);
if (orientGraph != null) {
orientGraph.rollback();
}
throw new ContextException(e);
} finally {
if (orientGraph != null) {
orientGraph.shutdown();
}
}
}
}