resource-registry/src/main/java/org/gcube/informationsystem/resourceregistry/instances/type/SchemaManagementImpl.java

454 lines
18 KiB
Java

/**
*
*/
package org.gcube.informationsystem.resourceregistry.instances.type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.activation.UnsupportedDataTypeException;
import org.gcube.informationsystem.base.reference.AccessType;
import org.gcube.informationsystem.base.reference.entities.BaseEntity;
import org.gcube.informationsystem.base.reference.properties.BaseProperty;
import org.gcube.informationsystem.base.reference.relations.BaseRelation;
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.Property;
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.schema.SchemaAlreadyPresentException;
import org.gcube.informationsystem.resourceregistry.api.exceptions.schema.SchemaCreationException;
import org.gcube.informationsystem.resourceregistry.api.exceptions.schema.SchemaException;
import org.gcube.informationsystem.resourceregistry.api.exceptions.schema.SchemaNotFoundException;
import org.gcube.informationsystem.resourceregistry.instances.base.ERManagement;
import org.gcube.informationsystem.resourceregistry.instances.context.ContextUtility;
import org.gcube.informationsystem.resourceregistry.instances.type.entities.FacetTypeDefinitionManagement;
import org.gcube.informationsystem.resourceregistry.instances.type.entities.ResourceTypeDefinitionManagement;
import org.gcube.informationsystem.resourceregistry.instances.type.properties.PropertyTypeDefinitionManagement;
import org.gcube.informationsystem.resourceregistry.instances.type.relations.ConsistsOfTypeDefinitionManagement;
import org.gcube.informationsystem.resourceregistry.instances.type.relations.IsRelatedToTypeDefinitionManagement;
import org.gcube.informationsystem.resourceregistry.security.AdminSecurityContext;
import org.gcube.informationsystem.resourceregistry.security.SecurityContext.PermissionMode;
import org.gcube.informationsystem.types.Type;
import org.gcube.informationsystem.types.TypeBinder;
import org.gcube.informationsystem.types.reference.TypeDefinition;
import org.gcube.informationsystem.types.reference.entities.EntityTypeDefinition;
import org.gcube.informationsystem.types.reference.entities.FacetTypeDefinition;
import org.gcube.informationsystem.types.reference.entities.ResourceTypeDefinition;
import org.gcube.informationsystem.types.reference.properties.PropertyDefinition;
import org.gcube.informationsystem.types.reference.properties.PropertyTypeDefinition;
import org.gcube.informationsystem.types.reference.properties.ResourceEntryDefinition;
import org.gcube.informationsystem.types.reference.relations.ConsistsOfTypeDefinition;
import org.gcube.informationsystem.types.reference.relations.IsRelatedToTypeDefinition;
import org.gcube.informationsystem.types.reference.relations.RelationTypeDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.orientechnologies.orient.core.db.document.ODatabaseDocument;
import com.orientechnologies.orient.core.exception.OSchemaException;
import com.orientechnologies.orient.core.metadata.OMetadata;
import com.orientechnologies.orient.core.metadata.schema.OClass;
import com.orientechnologies.orient.core.metadata.schema.OProperty;
import com.orientechnologies.orient.core.metadata.schema.OSchema;
import com.orientechnologies.orient.core.metadata.schema.OType;
import com.orientechnologies.orient.core.record.OElement;
/**
* @author Luca Frosini (ISTI - CNR)
*/
public class SchemaManagementImpl implements SchemaManagement {
private static Logger logger = LoggerFactory.getLogger(SchemaManagementImpl.class);
protected String typeName;
protected OClass getOClass(OSchema oSchema, String type) throws SchemaException {
return oSchema.getClass(type);
}
public void setTypeName(String typeName) {
this.typeName = typeName;
}
/*
private static TypeDefinition getOClassTypeDefinition(OClass oClass) throws SchemaException {
try {
ODocument oDocument = ((OClassImpl) oClass).toStream();
String json = oDocument.toJSON();
ObjectMapper mapper = new ObjectMapper();
ObjectNode node = (ObjectNode) mapper.readTree(json);
if(oClass.isSubClassOf(Property.NAME)) {
node.put(ISManageable.CLASS_PROPERTY, PropertyTypeDefinition.NAME);
} else if(oClass.isSubClassOf(Resource.NAME)) {
node.put(ISManageable.CLASS_PROPERTY, ResourceTypeDefinition.NAME);
} else if(oClass.isSubClassOf(Facet.NAME)) {
node.put(ISManageable.CLASS_PROPERTY, FacetTypeDefinition.NAME);
} else if(oClass.isSubClassOf(IsRelatedTo.NAME)) {
node.put(ISManageable.CLASS_PROPERTY, IsRelatedToTypeDefinition.NAME);
} else if(oClass.isSubClassOf(ConsistsOf.NAME)) {
node.put(ISManageable.CLASS_PROPERTY, ConsistsOfTypeDefinition.NAME);
}
if(!oClass.isSubClassOf(Resource.NAME)) {
ArrayNode arrayNode = (ArrayNode) node.get(EntityTypeDefinition.PROPERTIES_PROPERTY);
Iterator<JsonNode> iterator = arrayNode.iterator();
while(iterator.hasNext()) {
ObjectNode propertyNode = (ObjectNode) iterator.next();
propertyNode.put(ISManageable.CLASS_PROPERTY, PropertyDefinition.NAME);
}
}
String managedJson = mapper.writeValueAsString(node);
logger.trace("{} -> {}", json, managedJson);
return TypeBinder.deserializeTypeDefinition(managedJson);
} catch(Exception e) {
throw new SchemaException(e);
}
}
*/
private TypeDefinition getTypeDefinition(OClass oClass) throws SchemaException {
try {
ERManagement<? extends OElement> erManagement = null;
if(oClass.isSubClassOf(Property.NAME)) {
erManagement = new PropertyTypeDefinitionManagement();
((PropertyTypeDefinitionManagement) erManagement).setName(oClass.getName());
} else if(oClass.isSubClassOf(Resource.NAME)) {
erManagement = new ResourceTypeDefinitionManagement();
((ResourceTypeDefinitionManagement) erManagement).setName(oClass.getName());
} else if(oClass.isSubClassOf(Facet.NAME)) {
erManagement = new FacetTypeDefinitionManagement();
((FacetTypeDefinitionManagement) erManagement).setName(oClass.getName());
} else if(oClass.isSubClassOf(IsRelatedTo.NAME)) {
erManagement = new IsRelatedToTypeDefinitionManagement();
((IsRelatedToTypeDefinitionManagement) erManagement).setName(oClass.getName());
} else if(oClass.isSubClassOf(ConsistsOf.NAME)) {
erManagement = new ConsistsOfTypeDefinitionManagement();
((ConsistsOfTypeDefinitionManagement) erManagement).setName(oClass.getName());
}
if(erManagement!=null) {
String ret = erManagement.read();
return TypeBinder.deserializeTypeDefinition(ret);
}else {
throw new SchemaException("You can only request schema of IS Model types and their specilization");
}
} catch(Exception e) {
throw new SchemaException(e);
}
}
protected String getTypeDefinitionAsString(OClass oClass) throws SchemaException {
try {
TypeDefinition typeDefinition = getTypeDefinition(oClass);
return TypeBinder.serializeTypeDefinition(typeDefinition);
} catch(Exception e) {
throw new SchemaException(e);
}
}
protected List<OClass> getSuperclassesAndCheckCompliancy(ODatabaseDocument oDatabaseDocument,
TypeDefinition typeDefinition, String baseType) throws SchemaException, SchemaNotFoundException {
Set<String> superClasses = typeDefinition.getSuperClasses();
if(baseType != null) {
if(superClasses == null || superClasses.size() == 0) {
throw new RuntimeException(
String.format("No Superclass found in schema %s. The Type Definition must extend %s",
typeDefinition, baseType));
}
}
OMetadata oMetadata = oDatabaseDocument.getMetadata();
OSchema oSchema = oMetadata.getSchema();
List<OClass> oSuperclasses = new ArrayList<>();
for(String superClass : superClasses) {
OClass oSuperClass = getOClass(oSchema, superClass);
if(oSuperClass == null) {
throw new SchemaNotFoundException("Superclass " + superClass + " does not exists");
}
if(baseType != null) {
if(typeDefinition.getName().compareTo(baseType) != 0) {
if(!oSuperClass.isSubClassOf(baseType)) {
throw new RuntimeException(superClass + " is not a subsclass of " + baseType
+ ". Each Superclass MUST be a subclass of " + baseType);
}
}
}
oSuperclasses.add(oSuperClass);
}
return oSuperclasses;
}
private static Set<String> baseTypes;
public static Set<String> typeDefinitionTypes;
static {
baseTypes = new HashSet<String>();
baseTypes.add(BaseProperty.NAME);
baseTypes.add(BaseEntity.NAME);
baseTypes.add(BaseRelation.NAME);
typeDefinitionTypes = new HashSet<String>();
typeDefinitionTypes.add(PropertyTypeDefinition.NAME);
typeDefinitionTypes.add(ResourceEntryDefinition.NAME);
typeDefinitionTypes.add(EntityTypeDefinition.NAME);
typeDefinitionTypes.add(ResourceTypeDefinition.NAME);
typeDefinitionTypes.add(FacetTypeDefinition.NAME);
typeDefinitionTypes.add(RelationTypeDefinition.NAME);
typeDefinitionTypes.add(IsRelatedToTypeDefinition.NAME);
typeDefinitionTypes.add(ConsistsOfTypeDefinition.NAME);
}
protected void registerTypeSchema(TypeDefinition typeDefinition, AccessType baseType)
throws SchemaAlreadyPresentException, SchemaException {
ODatabaseDocument oDatabaseDocument = null;
try {
if(typeName.compareTo(typeDefinition.getName()) != 0) {
String error = String.format(
"Provided type name path argument %s does not match with the type name in the definition %S. Please be coherent.",
typeName, typeDefinition.getName());
throw new SchemaCreationException(error);
}
AdminSecurityContext adminSecurityContext = ContextUtility.getAdminSecurityContext();
oDatabaseDocument = adminSecurityContext.getDatabaseDocument(PermissionMode.WRITER);
OMetadata oMetadata = oDatabaseDocument.getMetadata();
OSchema oSchema = oMetadata.getSchema();
OClass oClass = null;
if(BaseEntity.class.isAssignableFrom(baseType.getTypeClass())) {
oClass = oDatabaseDocument.createVertexClass(typeDefinition.getName());
} else if(BaseRelation.class.isAssignableFrom(baseType.getTypeClass())) {
oClass = oDatabaseDocument.createEdgeClass(typeDefinition.getName());
} else if(BaseProperty.class.isAssignableFrom(baseType.getTypeClass())) {
oClass = oSchema.createClass(typeDefinition.getName());
} else {
String error = String.format("Allowed superclass are %s, %s, %s, or any subclasses of them.",
Entity.NAME, Relation.NAME, Property.NAME);
throw new SchemaCreationException(error);
}
try {
String description = typeDefinition.getDescription();
if(description != null && description.compareTo("") != 0) {
try {
oClass.setDescription(description);
} catch(Exception e) {
logger.warn(
"Unable to set description. This is an orient bug. See https://github.com/orientechnologies/orientdb/issues/7065");
}
}
try {
// oClass.setAbstract(false); // Used to allow to persist Schema in Context
// Management
oClass.setAbstract(typeDefinition.isAbstract());
} catch(Exception e) {
logger.error(
"Unable to set the Vertex Type {} as abstract. This is an OrientDB <= 2.2.12 bug. The Type will be created as it is not abstract.",
typeDefinition.getName());
}
if(!baseTypes.contains(typeDefinition.getName())) {
List<OClass> oSuperclasses = getSuperclassesAndCheckCompliancy(oDatabaseDocument, typeDefinition,
baseType.getName());
oClass.setSuperClasses(oSuperclasses);
}
if(!(typeDefinition instanceof ResourceTypeDefinition)) {
// A Resource cannot contains any properties.
Set<PropertyDefinition> propertyDefinitions = typeDefinition.getProperties();
for(PropertyDefinition propertyDefinition : propertyDefinitions) {
OType oType = OType.getById(propertyDefinition.getType().byteValue());
/*
* Types update is not allowed,
* hence bug https://github.com/orientechnologies/orientdb/issues/7354 cannot occur
* Excluding the check from types used for type definition
*
*/
if(!typeDefinitionTypes.contains(typeDefinition.getName())) {
switch(oType) {
case EMBEDDEDLIST:
throw new UnsupportedDataTypeException(Type.OType.PROPERTYLIST
+ " support is currently disabled due to OrientDB bug see https://github.com/orientechnologies/orientdb/issues/7354");
case EMBEDDEDSET:
throw new UnsupportedDataTypeException(Type.OType.PROPERTYSET
+ " support is currently disabled due to OrientDB bug see https://github.com/orientechnologies/orientdb/issues/7354");
default:
break;
}
}
OProperty op = oClass.createProperty(propertyDefinition.getName(), oType);
op.setDescription(propertyDefinition.getDescription());
/*
* Mandatory and notNull does not work in distributed mode: so that on Type
* declaration they are forced to false
* ovp.setMandatory(property.isMandatory());
* ovp.setNotNull(property.isNotnull()); This information are persisted in
* Management Context
*/
op.setMandatory(false);
op.setNotNull(false);
op.setReadonly(propertyDefinition.isReadonly());
op.setRegexp(propertyDefinition.getRegexp());
if(propertyDefinition.getLinkedClass() != null) {
OClass linkedClass = getOClass(oSchema, propertyDefinition.getLinkedClass());
if(linkedClass == null) {
logger.trace("class {} not found in schema", propertyDefinition.getLinkedClass());
throw new Exception(
"class " + propertyDefinition.getLinkedClass() + " not found in schema");
}
if(linkedClass.isEdgeType() || linkedClass.isVertexType()) {
throw new Exception("A Property Field cannot be an Entity or a Relation");
}
op.setLinkedClass(linkedClass);
} else if(propertyDefinition.getLinkedType() != null) {
op.setLinkedType(OType.getById(propertyDefinition.getLinkedType().byteValue()));
}
}
}
oDatabaseDocument.commit();
logger.info("{} {} registered successfully", baseType.getName(), typeDefinition.getName());
} catch(Exception e) {
oSchema.dropClass(typeDefinition.getName());
throw e;
}
} catch(OSchemaException ex) {
if(ex.getMessage().contains("already exists")) {
throw new SchemaAlreadyPresentException(ex);
}
throw new SchemaException(ex);
} catch(SchemaException e) {
throw e;
} catch(Exception ex) {
throw new SchemaCreationException(ex);
} finally {
oDatabaseDocument.close();
}
}
protected String getSchema(String type, boolean includeSubtypes) throws SchemaNotFoundException, SchemaException {
ODatabaseDocument oDatabaseDocument = null;
try {
AdminSecurityContext adminSecurityContext = ContextUtility.getAdminSecurityContext();
oDatabaseDocument = adminSecurityContext.getDatabaseDocument(PermissionMode.READER);
OMetadata oMetadata = oDatabaseDocument.getMetadata();
OSchema oSchema = oMetadata.getSchema();
OClass baseOClass = oSchema.getClass(type);
List<TypeDefinition> typeDefinitions = new ArrayList<>();
typeDefinitions.add(getTypeDefinition(baseOClass));
if(includeSubtypes) {
Collection<OClass> subClasses = baseOClass.getAllSubclasses();
for(OClass oClass : subClasses) {
typeDefinitions.add(getTypeDefinition(oClass));
}
}
return TypeBinder.serializeTypeDefinitions(typeDefinitions);
} catch(SchemaException e) {
throw e;
} catch(Exception e) {
throw new SchemaException(e);
} finally {
if(oDatabaseDocument != null) {
oDatabaseDocument.close();
}
}
}
@Override
public String create(String jsonSchema, AccessType accessType) throws SchemaAlreadyPresentException, SchemaException {
TypeDefinition typeDefinition = null;
try {
try {
typeDefinition = TypeBinder.deserializeTypeDefinition(jsonSchema);
logger.info("Trying to register {} {} : {}", accessType.getName(), typeDefinition.getName(),
jsonSchema);
} catch(Exception e) {
logger.error("Error while trying to register {} {}", accessType.getName(), jsonSchema);
throw new SchemaCreationException(e);
}
registerTypeSchema(typeDefinition, accessType);
ERManagement<? extends OElement> erManagement = null;
if(Resource.class.isAssignableFrom(accessType.getTypeClass())) {
erManagement = new ResourceTypeDefinitionManagement();
}else if(Facet.class.isAssignableFrom(accessType.getTypeClass())) {
erManagement = new FacetTypeDefinitionManagement();
} else if(IsRelatedTo.class.isAssignableFrom(accessType.getTypeClass())) {
erManagement = new IsRelatedToTypeDefinitionManagement();
} else if(ConsistsOf.class.isAssignableFrom(accessType.getTypeClass())) {
erManagement = new ConsistsOfTypeDefinitionManagement();
} else if(Property.class.isAssignableFrom(accessType.getTypeClass())) {
erManagement = new PropertyTypeDefinitionManagement();
}
String ret = null;
if(erManagement!=null) {
erManagement.setJson(jsonSchema);
ret = erManagement.create();
}else {
ret = TypeBinder.serializeTypeDefinition(typeDefinition);
}
return ret;
} catch(SchemaAlreadyPresentException e) {
throw e;
} catch(SchemaException e) {
throw e;
} catch(Exception ex) {
throw new SchemaCreationException(ex);
}
}
@Override
public String read(String entityType, boolean includeSubtypes) throws SchemaNotFoundException, SchemaException {
return getSchema(entityType, includeSubtypes);
}
@Override
public String update(String entityType, AccessType accessType, String jsonSchema)
throws SchemaNotFoundException, SchemaException {
throw new UnsupportedOperationException();
}
@Override
public String delete(String entityType, AccessType accessType) throws SchemaNotFoundException {
throw new UnsupportedOperationException();
}
}