/** * */ 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 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 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 getSuperclassesAndCheckCompliancy(ODatabaseDocument oDatabaseDocument, TypeDefinition typeDefinition, String baseType) throws SchemaException, SchemaNotFoundException { Set 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 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 baseTypes; public static Set typeDefinitionTypes; static { baseTypes = new HashSet(); baseTypes.add(BaseProperty.NAME); baseTypes.add(BaseEntity.NAME); baseTypes.add(BaseRelation.NAME); typeDefinitionTypes = new HashSet(); 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 oSuperclasses = getSuperclassesAndCheckCompliancy(oDatabaseDocument, typeDefinition, baseType.getName()); oClass.setSuperClasses(oSuperclasses); } if(!(typeDefinition instanceof ResourceTypeDefinition)) { // A Resource cannot contains any properties. Set 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 typeDefinitions = new ArrayList<>(); typeDefinitions.add(getTypeDefinition(baseOClass)); if(includeSubtypes) { Collection 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 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(); } }