package org.gcube.informationsystem.resourceregistry.queries.templates; 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; import org.gcube.informationsystem.queries.templates.reference.entities.QueryTemplate; import org.gcube.informationsystem.resourceregistry.api.exceptions.AlreadyPresentException; import org.gcube.informationsystem.resourceregistry.api.exceptions.NotFoundException; 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.SchemaViolationException; import org.gcube.informationsystem.resourceregistry.contexts.security.QueryTemplatesSecurityContext; import org.gcube.informationsystem.resourceregistry.contexts.security.SecurityContext; import org.gcube.informationsystem.resourceregistry.instances.base.ElementManagementUtility; import org.gcube.informationsystem.resourceregistry.instances.base.entities.EntityElementManagement; import org.gcube.informationsystem.resourceregistry.queries.json.JsonQuery; import org.gcube.informationsystem.resourceregistry.utils.DBUtility; import org.gcube.informationsystem.serialization.ElementMapper; import org.gcube.informationsystem.types.reference.entities.EntityType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.arcadedb.database.Document; import com.arcadedb.graph.Vertex; 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 QueryTemplateManagement extends EntityElementManagement { private static Logger logger = LoggerFactory.getLogger(QueryTemplateManagement.class); protected String name; protected JsonNode params; public QueryTemplateManagement() { super(AccessType.QUERY_TEMPLATE); this.typeName = QueryTemplate.NAME; } public QueryTemplateManagement(RemoteDatabase database) throws ResourceRegistryException { this(); this.database = database; getWorkingContext(); } protected void checkERMatch() throws ResourceRegistryException { getDocumentType(); } protected void checkNameMatch() throws ResourceRegistryException { if(jsonNode!=null && name!=null) { String jsonName = jsonNode.get(QueryTemplate.NAME_PROPERTY).asText(); if(name.compareTo(jsonName)!=0) { String error = String.format( "Name provided in json (%s) differs from the one (%s) used to identify the %s instance", jsonName, name, typeName); throw new ResourceRegistryException(error); } } } public void setName(String name) throws ResourceRegistryException { this.name = name; checkNameMatch(); } protected void checkJsonNode() throws ResourceRegistryException { super.checkJsonNode(); checkNameMatch(); } public String getName() { if (name == null) { if (element == null) { if (jsonNode != null) { name = jsonNode.get(QueryTemplate.NAME_PROPERTY).asText(); } } else { name = element.getString(QueryTemplate.NAME_PROPERTY); } } return name; } @Override protected SecurityContext getWorkingContext() throws ResourceRegistryException { if (workingContext == null) { workingContext = QueryTemplatesSecurityContext.getInstance(); } return workingContext; } @Override protected JsonNode createCompleteJsonNode() throws ResourceRegistryException { try { JsonNode queryTemplate = serializeSelfAsJsonNode(); ObjectMapper objectMapper = new ObjectMapper(); String templateString = element.getString(QueryTemplate.TEMPLATE_PROPERTY); JsonNode templateJsonNode = objectMapper.readTree(templateString); ((ObjectNode) queryTemplate).replace(QueryTemplate.TEMPLATE_PROPERTY, templateJsonNode); return queryTemplate; }catch (ResourceRegistryException e) { throw e; }catch (Exception e) { throw new ResourceRegistryException(e); } } protected StringBuffer getSelectQuery() { StringBuffer select = new StringBuffer(); select.append("SELECT FROM "); select.append(QueryTemplate.NAME); select.append(" WHERE "); select.append(QueryTemplate.NAME_PROPERTY); select.append(" = "); select.append("\""); select.append(getName()); select.append("\""); return select; } protected void checkIfNameAlreadyExists() throws AlreadyPresentException { StringBuffer select = getSelectQuery(); StringBuffer errorMessage = new StringBuffer(); errorMessage.append("A "); errorMessage.append(QueryTemplate.NAME); errorMessage.append(" with "); errorMessage.append(this.getName()); errorMessage.append(" already exists"); logger.trace("Checking if {} -> {}", errorMessage, select); ResultSet resultSet = database.command("sql", select.toString()); if (resultSet != null) { try { if(resultSet.hasNext()) { throw new AlreadyPresentException(errorMessage.toString()); } }finally { resultSet.close(); } } } protected void tryTemplate() throws Exception { QueryTemplate queryTemplate = ElementMapper.unmarshal(QueryTemplate.class, jsonNode.toString()); JsonNode jsonQueryNode = queryTemplate.getJsonQuery(); JsonQuery jsonQuery = new JsonQuery(); jsonQuery.setJsonQuery(jsonQueryNode); try { jsonQuery.query(); }catch (ResourceRegistryException e) { throw e; }catch (Exception e) { throw new ResourceRegistryException(e); } } @Override public Vertex retrieveElement() throws NotFoundException, ResourceRegistryException { try { StringBuffer select = getSelectQuery(); ResultSet resultSet = database.query("sql", select.toString()); if(resultSet == null || !resultSet.hasNext()) { if(resultSet!=null) { resultSet.close(); } throw new NotFoundException("Error retrieving " + QueryTemplate.NAME + " with name " + getName()); } Result result = resultSet.next(); Vertex queryTemplate = ElementManagementUtility.getElementFromOptional(result.getVertex()); logger.trace("{} representing vertex is {}", QueryTemplate.NAME, DBUtility.getAsStringForLogging(queryTemplate)); if(resultSet.hasNext()) { resultSet.close(); throw new NotFoundException("Found more than one " + QueryTemplate.NAME + " with name " + name + ". This should not occur, please contact the administrator"); } resultSet.close(); return queryTemplate; } catch(NotFoundException e) { throw getSpecificNotFoundException(e); } catch(ResourceRegistryException e) { throw e; } catch(Exception e) { throw new ResourceRegistryException(e); } } @Override protected Vertex reallyCreate() throws AlreadyPresentException, InvalidQueryException, ResourceRegistryException { try { checkIfNameAlreadyExists(); tryTemplate(); createVertex(); return getElement(); } catch(ResourceRegistryException e) { throw e; } catch(Exception e) { logger.trace("Error while creating {} for {} ({}) using {}", Vertex.class.getSimpleName(), accessType.getName(), typeName, jsonNode, e); throw new ResourceRegistryException("Error Creating " + typeName + " with " + jsonNode, e.getCause()); } } @Override protected Vertex reallyUpdate() throws NotFoundException, ResourceRegistryException { try { tryTemplate(); Vertex queryTemplate = getElement(); queryTemplate = (Vertex) updateProperties(documentType, queryTemplate, jsonNode, ignoreKeys, ignoreStartWithKeys); return getElement(); } catch(ResourceRegistryException e) { throw e; } catch(Exception e) { logger.trace("Error while creating {} for {} ({}) using {}", Vertex.class.getSimpleName(), accessType.getName(), typeName, jsonNode, e); throw new ResourceRegistryException("Error Creating " + typeName + " with " + jsonNode, e.getCause()); } } @Override protected void reallyDelete() throws NotFoundException, ResourceRegistryException { logger.debug("Going to delete {} with name {}", accessType.getName(), name); getElement().delete(); } @Override protected NotFoundException getSpecificNotFoundException(NotFoundException e) { return new NotFoundException(e); } @Override protected AlreadyPresentException getSpecificAlreadyPresentException(String message) { return new AlreadyPresentException(message); } @Override public String reallyGetAll(boolean polymorphic) throws ResourceRegistryException { ObjectMapper objectMapper = new ObjectMapper(); ArrayNode arrayNode = objectMapper.createArrayNode(); Iterable iterable = database.browseClass(typeName, polymorphic); for (Document vertex : iterable) { QueryTemplateManagement queryTemplateManagement = new QueryTemplateManagement(); queryTemplateManagement.setElement((Vertex) vertex); try { JsonNode jsonObject = queryTemplateManagement.serializeAsJsonNode(); arrayNode.add(jsonObject); } catch (ResourceRegistryException e) { logger.error("Unable to correctly serialize {}. It will be excluded from results. {}", vertex.toString(), DBUtility.SHOULD_NOT_OCCUR_ERROR_MESSAGE); } } try { return objectMapper.writeValueAsString(arrayNode); } catch (JsonProcessingException e) { throw new ResourceRegistryException(e); } } public void setParams(String params) throws ResourceRegistryException { try { if(params!=null && params.compareTo("")!=0) { ObjectMapper objectMapper = new ObjectMapper(); JsonNode jsonNode = objectMapper.readTree(params); setParams(jsonNode); } } catch (ResourceRegistryException e) { throw e; } catch (Exception e) { throw new ResourceRegistryException(e); } } public void setParams(JsonNode params) throws ResourceRegistryException { this.params = params; } /** * Run the query after having replaced query parameter with the provided values * @return the result as JSON string * @throws Exception */ public String run() throws ResourceRegistryException { try { String read = read(); QueryTemplate queryTemplate = ElementMapper.unmarshal(QueryTemplate.class, read); JsonNode query = null; if(params!=null) { query = queryTemplate.getJsonQuery(params); }else { query = queryTemplate.getJsonQuery(); } JsonQuery jsonQuery = new JsonQuery(); jsonQuery.setJsonQuery(query); return jsonQuery.query(); } catch (ResourceRegistryException e) { throw e; } catch (Exception e) { throw new ResourceRegistryException(e); } } public void sanityCheck() throws SchemaViolationException, ResourceRegistryException { // No sanity check required } }