package org.gcube.informationsystem.resourceregistry.queries.json; import java.io.IOException; import java.util.HashMap; import java.util.Map; 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.informationsystem.base.reference.AccessType; import org.gcube.informationsystem.base.reference.Direction; import org.gcube.informationsystem.resourceregistry.api.exceptions.ResourceRegistryException; 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.SchemaNotFoundException; import org.gcube.informationsystem.resourceregistry.contexts.ContextUtility; 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.queries.json.base.JsonQueryERElement; import org.gcube.informationsystem.resourceregistry.queries.json.base.entities.JsonQueryFacet; import org.gcube.informationsystem.resourceregistry.queries.json.base.entities.JsonQueryResource; import org.gcube.informationsystem.resourceregistry.queries.json.base.relations.JsonQueryConsistsOf; import org.gcube.informationsystem.resourceregistry.queries.json.base.relations.JsonQueryIsRelatedTo; import org.gcube.informationsystem.resourceregistry.types.CachedType; import org.gcube.informationsystem.resourceregistry.types.TypesCache; import org.gcube.informationsystem.resourceregistry.utils.DBUtility; import org.gcube.informationsystem.utils.TypeUtility; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.arcadedb.database.Document; import com.arcadedb.query.sql.executor.Result; import com.arcadedb.query.sql.executor.ResultSet; import com.arcadedb.remote.RemoteDatabase; /** * @author Luca Frosini (ISTI - CNR) */ public class JsonQuery { private static Logger logger = LoggerFactory.getLogger(JsonQuery.class); private static final Integer UNBOUNDED_LIMIT = -1; protected ObjectMapper objectMapper; protected JsonNode jsonQuery; protected JsonQueryERElement entryPoint; protected RemoteDatabase database; public JsonQuery() { this.objectMapper = new ObjectMapper(); } public void setJsonQuery(JsonNode jsonQuery) { this.jsonQuery = jsonQuery; } public void setJsonQuery(String jsonQuery) throws InvalidQueryException { try { this.jsonQuery = objectMapper.readTree(jsonQuery); } catch (IOException e) { throw new InvalidQueryException(e); } } public static JsonQueryERElement getJsonQueryERElement(JsonNode jsonQuery) throws SchemaNotFoundException, SchemaException, ResourceRegistryException { String type = TypeUtility.getTypeName(jsonQuery); AccessType accessType = TypesCache.getInstance().getCachedType(type).getAccessType(); JsonQueryERElement jsonQueryERElement = null; switch (accessType) { case RESOURCE: jsonQueryERElement = new JsonQueryResource(jsonQuery); jsonQueryERElement.setDirection(Direction.OUT); break; case FACET: jsonQueryERElement = new JsonQueryFacet(jsonQuery); break; case IS_RELATED_TO: jsonQueryERElement = new JsonQueryIsRelatedTo(jsonQuery); break; case CONSISTS_OF: jsonQueryERElement = new JsonQueryConsistsOf(jsonQuery); break; default: throw new InvalidQueryException(String.format("%s is not querable", type.toString())); } return jsonQueryERElement; } public StringBuffer createQuery() throws SchemaException, InvalidQueryException, ResourceRegistryException { entryPoint = getJsonQueryERElement(jsonQuery); entryPoint.setEntryPoint(true); return entryPoint.analize(new StringBuffer()); } public String query() throws InvalidQueryException, ResourceRegistryException { RemoteDatabase current = ContextUtility.getCurrentODatabaseDocumentFromThreadLocal(); database = null; try { SecurityContext securityContext = ContextUtility.getCurrentSecurityContext(); database = securityContext.getRemoteDatabase(PermissionMode.READER); database.begin(); StringBuffer stringBuffer = createQuery(); stringBuffer.append(" limit :limit"); Map map = new HashMap<>(); map.put("limit", JsonQuery.UNBOUNDED_LIMIT); String query = stringBuffer.toString(); logger.trace("Going to execute the following query:\n{} \n from the JSONQuery\n{}", query, objectMapper.writeValueAsString(jsonQuery)); ResultSet resultSet = database.command("sql", query, map); ArrayNode arrayNode = objectMapper.createArrayNode(); while(resultSet.hasNext()) { Result oResult = resultSet.next(); Document element = ElementManagementUtility.getElementFromOptional(oResult.getElement()); try { JsonNode jsonNodeResult = null; ElementManagement erManagement = ElementManagementUtility.getERManagement(securityContext, database, element); // To support polymorphism we do not include ="TypeName" in query. So we need post processing filtering of results String requestedType = entryPoint.getType(); String gotType = erManagement.getTypeName(); if(requestedType.compareTo(gotType)==0) { erManagement.setAsEntryPoint(); jsonNodeResult = erManagement.serializeAsJsonNode(); arrayNode.add(jsonNodeResult); continue; } CachedType cachedType = TypesCache.getInstance().getCachedType(gotType); if(cachedType.getSuperTypes().contains(requestedType)) { erManagement.setAsEntryPoint(); jsonNodeResult = erManagement.serializeAsJsonNode(); arrayNode.add(jsonNodeResult); continue; } } catch(ResourceRegistryException e) { logger.error("Unable to correctly serialize {}. It will be excluded from results. {}", element.toString(), DBUtility.SHOULD_NOT_OCCUR_ERROR_MESSAGE); } } return objectMapper.writeValueAsString(arrayNode); } catch(Exception e) { throw new InvalidQueryException(e.getMessage()); } finally { if(database != null) { database.close(); } // if(current!=null) { // current.activateOnCurrentThread(); // } } } }