diff --git a/phis2-ws/src/main/java/opensilex/service/dao/RadiometricTargetDAO.java b/phis2-ws/src/main/java/opensilex/service/dao/RadiometricTargetDAO.java index 0350b31c9..9e08edb05 100644 --- a/phis2-ws/src/main/java/opensilex/service/dao/RadiometricTargetDAO.java +++ b/phis2-ws/src/main/java/opensilex/service/dao/RadiometricTargetDAO.java @@ -12,9 +12,14 @@ import java.util.List; import opensilex.service.dao.exception.DAODataErrorAggregateException; import opensilex.service.dao.exception.DAOPersistenceException; + +import opensilex.service.model.User; +import org.apache.jena.arq.querybuilder.SelectBuilder; import org.apache.jena.arq.querybuilder.UpdateBuilder; import org.apache.jena.graph.Node; import org.apache.jena.graph.NodeFactory; +import org.apache.jena.query.Query; +import org.apache.jena.query.SortCondition; import org.apache.jena.rdf.model.Literal; import org.apache.jena.rdf.model.Resource; import org.apache.jena.rdf.model.ResourceFactory; @@ -50,14 +55,18 @@ */ public class RadiometricTargetDAO extends Rdf4jDAO { final static Logger LOGGER = LoggerFactory.getLogger(RadiometricTargetDAO.class); - + // This attribute is used to search all properties of the given uri public String uri; - + //The following params are used to search in the triplestore public String rdfType; public String label; - + + public RadiometricTargetDAO(User user) { super(user); } + + public RadiometricTargetDAO() { super(); } + /** * Generates the query to get the uri and the label of the radiometric targets * @example @@ -70,7 +79,7 @@ public class RadiometricTargetDAO extends Rdf4jDAO { protected SPARQLQueryBuilder prepareSearchQuery() { SPARQLQueryBuilder query = new SPARQLQueryBuilder(); query.appendDistinct(Boolean.TRUE); - + String select; if (uri != null) { select = "<" + uri + ">"; @@ -78,21 +87,21 @@ protected SPARQLQueryBuilder prepareSearchQuery() { select = "?" + URI; query.appendSelect(select); } - + query.appendSelect("?" + LABEL); query.appendTriplet(select, Rdf.RELATION_TYPE.toString(), Oeso.CONCEPT_RADIOMETRIC_TARGET.toString(), null); query.appendTriplet(select, Rdfs.RELATION_LABEL.toString(), "?" + LABEL, null); if (label != null) { query.appendFilter("REGEX ( ?" + LABEL + ",\".*" + label + ".*\",\"i\")"); } - + query.appendLimit(this.getPageSize()); query.appendOffset(this.getPage() * this.getPageSize()); - + LOGGER.debug(SPARQL_QUERY + query.toString()); return query; } - + /** * Gets a radiometric target from a given binding set. * Assume that the following attributes exist : URI, label. @@ -101,18 +110,18 @@ protected SPARQLQueryBuilder prepareSearchQuery() { */ private RadiometricTarget getFromBindingSet(BindingSet bindingSet) { RadiometricTarget radiometricTarget = new RadiometricTarget(); - + if (uri != null) { radiometricTarget.setUri(uri); } else { radiometricTarget.setUri(bindingSet.getValue(URI).stringValue()); } - + radiometricTarget.setLabel(bindingSet.getValue(LABEL).stringValue()); - + return radiometricTarget; } - + /** * Gets the radiometric targets (URI, label) of the triplestore. * @return the list of the radiometric target found @@ -124,7 +133,7 @@ public ArrayList allPaginate() { try (TupleQueryResult result = tupleQuery.evaluate()) { radiometricTargets = new ArrayList<>(); - + while (result.hasNext()) { BindingSet bindingSet = result.next(); RadiometricTarget radiometricTarget = getFromBindingSet(bindingSet); @@ -137,7 +146,7 @@ public ArrayList allPaginate() { /** * Counts query generated by the searched parameters : URI, rdfType, * label, brand, variable, inServiceDate, dateOfPurchase, dateOfLastCalibration - * @example + * @example * SELECT DISTINCT (count(distinct ?uri) as ?count) * WHERE { * ?uri rdf:type . @@ -155,7 +164,7 @@ private SPARQLQueryBuilder prepareCount() { LOGGER.debug(SPARQL_QUERY + " " + query.toString()); return query; } - + /** * Counts the number of sensors by the given searched parameters. * @return The number of sensors @@ -173,7 +182,7 @@ public Integer count() throws RepositoryException, MalformedQueryException, Quer } return count; } - + /** * Check the given list of radiometric targets (check properties domain, etc.) * @param radiometricTargets @@ -186,7 +195,7 @@ public POSTResultsReturn check(List radiometricTargets) throw //list of the returned status List status = new ArrayList<>(); boolean validData = true; - + //1. check if the user is an administrator UserDAO userDAO = new UserDAO(); if (userDAO.isAdmin(user)) { @@ -196,15 +205,15 @@ public POSTResultsReturn check(List radiometricTargets) throw if (radiometricTarget.getUri() != null) { uri = radiometricTarget.getUri(); ArrayList radiometricTargetCorresponding = allPaginate(); - + //Unknown radiometric target uri if (radiometricTargetCorresponding.isEmpty()) { validData = false; - status.add(new Status(StatusCodeMsg.UNKNOWN_URI, StatusCodeMsg.ERR, - "Unknown radiometric target uri " + radiometricTarget.getUri())); + status.add(new Status(StatusCodeMsg.UNKNOWN_URI, StatusCodeMsg.ERR, + "Unknown radiometric target uri " + radiometricTarget.getUri())); } } - + //2. check properties for (Property property : radiometricTarget.getProperties()) { //2.1 check if the property exist @@ -212,18 +221,18 @@ public POSTResultsReturn check(List radiometricTargets) throw //2.2 check the domain of the property if (!propertyDAO.isRelationDomainCompatibleWithRdfType(property.getRelation(), Oeso.CONCEPT_RADIOMETRIC_TARGET.toString())) { validData = false; - status.add(new Status(StatusCodeMsg.DATA_ERROR, StatusCodeMsg.ERR, - "the type of the given uri is not in the domain of the relation " + property.getRelation())); + status.add(new Status(StatusCodeMsg.DATA_ERROR, StatusCodeMsg.ERR, + "the type of the given uri is not in the domain of the relation " + property.getRelation())); } } else { validData = false; - status.add(new Status(StatusCodeMsg.DATA_ERROR, StatusCodeMsg.ERR, - StatusCodeMsg.UNKNOWN_URI + " " + property.getRelation())); + status.add(new Status(StatusCodeMsg.DATA_ERROR, StatusCodeMsg.ERR, + StatusCodeMsg.UNKNOWN_URI + " " + property.getRelation())); } //SILEX:todo //add the check range and the cardinality check //\SILEX:todo - + //SILEX:todo //add the check buisiness rules (e.g. the size property depends on the shape type) //\SILEX:todo @@ -233,19 +242,19 @@ public POSTResultsReturn check(List radiometricTargets) throw validData = false; status.add(new Status(StatusCodeMsg.ACCESS_DENIED, StatusCodeMsg.ERR, StatusCodeMsg.ADMINISTRATOR_ONLY)); } - + checkResult = new POSTResultsReturn(validData, null, validData); checkResult.statusList = status; - return checkResult; + return checkResult; } - + /** * Generates an insert query for the given radiometric target. * @param radiometricTarget * @return the query * @example * INSERT DATA { - * GRAPH { + * GRAPH { * . * "rt1" . * < http://www.opensilex.org/vocabulary/oeso#hasShape> "3" . @@ -254,18 +263,18 @@ public POSTResultsReturn check(List radiometricTargets) throw */ private UpdateRequest prepareInsertQuery(RadiometricTarget radiometricTarget) { UpdateBuilder spql = new UpdateBuilder(); - + Node graph = NodeFactory.createURI(Contexts.RADIOMETRIC_TARGETS.toString()); Resource radiometricTargetUri = ResourceFactory.createResource(radiometricTarget.getUri()); Node radiometricTargetConcept = NodeFactory.createURI(Oeso.CONCEPT_RADIOMETRIC_TARGET.toString()); - + spql.addInsert(graph, radiometricTargetUri, RDF.type, radiometricTargetConcept); spql.addInsert(graph, radiometricTargetUri, RDFS.label, radiometricTarget.getLabel()); - + for (Property property : radiometricTarget.getProperties()) { if (property.getValue() != null) { org.apache.jena.rdf.model.Property propertyRelation = ResourceFactory.createProperty(property.getRelation()); - + if (property.getRdfType() != null) { Node propertyValue = NodeFactory.createURI(property.getValue()); spql.addInsert(graph, radiometricTargetUri, propertyRelation, propertyValue); @@ -276,17 +285,17 @@ private UpdateRequest prepareInsertQuery(RadiometricTarget radiometricTarget) { } } } - + UpdateRequest query = spql.buildRequest(); LOGGER.debug(SPARQL_QUERY + " " + query.toString()); - + return query; } - + /** * Inserts the given radiometric targets in the triplestore. * /!\ Prerequisite : data must have been checked before calling this method - * @see RadiometricTargetDAO#check(java.util.List) + * @see RadiometricTargetDAO#check(java.util.List) * @param radiometricTargets * @return the insertion result, with the errors list or the URI of the * radiometric targets inserted @@ -294,11 +303,11 @@ private UpdateRequest prepareInsertQuery(RadiometricTarget radiometricTarget) { private POSTResultsReturn insert(List radiometricTargets) { List status = new ArrayList<>(); List createdResourcesUris = new ArrayList<>(); - + POSTResultsReturn results; boolean resultState = false; boolean insert = true; - + getConnection().begin(); for (RadiometricTarget radiometricTarget : radiometricTargets) { try { @@ -309,32 +318,32 @@ private POSTResultsReturn insert(List radiometricTargets) { } //Insert radiometric target UpdateRequest query = prepareInsertQuery(radiometricTarget); - + try { Update prepareUpdate = getConnection().prepareUpdate(QueryLanguage.SPARQL, query.toString()); prepareUpdate.execute(); createdResourcesUris.add(radiometricTarget.getUri()); } catch (RepositoryException ex) { - LOGGER.error("Error during commit or rolleback Triplestore statements: ", ex); + LOGGER.error("Error during commit or rolleback Triplestore statements: ", ex); } catch (MalformedQueryException e) { - LOGGER.error(e.getMessage(), e); - insert = false; - status.add(new Status(StatusCodeMsg.QUERY_ERROR, StatusCodeMsg.ERR, StatusCodeMsg.MALFORMED_CREATE_QUERY + " " + e.getMessage())); + LOGGER.error(e.getMessage(), e); + insert = false; + status.add(new Status(StatusCodeMsg.QUERY_ERROR, StatusCodeMsg.ERR, StatusCodeMsg.MALFORMED_CREATE_QUERY + " " + e.getMessage())); } } - + if (insert) { resultState = true; getConnection().commit(); } else { getConnection().rollback(); } - + if (getConnection() != null) { getConnection().close(); } - + results = new POSTResultsReturn(resultState, insert, true); results.statusList = status; results.setCreatedResources(createdResourcesUris); @@ -342,10 +351,10 @@ private POSTResultsReturn insert(List radiometricTargets) { results.createdResources = createdResourcesUris; results.statusList.add(new Status(StatusCodeMsg.RESOURCES_CREATED, StatusCodeMsg.INFO, createdResourcesUris.size() + " " + StatusCodeMsg.RESOURCES_CREATED)); } - + return results; } - + /** * Checks and inserts the given radiometric targets in the triplestore. * @param radiometricTargets @@ -361,7 +370,7 @@ public POSTResultsReturn checkAndInsert(List radiometricTarge return checkResult; } } - + /** * Deletes the data about the given radiometric target. * Delete all the occurrences of each relation of the properties of the radiometric target. @@ -388,16 +397,16 @@ public POSTResultsReturn checkAndInsert(List radiometricTarge */ private UpdateRequest prepareDeleteQuery(RadiometricTarget radiometricTarget) { UpdateBuilder spql = new UpdateBuilder(); - + Node graph = NodeFactory.createURI(Contexts.RADIOMETRIC_TARGETS.toString()); Resource radiometricTargetUri = ResourceFactory.createResource(radiometricTarget.getUri()); - + spql.addDelete(graph, radiometricTargetUri, RDFS.label, radiometricTarget.getLabel()); - + for (Property property : radiometricTarget.getProperties()) { if (property.getValue() != null) { org.apache.jena.rdf.model.Property propertyRelation = ResourceFactory.createProperty(property.getRelation()); - + if (property.getRdfType() != null) { Node propertyValue = NodeFactory.createURI(property.getValue()); spql.addDelete(graph, radiometricTargetUri, propertyRelation, propertyValue); @@ -408,16 +417,16 @@ private UpdateRequest prepareDeleteQuery(RadiometricTarget radiometricTarget) { } } } - + UpdateRequest request = spql.buildRequest(); LOGGER.debug(request.toString()); - + return request; } - + /** * Gets the radiometric target information using it's URI./!\ Prerequisite : the URI must have been checked and it must exist - before calling this method. + before calling this method. * @param radiometricTargetUri * @return the Radiometric Target information * @throws opensilex.service.dao.exception.DAOPersistenceException @@ -428,7 +437,7 @@ public RadiometricTarget getRadiometricTarget(String radiometricTargetUri) throw propertyDAO.getAllPropertiesWithLabels(radiometricTarget, null); return radiometricTarget; } - + /** * Compares the new radiometric target given and the old one. * Check if some properties are missing in the new radiometric target. @@ -442,11 +451,11 @@ public RadiometricTarget getRadiometricTarget(String radiometricTargetUri) throw private List compareNewAndOldRadiometricTarget(RadiometricTarget newRadiometricTargetData, RadiometricTarget oldRadiometricTargetData) { //Check each new radiometric target property to see if it has the same number or more occurences than the oldRadiometric. ArrayList status = new ArrayList<>(); - + //1. Count the number of occurences of each property in each radiometric target HashMap newOccurrences = new HashMap<>(); HashMap oldOccurrences = new HashMap<>(); - + for (Property property : newRadiometricTargetData.getProperties()) { if (newOccurrences.containsKey(property.getRelation())) { Integer nbOccurrences = newOccurrences.get(property.getRelation()) + 1; @@ -455,7 +464,7 @@ private List compareNewAndOldRadiometricTarget(RadiometricTarget newRadi newOccurrences.put(property.getRelation(), 1); } } - + for (Property property : oldRadiometricTargetData.getProperties()) { if (oldOccurrences.containsKey(property.getRelation())) { Integer nbOccurrences = oldOccurrences.get(property.getRelation()) + 1; @@ -464,20 +473,20 @@ private List compareNewAndOldRadiometricTarget(RadiometricTarget newRadi oldOccurrences.put(property.getRelation(), 1); } } - + //2. Compare numbers newOccurrences.forEach((relation, nbOccurrences) -> { //If the oldOccurrences contains the relation and has more occurrences //of its relation than the newOccurrences, something is wrong if (oldOccurrences.containsKey(relation) && oldOccurrences.get(relation) > nbOccurrences) { - status.add(new Status(StatusCodeMsg.DATA_ERROR, StatusCodeMsg.ERR, "Ambiguity on the values for the property : " + relation + status.add(new Status(StatusCodeMsg.DATA_ERROR, StatusCodeMsg.ERR, "Ambiguity on the values for the property : " + relation + ". There are less occcurrences of the property in the updated data than before (" + nbOccurrences + " instead of " + oldOccurrences.get(relation) + ")")); } }); - + return status; } - + /** * Updates the given radiometric targets in the triplestore. * /!\ Prerequisite : data must have been checked before calling this method. @@ -485,7 +494,7 @@ private List compareNewAndOldRadiometricTarget(RadiometricTarget newRadi * @param radiometricTargets the list of the radiometric targets to update * @return the update result with the list of all the updated radiometric targets. */ - private POSTResultsReturn updateAndReturnPOSTResultsReturn(List radiometricTargets) + private POSTResultsReturn updateAndReturnPOSTResultsReturn(List radiometricTargets) throws DAOPersistenceException { //SILEX:info //If a property of a radiometric target has a null value, @@ -494,10 +503,10 @@ private POSTResultsReturn updateAndReturnPOSTResultsReturn(List updateStatus = new ArrayList<>(); List updatedResourcesUri = new ArrayList<>(); POSTResultsReturn results; - + boolean annotationUpdate = true; boolean resultState = false; - + getConnection().begin(); for (RadiometricTarget radiometricTarget : radiometricTargets) { //1. get the old radiometric target data @@ -522,7 +531,7 @@ private POSTResultsReturn updateAndReturnPOSTResultsReturn(List radiometricTarge return checkResult; } } - + /** - * Prepares a query to get the higher id of the radiometric targets. - * @example - * SELECT ?uri WHERE { - * ?uri . + * Prepares a query to get the highest id of the radiometric targets. + * @example + * + *
+     * PREFIX rdf: 
+     * PREFIX oeso: 
+     *
+     * SELECT ?uri FROM {  } WHERE {
+     *      ?uri  rdf:type oeso:RadiometricTarget .     
      * }
      * ORDER BY DESC(?uri) 
+     *
+     * 
+ * @param graph : the graph context of the radiometric target set * @return the query */ - private SPARQLQueryBuilder prepareGetLastId() { - SPARQLQueryBuilder query = new SPARQLQueryBuilder(); - - query.appendSelect("?" + URI); - query.appendTriplet("?" + URI, Rdf.RELATION_TYPE.toString(), Oeso.CONCEPT_RADIOMETRIC_TARGET.toString(), null); - query.appendOrderBy("DESC(?" + URI + ")"); - query.appendLimit(1); - - LOGGER.debug(SPARQL_QUERY + query.toString()); - - return query; + private SelectBuilder prepareGetLastId(String graph) { + + Node uriVar = NodeFactory.createVariable(URI); + Node radiometricTargetConcept = NodeFactory.createURI(Oeso.CONCEPT_RADIOMETRIC_TARGET.toString()); + Node graphNode = NodeFactory.createURI(graph); + + SelectBuilder queryBuilder = new SelectBuilder() + .addGraph(graphNode, uriVar, RDF.type, radiometricTargetConcept) + .addOrderBy(new SortCondition(uriVar, Query.ORDER_DESCENDING)) + .setLimit(1); + + LOGGER.debug(SPARQL_QUERY + queryBuilder.toString()); + return queryBuilder; } - + /** * Gets the higher id of the radiometric targets. + * @param graph : uri of the context graph * @return the id */ - public int getLastId() { - SPARQLQueryBuilder query = prepareGetLastId(); - //get last unit uri inserted + public int getLastId(String graph) { + + SelectBuilder query = prepareGetLastId(graph); + TupleQuery tupleQuery = this.getConnection().prepareTupleQuery(QueryLanguage.SPARQL, query.toString()); TupleQueryResult result = tupleQuery.evaluate(); - + String uri = null; - + if (result.hasNext()) { BindingSet bindingSet = result.next(); uri = bindingSet.getValue(URI).stringValue(); diff --git a/phis2-ws/src/main/java/opensilex/service/utils/UriGenerator.java b/phis2-ws/src/main/java/opensilex/service/utils/UriGenerator.java index d6f5f5e79..29b427295 100644 --- a/phis2-ws/src/main/java/opensilex/service/utils/UriGenerator.java +++ b/phis2-ws/src/main/java/opensilex/service/utils/UriGenerator.java @@ -62,7 +62,7 @@ * \SILEX:todo * @update [Vincent Migot] 17 July 2019: Add syncronization on public methods to prevent URI duplication */ -public class UriGenerator { +public class UriGenerator { private static final String URI_CODE_ACTUATOR = "a"; private static final String URI_CODE_SCIENTIFIC_OBJECT = "o"; private static final String URI_CODE_IMAGE = "i"; @@ -88,14 +88,14 @@ public class UriGenerator { public static final String PLATFORM_URI_ID_VARIABLES = PLATFORM_URI_ID + "variables/" + URI_CODE_VARIABLE; private static final String PLATFORM_URI_ID_VARIETY = PLATFORM_URI + "v/"; private static final String PLATFORM_URI_ID_PROVENANCE = PLATFORM_URI_ID + "provenance/"; - + private static final String EXPERIMENT_URI_SEPARATOR = "-"; /** * Prevent URI generator to be instanciated */ private UriGenerator() {} - + /** * Generates a new vector URI. a vector URI has the following pattern: * :/ @@ -118,12 +118,12 @@ private static String generateVectorUri(String year) { } return getVectorUriPatternByYear(year) + newVectorNumber; } - + /** * Internal variable to store the last vector ID by year */ private static Map vectorLastIDByYear = new HashMap<>(); - + /** * Return the next vector ID by incrementing vectorLastIDByYear variable and initializing it before if needed * @return next vector ID @@ -133,7 +133,7 @@ private static int getNextVectorID(String year) { VectorDAO vectorDAO = new VectorDAO(); vectorLastIDByYear.put(year, vectorDAO.getLastIdFromYear(year)); } - + int vectorLastID = vectorLastIDByYear.get(year); vectorLastID++; vectorLastIDByYear.put(year, vectorLastID); @@ -148,7 +148,7 @@ private static int getNextVectorID(String year) { public static String getVectorUriPatternByYear(String year) { return PLATFORM_URI + year + "/" + URI_CODE_VECTOR + year.substring(2, 4); } - + /** * Generates a new sensor URI. A sensor URI has the following pattern: * :/ @@ -176,12 +176,12 @@ private static String generateSensorUri(String year) { } return getSensorUriPatternByYear(year) + newSensorNumber; } - + /** * Internal variable to store the last sensor ID by year */ private static Map sensorLastIDByYear = new HashMap<>(); - + /** * Return the next sensor ID by incrementing sensorLastIDByYear variable and initializing it before if needed * @return next sensor ID @@ -191,13 +191,13 @@ private static int getNextSensorID(String year) { SensorDAO sensorDAO = new SensorDAO(); sensorLastIDByYear.put(year, sensorDAO.getLastIdFromYear(year)); } - + int sensorLastID = sensorLastIDByYear.get(year); sensorLastID++; sensorLastIDByYear.put(year, sensorLastID); return sensorLastID; } - + /** * Return sensor uri pattern :/ * @param year @@ -206,7 +206,7 @@ private static int getNextSensorID(String year) { public static String getSensorUriPatternByYear(String year) { return PLATFORM_URI + year + "/" + URI_CODE_SENSOR + year.substring(2, 4); } - + /** * Generate a new actuator URI. A actuator URI has the following pattern: * :/ @@ -232,14 +232,14 @@ private static String generateActuatorUri(String year) { newActuatorNumber = numberOfActuators; break; } - return getActuatorUriPatternByYear(year) + newActuatorNumber; + return getActuatorUriPatternByYear(year) + newActuatorNumber; } /** * Internal variable to store the last actuator ID by year */ private static Map actuatorLastIDByYear = new HashMap<>(); - + /** * Return the next actuator ID by incrementing actuatorLastIDByYear variable and initializing it before if needed * @return next actuator ID @@ -249,13 +249,13 @@ private static int getNextActuatorID(String year) { ActuatorDAO actuatorDAO = new ActuatorDAO(); actuatorLastIDByYear.put(year, actuatorDAO.getLastIdFromYear(year)); } - + int actuatorLastID = actuatorLastIDByYear.get(year); actuatorLastID++; actuatorLastIDByYear.put(year, actuatorLastID); return actuatorLastID; } - + /** * Return actuator uri pattern :/ * @param year @@ -281,15 +281,15 @@ private static String generateScientificObjectUri(String year) { while (agronomicalObjectId.length() < 6) { agronomicalObjectId = "0" + agronomicalObjectId; } - - return getScientificObjectUriPatternByYear(year) + agronomicalObjectId; + + return getScientificObjectUriPatternByYear(year) + agronomicalObjectId; } /** * Internal variable to store the last scientifc object ID by year */ private static Map scientificObjectLastIDByYear = new HashMap<>(); - + /** * Return the next scientifc object ID by incrementing scientificObjectLastIDByYear variable and initializing it before if needed * @return next scientific object ID @@ -299,13 +299,13 @@ private static int getNextScientificObjectID(String year) { ScientificObjectRdf4jDAO scientificObjectDAO = new ScientificObjectRdf4jDAO(); scientificObjectLastIDByYear.put(year, scientificObjectDAO.getLastScientificObjectIdFromYear(year)); } - + int scientificObjectLastID = scientificObjectLastIDByYear.get(year); scientificObjectLastID++; scientificObjectLastIDByYear.put(year, scientificObjectLastID); return scientificObjectLastID; } - + /** * Return scientific object uri pattern :/ * @param year @@ -324,7 +324,7 @@ public static String getScientificObjectUriPatternByYear(String year) { */ private static String generateVariableUri() { // Generate variable URI based on next id - String variableId = Integer.toString(getNextVariableID()); + String variableId = Integer.toString(getNextVariableID()); while (variableId.length() < 3) { variableId = "0" + variableId; @@ -332,12 +332,12 @@ private static String generateVariableUri() { return PLATFORM_URI_ID_VARIABLES + variableId; } - + /** * Internal variable to store the last variable ID */ private static Integer variableLastID; - + /** * Return the next variable ID by incrementing variableLastID variable and initializing it before if needed * @return next variable ID @@ -347,9 +347,9 @@ private static int getNextVariableID() { VariableDAO variableDAO = new VariableDAO(); variableLastID = variableDAO.getLastId(); } - + variableLastID++; - + return variableLastID; } @@ -370,7 +370,7 @@ private static String generateTraitUri() { return PLATFORM_URI_ID_TRAITS + traitId; } - + /** * Internal variable to store the last trait ID */ @@ -385,9 +385,9 @@ private static int getNextTraitID() { TraitDAO traitDAO = new TraitDAO(); traitLastID = traitDAO.getLastId(); } - + traitLastID++; - + return traitLastID; } @@ -408,7 +408,7 @@ private static String generateMethodUri() { return PLATFORM_URI_ID_METHOD + methodId; } - + /** * Internal variable to store the last method ID */ @@ -423,9 +423,9 @@ private static int getNextMethodID() { MethodDAO methodDAO = new MethodDAO(); methodLastID = methodDAO.getLastId(); } - + methodLastID++; - + return methodLastID; } @@ -451,7 +451,7 @@ private static String generateUnitUri() { * Internal variable to store the last unit ID */ private static Integer unitLastID; - + /** * Return the next unit ID by incrementing unitLastID variable and initializing it before if needed * @return next unit ID @@ -461,12 +461,17 @@ private static int getNextUnitID() { UnitDAO unitDAO = new UnitDAO(); unitLastID = unitDAO.getLastId(); } - + unitLastID++; - + return unitLastID; } - + + /** + * Association between a platform graph context and the last radiometricTarget generated id into this graph. + */ + private static Map radiometricTargetLastIdByPlatform = new HashMap<>(); + /** * Generates a new radiometric target URI. A radiometric target URI follows the pattern: * :id/radiometricTargets/ @@ -475,19 +480,23 @@ private static int getNextUnitID() { * @return The new radiometric target URI */ private static String generateRadiometricTargetUri() { - //1. Get the highest radiometric target id (i.e. the last inserted - //radiometric target) - RadiometricTargetDAO radiometricTargetDAO = new RadiometricTargetDAO(); - int lastID = radiometricTargetDAO.getLastId(); - + + //1. Get the highest radiometric target id (i.e. the last inserted radiometric target) + String platformGraph = Contexts.RADIOMETRIC_TARGETS.toString(); + if(! radiometricTargetLastIdByPlatform.containsKey(platformGraph)) { + RadiometricTargetDAO radiometricTargetDAO = new RadiometricTargetDAO(); + radiometricTargetLastIdByPlatform.put(platformGraph,radiometricTargetDAO.getLastId(platformGraph)); + } + //2. Generate radiometric target URI - int newRadiometricTargetID = lastID + 1; - String radiometricTargetID = Integer.toString(newRadiometricTargetID); - + int radiometricTargetLastID = radiometricTargetLastIdByPlatform.get(platformGraph)+1; + String radiometricTargetID = Integer.toString(radiometricTargetLastID); while (radiometricTargetID.length() < 3) { radiometricTargetID = "0" + radiometricTargetID; } - + + // update the platform last radiometricId + radiometricTargetLastIdByPlatform.put(platformGraph,radiometricTargetLastID); return PLATFORM_URI_ID_RADIOMETRIC_TARGET + URI_CODE_RADIOMETRIC_TARGET + radiometricTargetID; } @@ -518,55 +527,34 @@ private static String generateAgentUri(String agentSuffixe) { /** * Generates a new annotation URI. A unit annotation follows the pattern: * :id/annotation/ - * = 1 letter type + java.util.UUID.randomUUID(); - * @example http://www.phenome-fppn.fr/diaphen/id/annotation/e073961b-e766-4493-b98f-74a8b2846893 + * = 1 letter type + {@link UUID#randomUUID()}+_+{@link System#nanoTime()} + * @example http://www.phenome-fppn.fr/diaphen/id/annotation/e073961b-e766-4493-b98f-74a8b2846893_295073540722571 * @return the new annotation URI */ private static String generateAnnotationUri() { - //1. check if URI already exists - AnnotationDAO annotationDao = new AnnotationDAO(); - String newAnnotationUri = PLATFORM_URI_ID_ANNOTATION + UUID.randomUUID(); - while (annotationDao.existUri(newAnnotationUri)) { - newAnnotationUri = PLATFORM_URI_ID_ANNOTATION + UUID.randomUUID(); - } - - return newAnnotationUri; + return PLATFORM_URI_ID_ANNOTATION +UUID.randomUUID() +"_" +System.nanoTime(); } /** * Generates a new event URI. an event URI follows the pattern: * :id/event/ - * = java.util.UUID.randomUUID(); - * @example http://www.phenome-fppn.fr/diaphen/id/event/e073961b-e766-4493-b98f-74a8b2846893 + * = {@link UUID#randomUUID()}+_+{@link System#nanoTime()} + * @example http://www.phenome-fppn.fr/diaphen/id/event/e073961b-e766-4493-b98f-74a8b2846893_295073540722571 * @return the new event URI */ private static String generateEventUri() { - // To check if URI already exists - EventDAO eventDao = new EventDAO(null); - String newEventUri = PLATFORM_URI_ID_EVENT + UUID.randomUUID(); - while (eventDao.existUri(newEventUri)) { - newEventUri = PLATFORM_URI_ID_EVENT + UUID.randomUUID(); - } - - return newEventUri; + return PLATFORM_URI_ID_EVENT +UUID.randomUUID() + "_" +System.nanoTime(); } /** * Generates a new Instant URI. The URI follows the pattern: * :id/instant/ - * = java.util.UUID.randomUUID(); - * @example http://www.phenome-fppn.fr/diaphen/id/instant/e073961b-e766-4493-b98f-74a8b2846893 + * = {@link UUID#randomUUID()}+_+{@link System#nanoTime()}; + * @example http://www.phenome-fppn.fr/diaphen/id/instant/e073961b-e766-4493-b98f-74a8b2846893_295073540722571 * @return the new URI */ private static String generateInstantUri() { - // To check if the URI already exists - EventDAO timeDao = new EventDAO(null); - String newInstantUri = PLATFORM_URI_ID_INSTANT + UUID.randomUUID(); - while (timeDao.existUri(newInstantUri)) { - newInstantUri = PLATFORM_URI_ID_INSTANT + UUID.randomUUID(); - } - - return newInstantUri; + return PLATFORM_URI_ID_INSTANT +UUID.randomUUID() +"_" +System.nanoTime(); } /** @@ -606,7 +594,7 @@ private static String generateImageUri(String year, String lastGeneratedUri) { return PLATFORM_URI + year + nbImagesByYear; } } - + /** * Generates a new project URI. A project URI follows the pattern: * : @@ -622,35 +610,41 @@ private static String generateProjectUri(String projectAcronyme) throws Exceptio if (projectDAO.existUri(projectUri)) { throw new AlreadyExists("The project uri " + projectUri + " already exist in the triplestore."); } - + return projectUri; } - + + /** + * Association between an experiment campaign year and the last id generated during this year + */ + private static Map lastIdByCampaigns = new HashMap<>(); + /** * Generates a new experiment URI. An experiment URI follows this pattern: * : * = infrastructure code + 4 digits year + auto increment digit (per year) * @example http://www.opensilex.org/demo/DMO2019-1 - * @param campaign the year of the campaign of the experiment + * @param campaignYear : the year of the campaign of the experiment * @return the new URI */ - private static String generateExperimentUri(String campaign) { - //1. Get the campaign last experiment URI - Integer campaignLastExperimentUri = (new ExperimentSQLDAO()).getCampaignLastExperimentUri(campaign); - //2. Generate the URI of the experiment - - Integer newExperimentNumber = campaignLastExperimentUri + 1; - - return PLATFORM_URI + PLATFORM_CODE + campaign + EXPERIMENT_URI_SEPARATOR + newExperimentNumber; + private static String generateExperimentUri(String campaignYear) { + + // check if the campaign year is already registered in the local map + if(! lastIdByCampaigns.containsKey(campaignYear)) { + lastIdByCampaigns.put(campaignYear, new ExperimentSQLDAO().getCampaignLastExperimentUri(campaignYear)); + } + Integer newExperimentNumber = lastIdByCampaigns.get(campaignYear) + 1; + lastIdByCampaigns.put(campaignYear, newExperimentNumber); + return PLATFORM_URI + PLATFORM_CODE + campaignYear + EXPERIMENT_URI_SEPARATOR + newExperimentNumber; } - + /** * Generate a new group URI. A group URI follows the pattern: * : * @example http://www.opensilex.org/demo/INRA-MISTEA.GAMMA * @param name the group name * @return the new generated URI - * @throws Exception + * @throws Exception */ private static String generateGroupUri(String name) throws Exception { //1. Generate URI @@ -658,20 +652,20 @@ private static String generateGroupUri(String name) throws Exception { //2. Check if the generated URI already exists GroupDAO groupDao = new GroupDAO(); Group group = new Group(groupUri); - + if (groupDao.existInDB(group)) { throw new AlreadyExists("The group uri " + groupUri + " already exist in the triplestore."); } - + return groupUri; } - + /** * Generates a new provenance URI. A provenance URI follows the pattern : * :id/provenance/ * @example http://www.opensilex.org/demo/id/provenance/019275849 * @return the new generated uri - * @throws Exception + * @throws Exception */ private static String generateProvenanceUri() { //Generates uri @@ -686,44 +680,44 @@ private static String generateProvenanceUri() { * @example http://www.opensilex.org/id/data/1e9eb2fbacc7222d3868ae96149a8a16b32b2a1870c67d753376381ebcbb5937e78da502ee3f42d3828eaa8cab237f93 * @param additionalInformation the key of the data * @return the new generated uri - * @throws Exception + * @throws Exception */ private static String generateDataUri(String additionalInformation) throws Exception { // Define data URI with key hash and random id to prevent collision String uri = Contexts.PLATFORM.toString() + "id/data/" + getUniqueHash(additionalInformation); - + return uri; } - + /** * Generates a new data file URI. * @example http://www.opensilex.org/id/dataFile/1e9eb2fbacc7222d3868ae96149a8a16b32b2a1870c67d753376381ebcbb5937e78da502ee3f42d3828eaa8cab237f93 * @param additionalInformation the key of the data file * @return the new generated uri - * @throws Exception + * @throws Exception */ private static String generateDataFileUri(String collection, String key) throws Exception { // Define data URI with key hash and random id to prevent collision String uri = Contexts.PLATFORM.toString() + "id/dataFile/" + collection + "/" + getUniqueHash(key); - + return uri; } - + private static String getUniqueHash(String key) throws NoSuchAlgorithmException { // Generate SHA-256 hash MessageDigest digest = MessageDigest.getInstance("SHA-256"); byte[] encodedhash = digest.digest(key.getBytes(StandardCharsets.UTF_8)); - + // Convert hash to base32 string in lower case string and remove = padding sign Base32 base32 = new Base32(); String encodedString = base32.encodeAsString(encodedhash).replaceAll("=", "").toLowerCase(); - + // Generate UUID without '-' sign String randomId = UUID.randomUUID().toString().replaceAll("-", ""); - + return encodedString + randomId; } - + /** * Generates scientific objects uris for a year. The number depends on the given numberOfUrisToGenerate. * @param year @@ -736,11 +730,11 @@ public synchronized static List generateScientificObjectUris(String year } List scientificObjectUris = new ArrayList<>(); - + for (int i = 0; i < numberOfUrisToGenerate; i++) { scientificObjectUris.add(generateScientificObjectUri(year)); } - + return scientificObjectUris; } @@ -756,7 +750,7 @@ public synchronized static List generateScientificObjectUris(String year * @return the generated URI * @throws java.lang.Exception */ - public synchronized static String generateNewInstanceUri(String instanceType, String year, String additionalInformation) + public synchronized static String generateNewInstanceUri(String instanceType, String year, String additionalInformation) throws Exception { if (year == null) { year = Integer.toString(Calendar.getInstance().get(Calendar.YEAR)); @@ -782,7 +776,7 @@ public synchronized static String generateNewInstanceUri(String instanceType, St return generateVarietyUri(additionalInformation); } else if (uriDao.isSubClassOf(instanceType, Oeso.CONCEPT_IMAGE.toString())) { return generateImageUri(year, additionalInformation); - } else if (instanceType.equals(Foaf.CONCEPT_AGENT.toString()) + } else if (instanceType.equals(Foaf.CONCEPT_AGENT.toString()) || uriDao.isSubClassOf(instanceType, Foaf.CONCEPT_AGENT.toString())) { return generateAgentUri(additionalInformation); } else if (instanceType.equals(Oeso.CONCEPT_ANNOTATION.toString())) { diff --git a/phis2-ws/src/test/java/opensilex/service/dao/manager/Rdf4jDAOTest.java b/phis2-ws/src/test/java/opensilex/service/dao/manager/Rdf4jDAOTest.java index 9d5670ada..acec2b495 100644 --- a/phis2-ws/src/test/java/opensilex/service/dao/manager/Rdf4jDAOTest.java +++ b/phis2-ws/src/test/java/opensilex/service/dao/manager/Rdf4jDAOTest.java @@ -72,7 +72,9 @@ protected static void initDaoWithInMemoryStoreConnection(Rdf4jDAO dao) { @AfterEach public void cleanStore() { - assert(memoryRepository != null); + if(memoryRepository == null) + return; + RepositoryConnection conn = memoryRepository.getConnection(); if(conn != null && conn.isOpen()) { long size = conn.size(); diff --git a/phis2-ws/src/test/java/opensilex/service/utils/UriGeneratorTest.java b/phis2-ws/src/test/java/opensilex/service/utils/UriGeneratorTest.java new file mode 100644 index 000000000..ec941e9bd --- /dev/null +++ b/phis2-ws/src/test/java/opensilex/service/utils/UriGeneratorTest.java @@ -0,0 +1,149 @@ +//****************************************************************************** +// UriGeneratorTest.java +// SILEX-PHIS +// Copyright © INRAE 2019 +// Creation date: 17 December 2019 +// Contact: renaud.colin@inra.fr, anne.tireau@inra.fr, pascal.neveu@inra.fr +//****************************************************************************** + +package opensilex.service.utils; + +import opensilex.service.dao.AnnotationDAO; +import opensilex.service.dao.EventDAO; +import opensilex.service.dao.RadiometricTargetDAO; +import opensilex.service.dao.UserDAO; +import opensilex.service.dao.manager.Rdf4jDAOTest; +import opensilex.service.model.*; +import opensilex.service.ontology.Oa; +import opensilex.service.ontology.Oeev; +import opensilex.service.ontology.Time; +import org.apache.commons.lang3.StringUtils; +import org.joda.time.DateTime; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.util.*; +import java.util.stream.Collectors; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +/** + * This class check that for any object URI generated with a method from {@link UriGenerator} + * the URI(s) are unique and not empty. + * @author renaud.colin@inra.fr + */ + +class UriGeneratorTest extends Rdf4jDAOTest { + + private static String userUri; + private static UserDAO userDao; + + @BeforeAll + public static void setUp() throws Exception { + userDao = new UserDAO(); + userUri = "http://www.opensilex.org/demo/id/agent/admin_phis"; + } + + /** + * Check if all generated URI(s) are unique and non empty + * + * @param uris : a list of URI + * @param objects : a list of Object + */ + protected void checkUris(Set uris, Collection objects){ + + // check that each target uri has been filled + uris.forEach( uri -> assertFalse(StringUtils.isEmpty( uri ) )); + // check that all uris are uniques + // if the size of the collections are not equals, then some URI(s) has not been added to to the URI set + assertEquals(uris.size(),objects.size()); + } + + @Test + public void testRadiometricTargetGenerate() throws Exception { + + RadiometricTargetDAO radioDao = new RadiometricTargetDAO(userDao.findById(userUri)); + initDaoWithInMemoryStoreConnection(radioDao); + + RadiometricTarget t1 = new RadiometricTarget(); + t1.setLabel("rt0"); + + radioDao.checkAndInsert(Arrays.asList(t1)); + assertFalse(StringUtils.isEmpty(t1.getUri())); + + List targets = new ArrayList<>(); + for(int i=1 ;i<5 ; i++){ + RadiometricTarget target = new RadiometricTarget(); + target.setLabel("rt"+i); + targets.add(target); + } + + radioDao.checkAndInsert(targets); + Set uris = targets.stream().map(RdfResourceDefinition::getUri).collect(Collectors.toSet()); + checkUris(uris,targets); + } + + @Test + public void testEventGenerate() throws Exception{ + + Project testProject = createAndGetProject(); + Experiment xp = createAndGetExperiment(testProject); + ScientificObject so = createAndGetScientificObject(xp); + + int nbEvent = 5; + List events = new ArrayList<>(nbEvent); + for (int i = 0; i < nbEvent; i++) { + Event event = new Event(null, Oeev.Event.getURI(),Arrays.asList(so.getUri()),new DateTime(), new ArrayList<>(1),new ArrayList<>(1)); + events.add(event); + } + EventDAO eventDAO = new EventDAO(userDao.findById(userUri)); + eventDAO.create(events); + + Set uris = events.stream().map(RdfResourceDefinition::getUri).collect(Collectors.toSet()); + checkUris(uris,events); + } + + @Test + public void testAnnotationGenerate() throws Exception{ + + Project testProject = createAndGetProject(); + Experiment xp = createAndGetExperiment(testProject); + ScientificObject so = createAndGetScientificObject(xp); + Event event = createAndGetEvent(so.getUri()); + + int nbAnnotation = 5; + List annotations = new ArrayList<>(nbAnnotation); + + ArrayList bodyValues = new ArrayList<>(); + bodyValues.add("annotate an event"); + ArrayList annotationTargetList = new ArrayList<>(); + annotationTargetList.add(event.getUri()); + + for (int i = 0; i < nbAnnotation; i++) { + Annotation annotation = new Annotation(null,DateTime.now(),userUri,bodyValues, Oa.INSTANCE_DESCRIBING.toString(),annotationTargetList); + annotations.add(annotation); + } + AnnotationDAO annotationDAO = new AnnotationDAO(userDao.findById(userUri)); + annotationDAO.create(annotations); + + Set uris = annotations.stream().map(Annotation::getUri).collect(Collectors.toSet()); + checkUris(uris,annotations); + } + + @Test + public void testInstantGenerate() throws Exception{ + + int nbInstant = 5; + + Set uris = new HashSet<>(); + for (int i = 0; i < nbInstant; i++) { + String instantUri = UriGenerator.generateNewInstanceUri(Time.Instant.toString(), null, null); + uris.add(instantUri); + } + + // if the size are not equals, then it's means that there exist one duplicate URI, which is a fail + assertEquals(uris.size(),nbInstant); + } + +}