/******************************************************************************* * Copyright 2006 - 2014 Vienna University of Technology, * Department of Software Technology and Interactive Systems, IFS * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package eu.scape_project.planning.manager; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.annotation.PostConstruct; import javax.ejb.ConcurrencyManagement; import javax.ejb.ConcurrencyManagementType; import javax.ejb.Lock; import javax.ejb.LockType; import javax.ejb.Singleton; import javax.ejb.Startup; import javax.inject.Named; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.hp.hpl.jena.query.Query; import com.hp.hpl.jena.query.QueryExecution; import com.hp.hpl.jena.query.QueryExecutionFactory; import com.hp.hpl.jena.query.QueryFactory; import com.hp.hpl.jena.query.QuerySolution; import com.hp.hpl.jena.query.ResultSet; import com.hp.hpl.jena.query.Syntax; import com.hp.hpl.jena.rdf.model.Model; import com.hp.hpl.jena.rdf.model.ModelFactory; import com.hp.hpl.jena.rdf.model.Resource; import com.hp.hpl.jena.util.FileManager; import eu.scape_project.planning.model.measurement.Attribute; import eu.scape_project.planning.model.measurement.CriterionCategory; import eu.scape_project.planning.model.measurement.EvaluationScope; import eu.scape_project.planning.model.measurement.Measure; import eu.scape_project.planning.model.scales.BooleanScale; import eu.scape_project.planning.model.scales.FloatScale; import eu.scape_project.planning.model.scales.FreeStringScale; import eu.scape_project.planning.model.scales.OrdinalScale; import eu.scape_project.planning.model.scales.PositiveFloatScale; import eu.scape_project.planning.model.scales.PositiveIntegerScale; import eu.scape_project.planning.model.scales.RestrictedScale; import eu.scape_project.planning.model.scales.Scale; /** * For administration of metrics, measurable properties and criteria. This * should be the interface to a Measurement Property Registry (MPR) - the * registry should be queried for all measurement entities - this would prevent * entities being overwritten by accident, and ease notification on changed * entities - changes to already known entities should trigger events for * preservation watch * * @author kraxner */ @Singleton @ConcurrencyManagement(ConcurrencyManagementType.BEAN) @Startup @Named("criteriaManager") public class CriteriaManager implements Serializable { private static final long serialVersionUID = -2305838596050068452L; public static final String MEASURES_FILE = "data/vocabulary/quality_measures.rdf"; public static final String ATTRIBUTES_FILE = "data/vocabulary/quality_attributes.rdf"; public static final String CATEGORIES_FILE = "data/vocabulary/quality_categories.rdf"; private Logger log = LoggerFactory.getLogger(CriteriaManager.class); private Model model; /** * Constructs a new criteria manager. */ public CriteriaManager() { model = ModelFactory.createMemModelMaker().createDefaultModel(); } /** * Cache for looking up CriterionCategories by their URI. */ private Map<String, CriterionCategory> knownCategories = new HashMap<String, CriterionCategory>(); /** * Cache for lookup of all currently known measures by their URI. * */ private Map<String, Measure> knownMeasures = new HashMap<String, Measure>(); /** * Cache for lookup of all currently known attributes by their URI. */ private Map<String, Attribute> knownAttributes = new HashMap<String, Attribute>(); /** * Returns a list of all known categories IMPORTANT: this list can not (and * must not) be altered! * * @return a collection of categories */ public Collection<CriterionCategory> getAllCriterionCategories() { return Collections.unmodifiableCollection(knownCategories.values()); } /** * Returns a list of all known criteria IMPORTANT: this list can not (and * must not) be altered! * * @return a collection of measures */ @Lock(LockType.READ) public Collection<Measure> getAllMeasures() { return Collections.unmodifiableCollection(knownMeasures.values()); } /** * Returns a list of all known properties IMPORTANT: this list can not (and * must not) be altered! * * @return a collection of attributes */ @Lock(LockType.READ) public Collection<Attribute> getAllAttributes() { return Collections.unmodifiableCollection(knownAttributes.values()); } /** * Returns the criterion for the given criterionUri. * * @param measureUri * the URI of the measure * @return the measure or null if no measure could be found */ @Lock(LockType.READ) public Measure getMeasure(String measureUri) { return knownMeasures.get(measureUri); } /** * Returns the attribute for the given attributeUri. * * @param attributeUri * the URI of the attribute * @return the attribute of null if no attribute could be found */ @Lock(LockType.READ) public Attribute getAttribute(String attributeUri) { return knownAttributes.get(attributeUri); } /** * Builds the hierarchy of the measure. * * @param measureUri * the URI of the measure * @return a list of URIs ordered from measure upwards */ @Lock(LockType.READ) public List<String> getCategoryHierachy(String measureUri) { List<String> hierarchy = new ArrayList<String>(); Measure m = knownMeasures.get(measureUri); if (m == null) { return hierarchy; } Attribute a = knownAttributes.get(m.getAttribute().getUri()); if (a == null) { return hierarchy; } hierarchy.add(0, a.getName()); CriterionCategory criterionCategory = a.getCategory(); if (criterionCategory == null) { return hierarchy; } hierarchy.add(0, criterionCategory.getName()); return hierarchy; } /** * measureUri categories from the model. */ private void resolveCriterionCategories() { knownCategories.clear(); // @formatter:off String statement = "SELECT ?c ?cn ?scope WHERE { " + "?c rdf:type quality:CriterionCategory . " + "?c skos:prefLabel ?cn . " + "?c quality:scope ?scope }"; String commonNS = "PREFIX rdf:<http://www.w3.org/1999/02/22-rdf-syntax-ns#> " + "PREFIX rdfs:<http://www.w3.org/2000/01/rdf-schema#> " + "PREFIX skos:<http://www.w3.org/2004/02/skos/core#> " + "PREFIX quality: <http://purl.org/DP/quality#>"; // @formatter:on Query query = QueryFactory.create(commonNS + statement, Syntax.syntaxARQ); QueryExecution qe = QueryExecutionFactory.create(query, model); ResultSet results = qe.execSelect(); while ((results != null) && (results.hasNext())) { QuerySolution qs = results.next(); String categoryId = qs.getResource("c").toString(); String name = qs.getLiteral("cn").getString(); String scopeStr = qs.getResource("scope").getLocalName(); EvaluationScope scope = null; if ("OBJECT".equals(scopeStr)) { scope = EvaluationScope.OBJECT; } else { scope = EvaluationScope.ALTERNATIVE_ACTION; } if (scope != null) { CriterionCategory category = new CriterionCategory(categoryId, name, scope); knownCategories.put(categoryId, category); } else { log.warn("CriterionCategory without defined scope: " + categoryId + ", " + name); } } } /** * Resolves attributes from the model. */ private void resolveAttributes() { knownAttributes.clear(); // @formatter:off String statement = "SELECT ?a ?an ?ad ?ac WHERE { " + "?a rdf:type quality:Attribute . " + "?a skos:prefLabel ?an . " + "?a dct:description ?ad . " + "?a quality:criterionCategory ?ac }"; String commonNS = "PREFIX rdf:<http://www.w3.org/1999/02/22-rdf-syntax-ns#> " + "PREFIX rdfs:<http://www.w3.org/2000/01/rdf-schema#> " + "PREFIX dct:<http://purl.org/dc/terms/> " + "PREFIX skos:<http://www.w3.org/2004/02/skos/core#> " + "PREFIX quality: <http://purl.org/DP/quality#>"; // @formatter:on Query query = QueryFactory.create(commonNS + statement, Syntax.syntaxARQ); QueryExecution qe = QueryExecutionFactory.create(query, model); ResultSet results = qe.execSelect(); while ((results != null) && (results.hasNext())) { QuerySolution qs = results.next(); Attribute a = new Attribute(); a.setDescription(qs.getLiteral("ad").getString()); a.setName(qs.getLiteral("an").getString()); a.setUri(qs.getResource("a").toString()); String categoryUri = qs.getResource("ac").toString(); a.setCategory(knownCategories.get(categoryUri)); knownAttributes.put(a.getUri(), a); } } /** * Resolves measures from the model. */ private void resolveMeasures() { knownMeasures.clear(); // @formatter:off String statement = "SELECT ?m ?mn ?md ?a ?s ?r WHERE { " + "?m rdf:type quality:Measure . " + "?m quality:attribute ?a . " + "?m skos:prefLabel ?mn . " + "?m dct:description ?md . " + "?m quality:scale ?s . " + "optional{?m quality:restriction ?r} }"; String commonNS = "PREFIX rdf:<http://www.w3.org/1999/02/22-rdf-syntax-ns#> " + "PREFIX rdfs:<http://www.w3.org/2000/01/rdf-schema#> " + "PREFIX dct:<http://purl.org/dc/terms/> " + "PREFIX skos:<http://www.w3.org/2004/02/skos/core#> " + "PREFIX quality: <http://purl.org/DP/quality#>"; // @formatter:on Query query = QueryFactory.create(commonNS + statement, Syntax.syntaxARQ); QueryExecution qe = QueryExecutionFactory.create(query, model); ResultSet results = qe.execSelect(); while ((results != null) && (results.hasNext())) { QuerySolution qs = results.next(); Resource attribute = qs.getResource("a"); String attributeUri = attribute.toString(); Measure m = new Measure(); m.setUri(qs.getResource("m").toString()); m.setName(qs.getLiteral("mn").getString()); m.setDescription(qs.getLiteral("md").getString()); Scale s = createScale(qs.getResource("s").getLocalName()); m.setScale(s); if ((s instanceof RestrictedScale) && (qs.contains("r"))) { String restriction = qs.getLiteral("r").getString(); ((RestrictedScale) s).setRestriction(restriction); } Attribute a = knownAttributes.get(attributeUri); m.setAttribute(a); if ((a != null) && (s != null)) { // only add completely defined measures knownMeasures.put(m.getUri(), m); } } } /** * Creates a scale from the provided scale name. * * @param scaleName * the name of the scale * @return the scale or null if no scale could be found */ private Scale createScale(String scaleName) { if ("Boolean".equalsIgnoreCase(scaleName)) { return new BooleanScale(); } else if ("FreeText".equalsIgnoreCase(scaleName)) { return new FreeStringScale(); } else if ("Number".equalsIgnoreCase(scaleName)) { return new FloatScale(); } else if ("PositiveNumber".equalsIgnoreCase(scaleName)) { return new PositiveFloatScale(); } else if ("PositiveInteger".equalsIgnoreCase(scaleName)) { return new PositiveIntegerScale(); } else if ("Ordinal".equalsIgnoreCase(scaleName)) { return new OrdinalScale(); } return null; } /** * FIXME: reload from RDF * * Reads the XML file from {@link #DESCRIPTOR_FILE} and adds the contained * criteria to the database. For criteria that already exist in the database * (as designated by URI), the information is updated. * * @see eu.scape_project.planning.application.ICriteriaManager#reload() * ATTENTION: From all available CRUD operation only Create and Update * are covered. Delete operations are not executed. Thus, if you have * deleted Properties in your XML they are not deleted from the * database. */ @Lock(LockType.WRITE) public void reload() { model = FileManager.get().loadModel(CATEGORIES_FILE); model.add(FileManager.get().loadModel(ATTRIBUTES_FILE)); model.add(FileManager.get().loadModel(MEASURES_FILE)); resolveCriterionCategories(); resolveAttributes(); resolveMeasures(); } /** * Initialises this criteria manager. */ @PostConstruct public void init() { if (knownMeasures.isEmpty()) { reload(); } } }