Merge branch 'develop' into feature/fauxEditing

This commit is contained in:
Jim Blake 2014-12-08 15:55:32 -05:00
commit 875ba66a46
36 changed files with 1709 additions and 1058 deletions

View file

@ -35,7 +35,6 @@ log4j.rootLogger=INFO, AllAppender
# These classes are too chatty to display INFO messages. # These classes are too chatty to display INFO messages.
log4j.logger.edu.cornell.mannlib.vitro.webapp.startup.StartupStatus=WARN log4j.logger.edu.cornell.mannlib.vitro.webapp.startup.StartupStatus=WARN
log4j.logger.edu.cornell.mannlib.vitro.webapp.dao.jena.pellet.PelletListener=WARN
log4j.logger.edu.cornell.mannlib.vitro.webapp.servlet.setup.UpdateKnowledgeBase=DEBUG log4j.logger.edu.cornell.mannlib.vitro.webapp.servlet.setup.UpdateKnowledgeBase=DEBUG
# Spring as a whole is too chatty to display INFO messages. # Spring as a whole is too chatty to display INFO messages.

View file

@ -16,6 +16,7 @@ import edu.cornell.mannlib.vitro.webapp.modules.ComponentStartupStatus;
import edu.cornell.mannlib.vitro.webapp.modules.fileStorage.FileStorage; import edu.cornell.mannlib.vitro.webapp.modules.fileStorage.FileStorage;
import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor; import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor;
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine; import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine;
import edu.cornell.mannlib.vitro.webapp.modules.tboxreasoner.TBoxReasonerModule;
import edu.cornell.mannlib.vitro.webapp.modules.tripleSource.ConfigurationTripleSource; import edu.cornell.mannlib.vitro.webapp.modules.tripleSource.ConfigurationTripleSource;
import edu.cornell.mannlib.vitro.webapp.modules.tripleSource.ContentTripleSource; import edu.cornell.mannlib.vitro.webapp.modules.tripleSource.ContentTripleSource;
import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.RDFServiceUtils; import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.RDFServiceUtils;
@ -41,6 +42,7 @@ public class ApplicationImpl implements Application {
private FileStorage fileStorage; private FileStorage fileStorage;
private ContentTripleSource contentTripleSource; private ContentTripleSource contentTripleSource;
private ConfigurationTripleSource configurationTripleSource; private ConfigurationTripleSource configurationTripleSource;
private TBoxReasonerModule tboxReasonerModule;
public void setServletContext(ServletContext ctx) { public void setServletContext(ServletContext ctx) {
this.ctx = ctx; this.ctx = ctx;
@ -140,6 +142,22 @@ public class ApplicationImpl implements Application {
} }
} }
@Override
public TBoxReasonerModule getTBoxReasonerModule() {
return tboxReasonerModule;
}
@Property(uri = "http://vitro.mannlib.cornell.edu/ns/vitro/ApplicationSetup#hasTBoxReasonerModule")
public void setTBoxReasonerModule(TBoxReasonerModule module) {
if (tboxReasonerModule == null) {
tboxReasonerModule = module;
} else {
throw new IllegalStateException(
"Configuration includes multiple intances of TBoxReasonerModule: "
+ tboxReasonerModule + ", and " + module);
}
}
@Validation @Validation
public void validate() throws Exception { public void validate() throws Exception {
if (searchEngine == null) { if (searchEngine == null) {
@ -162,6 +180,10 @@ public class ApplicationImpl implements Application {
throw new IllegalStateException( throw new IllegalStateException(
"Configuration did not include a ConfigurationTripleSource."); "Configuration did not include a ConfigurationTripleSource.");
} }
if (tboxReasonerModule == null) {
throw new IllegalStateException(
"Configuration did not include a TBoxReasonerModule.");
}
} }
@Override @Override
@ -244,4 +266,30 @@ public class ApplicationImpl implements Application {
} }
} }
// ----------------------------------------------------------------------
// Setup the reasoners.
//
// This must happen after the FileGraphSetup.
// ----------------------------------------------------------------------
public static class ReasonersSetup implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext ctx = sce.getServletContext();
Application app = ApplicationUtils.instance();
StartupStatus ss = StartupStatus.getBean(ctx);
ComponentStartupStatus css = new ComponentStartupStatusImpl(this,
ss);
TBoxReasonerModule tboxReasoner = app.getTBoxReasonerModule();
tboxReasoner.startup(app, css);
ss.info(this, "Started the TBoxReasonerModule: " + tboxReasoner);
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
Application app = ApplicationUtils.instance();
app.getTBoxReasonerModule().shutdown(app);
}
}
} }

View file

@ -15,6 +15,7 @@ import org.apache.commons.logging.LogFactory;
import edu.cornell.mannlib.vedit.beans.Option; import edu.cornell.mannlib.vedit.beans.Option;
import edu.cornell.mannlib.vedit.util.FormUtils; import edu.cornell.mannlib.vedit.util.FormUtils;
import edu.cornell.mannlib.vitro.webapp.application.ApplicationUtils;
import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission; import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission;
import edu.cornell.mannlib.vitro.webapp.auth.policy.PolicyHelper; import edu.cornell.mannlib.vitro.webapp.auth.policy.PolicyHelper;
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.AuthorizationRequest; import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.AuthorizationRequest;
@ -24,7 +25,7 @@ import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder.ParamMa
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues;
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues; import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues;
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
import edu.cornell.mannlib.vitro.webapp.dao.jena.pellet.PelletListener; import edu.cornell.mannlib.vitro.webapp.modules.tboxreasoner.TBoxReasonerStatus;
import edu.cornell.mannlib.vitro.webapp.search.controller.IndexController; import edu.cornell.mannlib.vitro.webapp.search.controller.IndexController;
import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus; import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus;
@ -159,26 +160,23 @@ public class BaseSiteAdminController extends FreemarkerHttpServlet {
if (PolicyHelper.isAuthorizedForActions(vreq, SimplePermission.EDIT_ONTOLOGY.ACTION)) { if (PolicyHelper.isAuthorizedForActions(vreq, SimplePermission.EDIT_ONTOLOGY.ACTION)) {
String pelletError = null; String error = null;
String pelletExplanation = null; String explanation = null;
Object plObj = getServletContext().getAttribute("pelletListener"); TBoxReasonerStatus status = ApplicationUtils.instance().getTBoxReasonerModule().getStatus();
if ( (plObj != null) && (plObj instanceof PelletListener) ) { if (!status.isConsistent()) {
PelletListener pelletListener = (PelletListener) plObj; error = "INCONSISTENT ONTOLOGY: reasoning halted.";
if (!pelletListener.isConsistent()) { explanation = status.getExplanation();
pelletError = "INCONSISTENT ONTOLOGY: reasoning halted."; } else if ( status.isInErrorState() ) {
pelletExplanation = pelletListener.getExplanation(); error = "An error occurred during reasoning. Reasoning has been halted. See error log for details.";
} else if ( pelletListener.isInErrorState() ) {
pelletError = "An error occurred during reasoning. Reasoning has been halted. See error log for details.";
}
} }
if (pelletError != null) { if (error != null) {
Map<String, String> pellet = new HashMap<String, String>(); Map<String, String> tboxReasonerStatus = new HashMap<String, String>();
pellet.put("error", pelletError); tboxReasonerStatus.put("error", error);
if (pelletExplanation != null) { if (explanation != null) {
pellet.put("explanation", pelletExplanation); tboxReasonerStatus.put("explanation", explanation);
} }
map.put("pellet", pellet); map.put("tboxReasonerStatus", tboxReasonerStatus);
} }
Map<String, String> urls = new HashMap<String, String>(); Map<String, String> urls = new HashMap<String, String>();

View file

@ -44,10 +44,12 @@ import com.hp.hpl.jena.vocabulary.RDF;
import com.hp.hpl.jena.vocabulary.RDFS; import com.hp.hpl.jena.vocabulary.RDFS;
import edu.cornell.mannlib.vedit.controller.BaseEditController; import edu.cornell.mannlib.vedit.controller.BaseEditController;
import edu.cornell.mannlib.vitro.webapp.application.ApplicationUtils;
import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission; import edu.cornell.mannlib.vitro.webapp.auth.permissions.SimplePermission;
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest; import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess; import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess;
import edu.cornell.mannlib.vitro.webapp.modules.tboxreasoner.TBoxReasonerModule;
public class JenaAdminActions extends BaseEditController { public class JenaAdminActions extends BaseEditController {
@ -190,9 +192,8 @@ public class JenaAdminActions extends BaseEditController {
} }
private void printRestrictions() { private void printRestrictions() {
OntModel memoryModel = (OntModel) getServletContext().getAttribute("pelletOntModel"); TBoxReasonerModule reasoner = ApplicationUtils.instance().getTBoxReasonerModule();
for (Restriction rest : memoryModel.listRestrictions().toList() ) { for (Restriction rest : reasoner.listRestrictions() ) {
//System.out.println();
if (rest.isAllValuesFromRestriction()) { if (rest.isAllValuesFromRestriction()) {
log.trace("All values from: "); log.trace("All values from: ");
AllValuesFromRestriction avfr = rest.asAllValuesFromRestriction(); AllValuesFromRestriction avfr = rest.asAllValuesFromRestriction();

View file

@ -41,6 +41,7 @@ import com.hp.hpl.jena.vocabulary.OWL;
import com.hp.hpl.jena.vocabulary.RDF; import com.hp.hpl.jena.vocabulary.RDF;
import com.hp.hpl.jena.vocabulary.RDFS; import com.hp.hpl.jena.vocabulary.RDFS;
import edu.cornell.mannlib.vitro.webapp.application.ApplicationUtils;
import edu.cornell.mannlib.vitro.webapp.beans.BaseResourceBean; import edu.cornell.mannlib.vitro.webapp.beans.BaseResourceBean;
import edu.cornell.mannlib.vitro.webapp.beans.DataProperty; import edu.cornell.mannlib.vitro.webapp.beans.DataProperty;
import edu.cornell.mannlib.vitro.webapp.beans.DataPropertyStatement; import edu.cornell.mannlib.vitro.webapp.beans.DataPropertyStatement;
@ -51,7 +52,7 @@ import edu.cornell.mannlib.vitro.webapp.dao.DataPropertyDao;
import edu.cornell.mannlib.vitro.webapp.dao.InsertException; import edu.cornell.mannlib.vitro.webapp.dao.InsertException;
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
import edu.cornell.mannlib.vitro.webapp.dao.jena.event.EditEvent; import edu.cornell.mannlib.vitro.webapp.dao.jena.event.EditEvent;
import edu.cornell.mannlib.vitro.webapp.dao.jena.pellet.PelletListener; import edu.cornell.mannlib.vitro.webapp.modules.tboxreasoner.TBoxReasonerStatus;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
public class DataPropertyDaoJena extends PropertyDaoJena implements public class DataPropertyDaoJena extends PropertyDaoJena implements
@ -357,10 +358,8 @@ public class DataPropertyDaoJena extends PropertyDaoJena implements
} }
protected boolean reasoningAvailable() { protected boolean reasoningAvailable() {
PelletListener pl = getWebappDaoFactory().getPelletListener(); TBoxReasonerStatus status = ApplicationUtils.instance().getTBoxReasonerModule().getStatus();
return !( return status.isConsistent() && !status.isInErrorState();
( pl == null || !pl.isConsistent() || pl.isInErrorState() )
);
} }
private String getRequiredDatatypeURI(Individual individual, DataProperty dataprop, List<String> vclassURIs) { private String getRequiredDatatypeURI(Individual individual, DataProperty dataprop, List<String> vclassURIs) {

View file

@ -18,7 +18,6 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.apache.jena.iri.IRI; import org.apache.jena.iri.IRI;
import org.apache.jena.iri.IRIFactory; import org.apache.jena.iri.IRIFactory;
import org.mindswap.pellet.jena.vocabulary.SWRL;
import com.hp.hpl.jena.datatypes.xsd.XSDDatatype; import com.hp.hpl.jena.datatypes.xsd.XSDDatatype;
import com.hp.hpl.jena.graph.Node; import com.hp.hpl.jena.graph.Node;
@ -56,6 +55,8 @@ public class JenaBaseDao extends JenaBaseDaoCon {
public static final boolean KEEP_ONLY_IF_TRUE = true; //used for updatePropertyBooleanValue() public static final boolean KEEP_ONLY_IF_TRUE = true; //used for updatePropertyBooleanValue()
public static final boolean KEEP_ONLY_IF_FALSE = false; //used for updatePropertyBooleanValue() public static final boolean KEEP_ONLY_IF_FALSE = false; //used for updatePropertyBooleanValue()
private static final String SWRL_IMP = "http://www.w3.org/2003/11/swrl#Imp";
protected static final Log log = LogFactory.getLog(JenaBaseDao.class.getName()); protected static final Log log = LogFactory.getLog(JenaBaseDao.class.getName());
/* ******************* static constants ****************** */ /* ******************* static constants ****************** */
@ -1105,7 +1106,7 @@ public class JenaBaseDao extends JenaBaseDaoCon {
} }
public void removeRulesMentioningResource(Resource res, OntModel ontModel) { public void removeRulesMentioningResource(Resource res, OntModel ontModel) {
Iterator<Resource> impIt = ontModel.listSubjectsWithProperty(RDF.type, SWRL.Imp); Iterator<Resource> impIt = ontModel.listSubjectsWithProperty(RDF.type, SWRL_IMP);
while (impIt.hasNext()) { while (impIt.hasNext()) {
Resource imp = impIt.next(); Resource imp = impIt.next();
boolean removeMe = false; boolean removeMe = false;

View file

@ -564,26 +564,36 @@ public class PropertyDaoJena extends JenaBaseDao implements PropertyDao {
} }
/* /* Removed: see VIVO-766.
* sorts VClasses so that subclasses come before superclasses * sorts VClasses so that subclasses come before superclasses
*
* Because subclass/superclass is only a partial ordering, this breaks the
* contract for Comparator, and throws an IllegalArgumentException under Java 8.
* In particular, for classes sub, super and other, we have sub=other, sub<super,
* which should imply other<super, but that is not true.
*
* As far as I can determine, this sort is never relied on anyway, so there
* should be no impact in removing it. Note that PropertyInstanceDaoJena re-sorts
* thes results before using them, so this sort was irrelevant for any calls
* through that path.
*/ */
private class VClassHierarchyRanker implements Comparator<VClass> { // private class VClassHierarchyRanker implements Comparator<VClass> {
private VClassDao vcDao; // private VClassDao vcDao;
public VClassHierarchyRanker(VClassDao vcDao) { // public VClassHierarchyRanker(VClassDao vcDao) {
this.vcDao = vcDao; // this.vcDao = vcDao;
} // }
@Override // @Override
public int compare(VClass vc1, VClass vc2) { // public int compare(VClass vc1, VClass vc2) {
if (vcDao.isSubClassOf(vc1, vc2)) { // if (vcDao.isSubClassOf(vc1, vc2)) {
return -1; // return -1;
} else if (vcDao.isSubClassOf(vc2, vc1)) { // } else if (vcDao.isSubClassOf(vc2, vc1)) {
return 1; // return 1;
} else { // } else {
return 0; // return 0;
} // }
} // }
} // }
//
public List<PropertyInstance> getAllPropInstByVClass(String classURI) { public List<PropertyInstance> getAllPropInstByVClass(String classURI) {
if (classURI==null || classURI.length()<1) { if (classURI==null || classURI.length()<1) {
@ -708,7 +718,8 @@ public class PropertyDaoJena extends JenaBaseDao implements PropertyDao {
return propInsts; return propInsts;
} }
Collections.sort(vclasses, new VClassHierarchyRanker(this.getWebappDaoFactory().getVClassDao())); // Removed: see VIVO-766.
// Collections.sort(vclasses, new VClassHierarchyRanker(this.getWebappDaoFactory().getVClassDao()));
OntModel ontModel = getOntModelSelector().getTBoxModel(); OntModel ontModel = getOntModelSelector().getTBoxModel();

View file

@ -48,7 +48,6 @@ import edu.cornell.mannlib.vitro.webapp.dao.VClassGroupDao;
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactoryConfig; import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactoryConfig;
import edu.cornell.mannlib.vitro.webapp.dao.jena.pellet.PelletListener;
import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames; import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.jena.model.RDFServiceModel; import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.jena.model.RDFServiceModel;
@ -71,8 +70,6 @@ public class WebappDaoFactoryJena implements WebappDaoFactory {
protected WebappDaoFactoryConfig config; protected WebappDaoFactoryConfig config;
protected PelletListener pelletListener;
protected String userURI; protected String userURI;
private Map<String,String> properties = new HashMap<String,String>(); private Map<String,String> properties = new HashMap<String,String>();
@ -239,18 +236,6 @@ public class WebappDaoFactoryJena implements WebappDaoFactory {
return config.getNonUserNamespaces(); return config.getNonUserNamespaces();
} }
/**
* This enables the WebappDaoFactory to check the status of a reasoner.
* This will likely be refactored in future releases.
*/
public void setPelletListener(PelletListener pl) {
this.pelletListener = pl;
}
public PelletListener getPelletListener() {
return this.pelletListener;
}
@Override @Override
public List<String> getCommentsForResource(String resourceURI) { public List<String> getCommentsForResource(String resourceURI) {
List<String> commentList = new LinkedList<String>(); List<String> commentList = new LinkedList<String>();

View file

@ -1,52 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.dao.jena.pellet;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.Resource;
public class ObjectPropertyStatementPattern {
private Resource subject = null;
private Property predicate = null;
private Resource object = null;
public ObjectPropertyStatementPattern(Resource subject, Property predicate, Resource object) {
this.subject = subject;
this.predicate = predicate;
this.object = object;
}
public Resource getSubject() {
return this.subject;
}
public Property getPredicate() {
return this.predicate;
}
public Resource getObject() {
return this.object;
}
public boolean matches(ObjectPropertyStatementPattern p2) {
boolean sMatch = false;
boolean pMatch = false;
boolean oMatch = false;
if (this.getSubject() == null || p2.getSubject()==null) {
sMatch = true; // (this.getSubject() == null && p2.getSubject() == null);
} else {
sMatch = (this.getSubject().equals(p2.getSubject()));
}
if (this.getPredicate() == null || p2.getPredicate()==null) {
pMatch = true; // (this.getPredicate() == null && p2.getPredicate() == null);
} else {
pMatch = (this.getPredicate().equals(p2.getPredicate()));
}
if (this.getObject() == null || p2.getObject()==null) {
oMatch = true ; // (this.getObject() == null && p2.getObject() == null);
} else {
oMatch = (this.getObject().equals(p2.getObject()));
}
return (sMatch && pMatch && oMatch);
}
}

View file

@ -1,26 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.dao.jena.pellet;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.Resource;
public class ObjectPropertyStatementPatternFactory {
//private static Set<ObjectPropertyStatementPattern> patternSet = new HashSet<ObjectPropertyStatementPattern>();
public static ObjectPropertyStatementPattern getPattern(Resource subject, Property predicate, Resource object) {
//for (Iterator<ObjectPropertyStatementPattern> i = patternSet.iterator(); i.hasNext(); ) {
// ObjectPropertyStatementPattern pat = i.next();
// if ( ( (pat.getSubject()==null && subject==null) || (pat.getSubject().equals(subject)) )
// && ( (pat.getPredicate()==null && predicate==null) || (pat.getPredicate().equals(predicate)) )
// && ( (pat.getObject()==null && object==null) || (pat.getObject().equals(object)) ) ) {
// return pat;
// }
//}
ObjectPropertyStatementPattern newPat = new ObjectPropertyStatementPattern(subject,predicate,object);
//patternSet.add(newPat);
return newPat;
}
}

View file

@ -1,732 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.dao.jena.pellet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mindswap.pellet.exceptions.InconsistentOntologyException;
import org.mindswap.pellet.jena.PelletInfGraph;
import org.mindswap.pellet.jena.PelletReasonerFactory;
import com.hp.hpl.jena.ontology.DatatypeProperty;
import com.hp.hpl.jena.ontology.ObjectProperty;
import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.rdf.model.Literal;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelChangedListener;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.ResourceFactory;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.rdf.model.StmtIterator;
import com.hp.hpl.jena.shared.Lock;
import com.hp.hpl.jena.util.iterator.ClosableIterator;
import com.hp.hpl.jena.vocabulary.OWL;
import com.hp.hpl.jena.vocabulary.RDF;
import com.hp.hpl.jena.vocabulary.RDFS;
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
import edu.cornell.mannlib.vitro.webapp.dao.jena.event.EditEvent;
public class PelletListener implements ModelChangedListener {
private static final Log log = LogFactory.getLog(PelletListener.class.getName());
private boolean isReasoning = false;
private boolean isSynchronizing = false;
private boolean dirty = false;
private OntModel pelletModel;
private OntModel fullModel;
private OntModel mainModel;
private Model inferenceModel;
private ReasonerConfiguration reasonerConfiguration;
private Set<ObjectPropertyStatementPattern> inferenceDrivingPatternAllowSet;
private Set<ObjectPropertyStatementPattern> inferenceDrivingPatternDenySet;
private Set<ObjectPropertyStatementPattern> inferenceReceivingPatternAllowSet;
private Map<Property,List<ObjectPropertyStatementPattern>> inferenceDrivingPatternMap;
private Model additionModel;
private Model removalModel;
private Model deletedObjectProperties;
private Model deletedDataProperties;
private boolean pipeOpen;
private boolean isConsistent = true;
private boolean inErrorState = false;
private String explanation = "";
public boolean isConsistent() {
return this.isConsistent;
}
public String getExplanation() {
return this.explanation;
}
public boolean isInErrorState() {
return this.inErrorState;
}
public boolean isReasoning() {
return this.isReasoning;
}
public void closePipe() {
pipeOpen = false;
}
public void openPipe() {
pipeOpen = true;
}
public synchronized boolean checkAndStartReasoning(){
if( this.isReasoning )
return false;
else{
this.isReasoning = true;
return true;
}
}
public synchronized void endReasoning() {
this.isReasoning = false;
}
public boolean isDirty() {
return this.dirty;
}
public void setDirty(boolean dirt) {
this.dirty = dirt;
}
private int inferenceRounds = 0;
private boolean foreground = false;
private static final boolean FOREGROUND = true;
private static final boolean BACKGROUND = false;
private static final boolean DONT_SKIP_INITIAL_REASONING = false;
public PelletListener(OntModel fullModel, OntModel model, Model inferenceModel, ReasonerConfiguration reasonerConfiguration) {
this(fullModel, model, inferenceModel, reasonerConfiguration, BACKGROUND);
}
public PelletListener(OntModel fullModel, OntModel model, Model inferenceModel, ReasonerConfiguration reasonerConfiguration, boolean foreground) {
this(fullModel, model, inferenceModel, reasonerConfiguration, foreground, DONT_SKIP_INITIAL_REASONING);
}
public PelletListener(OntModel fullModel, OntModel model, Model inferenceModel, ReasonerConfiguration reasonerConfiguration, boolean foreground, boolean skipReasoningUponInitialization) {
this.pelletModel = ModelFactory.createOntologyModel(reasonerConfiguration.getOntModelSpec());
this.fullModel = fullModel;
this.mainModel = model;
this.inferenceModel = inferenceModel;
if (this.inferenceModel == null) {
log.trace("Inference model is null");
}
this.reasonerConfiguration = reasonerConfiguration;
this.inferenceDrivingPatternAllowSet = reasonerConfiguration.getInferenceDrivingPatternAllowSet();
this.inferenceDrivingPatternDenySet = reasonerConfiguration.getInferenceDrivingPatternDenySet();
this.inferenceReceivingPatternAllowSet = reasonerConfiguration.getInferenceReceivingPatternAllowSet();
if (this.inferenceDrivingPatternAllowSet != null) {
this.inferenceDrivingPatternMap = new HashMap<Property,List<ObjectPropertyStatementPattern>>();
for (Iterator<ObjectPropertyStatementPattern> i = inferenceDrivingPatternAllowSet.iterator(); i.hasNext();) {
ObjectPropertyStatementPattern pat = i.next();
Property p = pat.getPredicate();
List<ObjectPropertyStatementPattern> patList = inferenceDrivingPatternMap.get(p);
if (patList == null) {
patList = new LinkedList<ObjectPropertyStatementPattern>();
patList.add(pat);
inferenceDrivingPatternMap.put(p, patList);
} else {
patList.add(pat);
}
}
}
this.pipeOpen = true;
this.additionModel = ModelFactory.createDefaultModel();
this.removalModel = ModelFactory.createDefaultModel();
this.deletedObjectProperties = ModelFactory.createDefaultModel();
this.deletedDataProperties = ModelFactory.createDefaultModel();
this.mainModel.enterCriticalSection(Lock.READ);
try {
for (ObjectPropertyStatementPattern pat : this.inferenceDrivingPatternAllowSet) {
addedStatements(mainModel.listStatements((Resource) null, pat.getPredicate(), (RDFNode) null));
}
if (!skipReasoningUponInitialization) {
this.foreground = foreground;
notifyEvent(null,new EditEvent(null,false));
} else if (inferenceModel.size() == 0){
foreground = true;
notifyEvent(null,new EditEvent(null,false));
this.foreground = foreground;
}
} finally {
this.mainModel.leaveCriticalSection();
}
this.fullModel.getBaseModel().register(this);
this.mainModel.getBaseModel().register(this);
}
private class InferenceGetter implements Runnable {
private PelletListener pelletListener;
public InferenceGetter(PelletListener pelletListener) {
this.pelletListener = pelletListener;
}
public void run() {
while (pelletListener.isDirty()) {
//pipeOpen = false;
try {
pelletListener.setDirty(false);
inferenceRounds++;
log.info("Getting new inferences");
long startTime = System.currentTimeMillis();
LinkedList<ObjectPropertyStatementPattern> irpl = new LinkedList<ObjectPropertyStatementPattern>();
if (inferenceReceivingPatternAllowSet != null) {
irpl.addAll(inferenceReceivingPatternAllowSet);
} else {
irpl.add(ObjectPropertyStatementPatternFactory.getPattern(null,null,null));
}
if (reasonerConfiguration.getQueryForAllObjectProperties()) {
pelletModel.enterCriticalSection(Lock.READ);
try {
ClosableIterator closeIt = pelletModel.listObjectProperties();
try {
for (Iterator objPropIt = closeIt; objPropIt.hasNext();) {
ObjectProperty objProp = (ObjectProperty) objPropIt.next();
if ( !("http://www.w3.org/2002/07/owl#".equals(objProp.getNameSpace())) ) {
irpl.add(ObjectPropertyStatementPatternFactory.getPattern(null,objProp,null));
}
}
} finally {
closeIt.close();
}
} finally {
pelletModel.leaveCriticalSection();
}
deletedObjectProperties.enterCriticalSection(Lock.WRITE);
try {
ClosableIterator sit = deletedObjectProperties.listSubjects();
try {
while (sit.hasNext()) {
Resource subj = (Resource) sit.next();
irpl.add(ObjectPropertyStatementPatternFactory.getPattern(null,ResourceFactory.createProperty(subj.getURI()),null));
}
} finally {
sit.close();
}
deletedObjectProperties.removeAll();
} finally {
deletedObjectProperties.leaveCriticalSection();
}
}
if (reasonerConfiguration.getQueryForAllDatatypeProperties()) {
pelletModel.enterCriticalSection(Lock.READ);
try {
ClosableIterator closeIt = pelletModel.listDatatypeProperties();
try {
for (Iterator dataPropIt = closeIt; dataPropIt.hasNext();) {
DatatypeProperty dataProp = (DatatypeProperty) dataPropIt.next();
if ( !("http://www.w3.org/2002/07/owl#".equals(dataProp.getNameSpace())) ) {
// TODO: THIS WILL WORK, BUT NEED TO GENERALIZE THE PATTERN CLASSES
irpl.add(ObjectPropertyStatementPatternFactory.getPattern(null,dataProp,null));
}
}
} finally {
closeIt.close();
}
} finally {
pelletModel.leaveCriticalSection();
}
deletedDataProperties.enterCriticalSection(Lock.WRITE);
try {
ClosableIterator sit = deletedDataProperties.listSubjects();
try {
while (sit.hasNext()) {
Resource subj = (Resource) sit.next();
irpl.add(ObjectPropertyStatementPatternFactory.getPattern(null,ResourceFactory.createProperty(subj.getURI()),null));
}
} finally {
sit.close();
}
deletedDataProperties.removeAll();
} finally {
deletedDataProperties.leaveCriticalSection();
}
}
int addCount = 0;
int retractCount = 0;
// force new reasoner (disabled)
if (false && !reasonerConfiguration.isIncrementalReasoningEnabled()) {
Model baseModel = pelletModel.getBaseModel();
pelletModel = ModelFactory.createOntologyModel(PelletReasonerFactory.THE_SPEC);
pelletModel.getDocumentManager().setProcessImports(false);
pelletModel.add(baseModel);
}
pelletModel.enterCriticalSection(Lock.WRITE);
try {
pelletModel.rebind();
pelletModel.prepare();
} finally {
pelletModel.leaveCriticalSection();
}
for (Iterator<ObjectPropertyStatementPattern> patIt = irpl.iterator(); patIt.hasNext(); ) {
ObjectPropertyStatementPattern pat = patIt.next();
if (log.isDebugEnabled()) {
String subjStr = (pat.getSubject() != null) ? pat.getSubject().getURI() : "*";
String predStr = (pat.getPredicate() != null) ? pat.getPredicate().getURI() : "*";
String objStr = (pat.getObject() != null) ? pat.getObject().getURI() : "*";
log.debug("Querying for "+subjStr+" : "+predStr+" : "+objStr);
}
Model tempModel = ModelFactory.createDefaultModel();
pelletModel.enterCriticalSection(Lock.READ);
try {
ClosableIterator ci = pelletModel.listStatements(pat.getSubject(),pat.getPredicate(),pat.getObject());
try {
for (ClosableIterator i=ci; i.hasNext();) {
Statement stmt = (Statement) i.next();
boolean reject = false;
// this next part is only needed if we're using Jena's OWL reasoner instead of actually using Pellet
try {
if ( ( ((Resource)stmt.getObject()).equals(RDFS.Resource) ) ) {
reject = true;
} else if ( ( stmt.getSubject().equals(OWL.Nothing) ) ) {
reject = true;
} else if ( ( stmt.getObject().equals(OWL.Nothing) ) ) {
reject = true;
}
} catch (Exception e) {}
if (!reject) {
tempModel.add(stmt);
boolean fullModelContainsStatement = false;
fullModel.enterCriticalSection(Lock.READ);
try {
fullModelContainsStatement = fullModel.contains(stmt);
} finally {
fullModel.leaveCriticalSection();
}
if (!fullModelContainsStatement) {
// in theory we should be able to lock only the inference model, but I'm not sure yet if Jena propagates the locking upward
fullModel.enterCriticalSection(Lock.WRITE);
closePipe();
try {
inferenceModel.add(stmt);
addCount++;
} finally {
openPipe();
fullModel.leaveCriticalSection();
}
}
}
}
} finally {
ci.close();
}
} finally {
pelletModel.leaveCriticalSection();
}
// now we see what's in the inference model that isn't in the temp model and remove it
try {
Queue<Statement> localRemovalQueue = new LinkedList<Statement>();
inferenceModel.enterCriticalSection(Lock.READ);
try {
ClosableIterator ci = inferenceModel.listStatements(pat.getSubject(),pat.getPredicate(),pat.getObject());
try {
for (ClosableIterator i=ci; i.hasNext();) {
Statement stmt = (Statement) i.next();
if (!tempModel.contains(stmt)) {
localRemovalQueue.add(stmt);
}
}
} finally {
ci.close();
}
} finally {
inferenceModel.leaveCriticalSection();
}
for (Iterator<Statement> i = localRemovalQueue.iterator(); i.hasNext(); ) {
fullModel.enterCriticalSection(Lock.WRITE);
closePipe();
try {
retractCount++;
inferenceModel.remove(i.next());
} finally {
openPipe();
fullModel.leaveCriticalSection();
}
}
localRemovalQueue.clear();
} catch (Exception e) {
log.error("Error getting inferences", e);
}
tempModel = null;
}
this.pelletListener.isConsistent = true;
this.pelletListener.inErrorState = false;
this.pelletListener.explanation = "";
if (log.isDebugEnabled()) {
log.info("Added "+addCount+" statements entailed by assertions");
log.info("Retracted "+retractCount+" statements no longer entailed by assertions");
log.info("Done getting new inferences: "+(System.currentTimeMillis()-startTime)/1000+" seconds");
}
} catch (InconsistentOntologyException ioe) {
this.pelletListener.isConsistent = false;
String explanation = ((PelletInfGraph)pelletModel.getGraph()).getKB().getExplanation();
this.pelletListener.explanation = explanation;
log.error(ioe);
log.error(explanation);
} catch (Exception e) {
this.pelletListener.inErrorState = true;
log.error("Exception during inference", e);
} finally {
pelletListener.endReasoning();
}
}
}
}
private void getInferences() {
this.setDirty(true);
if ( this.checkAndStartReasoning() ){
if (foreground) {
(new InferenceGetter(this)).run();
} else {
new Thread(new InferenceGetter(this), "PelletListener.InferenceGetter").start();
}
}
}
// TODO: These next two methods are really ugly; I need to refactor them to remove redundancy.
private void tryAdd(Statement stmt) {
boolean sendToPellet = false;
if ( pipeOpen && reasonerConfiguration.getReasonOnAllDatatypePropertyStatements() && stmt.getObject().isLiteral() ) {
sendToPellet = true;
} else
if ( pipeOpen && hasCardinalityPredicate(stmt) ) { // see comment on this method
sendToPellet = true;
} else
if ( (stmt.getObject().isResource()) && !((stmt.getPredicate().getURI().indexOf(VitroVocabulary.vitroURI)==0)) ) {
if (pipeOpen) {
sendToPellet = false;
boolean denied = false;
ObjectPropertyStatementPattern stPat = ObjectPropertyStatementPatternFactory.getPattern(stmt.getSubject(), stmt.getPredicate(), (Resource) stmt.getObject());
if (inferenceDrivingPatternDenySet != null) {
for (Iterator<ObjectPropertyStatementPattern> i = inferenceDrivingPatternDenySet.iterator(); i.hasNext(); ){
ObjectPropertyStatementPattern pat = i.next();
if (pat.matches(stPat)) {
denied = true;
break;
}
}
}
if (!denied) {
if (inferenceDrivingPatternAllowSet==null) {
sendToPellet = true;
} else {
// TODO: O(1) implementation of this
List<ObjectPropertyStatementPattern> patList = this.inferenceDrivingPatternMap.get(stmt.getPredicate());
if (patList != null) {
for (Iterator<ObjectPropertyStatementPattern> i = patList.iterator(); i.hasNext(); ){
ObjectPropertyStatementPattern pat = i.next();
if (pat.matches(stPat)) {
sendToPellet = true;
break;
}
}
}
}
}
}
}
if (sendToPellet) {
//long startTime = System.currentTimeMillis();
String valueStr = (stmt.getObject().isResource()) ? ((Resource)stmt.getObject()).getLocalName() : ((Literal)stmt.getObject()).getLexicalForm();
if ( log.isDebugEnabled() ) {
log.debug( "Adding to Pellet: " + renderStatement( stmt ) );
}
additionModel.enterCriticalSection(Lock.WRITE);
try {
additionModel.add(stmt);
} finally {
additionModel.leaveCriticalSection();
}
} else {
if ( log.isDebugEnabled() ) {
log.debug( "Not adding to Pellet: " + renderStatement( stmt ) );
}
}
}
private void tryRemove(Statement stmt) {
boolean removeFromPellet = false;
if ( pipeOpen && reasonerConfiguration.getReasonOnAllDatatypePropertyStatements() && stmt.getObject().isLiteral() ) {
removeFromPellet = true;
} else
if ( pipeOpen && hasCardinalityPredicate(stmt) ) { // see comment on this method
removeFromPellet = true;
} else
if ( stmt.getObject().isResource() ) {
if (pipeOpen) {
if (reasonerConfiguration.getQueryForAllObjectProperties() && stmt.getPredicate().equals(RDF.type) && stmt.getObject().equals(OWL.ObjectProperty)) {
deletedObjectProperties.enterCriticalSection(Lock.WRITE);
try {
deletedObjectProperties.add(stmt);
} finally {
deletedObjectProperties.leaveCriticalSection();
}
}
if (reasonerConfiguration.getQueryForAllDatatypeProperties() && stmt.getPredicate().equals(RDF.type) && stmt.getObject().equals(OWL.DatatypeProperty)) {
deletedDataProperties.enterCriticalSection(Lock.WRITE);
try{
deletedDataProperties.add(stmt);
} finally {
deletedDataProperties.leaveCriticalSection();
}
}
removeFromPellet = false;
boolean denied = false;
ObjectPropertyStatementPattern stPat = ObjectPropertyStatementPatternFactory.getPattern(stmt.getSubject(), stmt.getPredicate(), (Resource) stmt.getObject());
if (inferenceDrivingPatternDenySet != null) {
for (Iterator<ObjectPropertyStatementPattern> i = inferenceDrivingPatternDenySet.iterator(); i.hasNext(); ){
ObjectPropertyStatementPattern pat = i.next();
if (pat.matches(stPat)) {
denied = true;
break;
}
}
}
if (!denied) {
if (inferenceDrivingPatternAllowSet==null) {
removeFromPellet = true;
} else {
// TODO: O(1) implementation of this
List<ObjectPropertyStatementPattern> patList = this.inferenceDrivingPatternMap.get(stmt.getPredicate());
if (patList != null) {
for (Iterator<ObjectPropertyStatementPattern> i = patList.iterator(); i.hasNext(); ){
ObjectPropertyStatementPattern pat = i.next();
if (pat.matches(stPat)) {
removeFromPellet = true;
break;
}
}
}
}
}
}
}
if (removeFromPellet) {
String valueStr = (stmt.getObject().isResource()) ? ((Resource)stmt.getObject()).getLocalName() : ((Literal)stmt.getObject()).getLexicalForm();
log.info("Removing from Pellet: "+stmt.getSubject().getLocalName()+" "+stmt.getPredicate().getLocalName()+" "+valueStr);
removalModel.enterCriticalSection(Lock.WRITE);
try {
removalModel.add(stmt);
} finally {
removalModel.leaveCriticalSection();
}
}
}
// The pattern matching stuff needs to get reworked.
// It originally assumed that only resources would be in object
// position, but cardinality axioms will have e.g. nonNegativeIntegers.
// This is a temporary workaround: all cardinality statements will
// be exposed to Pellet, regardless of configuration patterns.
private boolean hasCardinalityPredicate(Statement stmt) {
return (
stmt.getPredicate().equals(OWL.cardinality) ||
stmt.getPredicate().equals(OWL.minCardinality) ||
stmt.getPredicate().equals(OWL.maxCardinality)
) ;
}
public void addedStatement(Statement arg0) {
tryAdd(arg0);
}
public void addedStatements(Statement[] arg0) {
for (int i=0; i<arg0.length; i++) {
tryAdd(arg0[i]);
}
}
public void addedStatements( List arg0 ) {
for ( Iterator i = arg0.iterator(); i.hasNext(); ) {
tryAdd( (Statement) i.next() );
}
}
public void addedStatements(StmtIterator arg0) {
for (Iterator i = arg0; i.hasNext(); ) {
tryAdd( (Statement) i.next() );
}
}
public void addedStatements(Model arg0) {
for (Iterator i = arg0.listStatements(); i.hasNext(); ) {
tryAdd( (Statement) i.next() );
}
}
public void notifyEvent(Model arg0, Object arg1) {
if (arg1 instanceof EditEvent) {
EditEvent ee = (EditEvent) arg1;
if (!ee.getBegin()) {
if ( (additionModel.size() > 0) || (removalModel.size()>0) ) {
if (!isSynchronizing) {
if (foreground) {
log.debug("Running Pellet in foreground.");
(new PelletSynchronizer()).run();
} else {
log.debug("Running Pellet in background.");
new Thread(new PelletSynchronizer(), "PelletListener.PelletSynchronizer").start();
}
}
}
}
}
}
private class PelletSynchronizer implements Runnable {
public void run() {
try {
isSynchronizing = true;
while (removalModel.size()>0 || additionModel.size()>0) {
Model tempModel = ModelFactory.createDefaultModel();
removalModel.enterCriticalSection(Lock.WRITE);
try {
tempModel.add(removalModel);
removalModel.removeAll();
} finally {
removalModel.leaveCriticalSection();
}
pelletModel.enterCriticalSection(Lock.WRITE);
try {
pelletModel.remove(tempModel);
} finally {
pelletModel.leaveCriticalSection();
}
tempModel.removeAll();
additionModel.enterCriticalSection(Lock.WRITE);
try {
tempModel.add(additionModel);
additionModel.removeAll();
} finally {
additionModel.leaveCriticalSection();
}
pelletModel.enterCriticalSection(Lock.WRITE);
try {
pelletModel.add(tempModel);
} finally {
pelletModel.leaveCriticalSection();
}
tempModel = null;
getInferences();
}
} finally {
isSynchronizing = false;
}
}
}
public void removedStatement(Statement arg0) {
tryRemove(arg0);
}
public void removedStatements(Statement[] arg0) {
for (int i=0; i<arg0.length; i++) {
tryRemove(arg0[i]);
}
}
public void removedStatements(List arg0) {
for (Iterator i = arg0.iterator(); i.hasNext(); ) {
tryRemove( (Statement) i.next());
}
}
public void removedStatements(StmtIterator arg0) {
for (Iterator i = arg0; i.hasNext();) {
tryRemove( (Statement) i.next());
}
}
public void removedStatements(Model arg0) {
for (Iterator i = arg0.listStatements(); i.hasNext();) {
tryRemove( (Statement) i.next());
}
}
public OntModel getPelletModel() {
return this.pelletModel;
}
private String renderStatement(Statement stmt) {
String subjStr = (stmt.getSubject().getURI() != null) ? stmt.getSubject().getURI() : stmt.getSubject().getId().toString();
String predStr = stmt.getPredicate().getURI();
String objStr = "";
RDFNode obj = stmt.getObject();
if (obj.isLiteral()) {
objStr = "\""+(((Literal)obj).getLexicalForm());
} else {
objStr = (((Resource)stmt.getObject()).getURI() != null) ? ((Resource)stmt.getObject()).getURI() : ((Resource)stmt.getObject()).getId().toString();
}
return (subjStr+" : "+predStr+" : "+objStr);
}
}

View file

@ -8,12 +8,12 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import edu.cornell.mannlib.vitro.webapp.application.ApplicationUtils;
import edu.cornell.mannlib.vitro.webapp.beans.Individual; import edu.cornell.mannlib.vitro.webapp.beans.Individual;
import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao; import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao;
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory; import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
import edu.cornell.mannlib.vitro.webapp.dao.jena.WebappDaoFactoryJena;
import edu.cornell.mannlib.vitro.webapp.dao.jena.pellet.PelletListener;
import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditConfigurationVTwo; import edu.cornell.mannlib.vitro.webapp.edit.n3editing.VTwo.EditConfigurationVTwo;
import edu.cornell.mannlib.vitro.webapp.modules.tboxreasoner.TBoxReasonerStatus;
public class IndividualsViaVClassOptions implements FieldOptions { public class IndividualsViaVClassOptions implements FieldOptions {
@ -101,20 +101,13 @@ public class IndividualsViaVClassOptions implements FieldOptions {
return individualMap; return individualMap;
} }
protected boolean isReasoningAvailable( WebappDaoFactory wDaoFact){ protected boolean isReasoningAvailable(){
boolean inferenceAvailable = false; TBoxReasonerStatus status = ApplicationUtils.instance().getTBoxReasonerModule().getStatus();
if (wDaoFact instanceof WebappDaoFactoryJena) { return status.isConsistent() && !status.isInErrorState();
PelletListener pl = ((WebappDaoFactoryJena) wDaoFact).getPelletListener();
if (pl != null && pl.isConsistent() && !pl.isInErrorState()
&& !pl.isReasoning()) {
inferenceAvailable = true;
}
}
return inferenceAvailable;
} }
protected Map<String, Individual> addWhenMissingInference( String classUri , WebappDaoFactory wDaoFact ){ protected Map<String, Individual> addWhenMissingInference( String classUri , WebappDaoFactory wDaoFact ){
boolean inferenceAvailable = isReasoningAvailable(wDaoFact); boolean inferenceAvailable = isReasoningAvailable();
Map<String,Individual> individualMap = new HashMap<String,Individual>(); Map<String,Individual> individualMap = new HashMap<String,Individual>();
if ( !inferenceAvailable ) { if ( !inferenceAvailable ) {
for (String subclassURI : wDaoFact.getVClassDao().getAllSubClassURIs(classUri)) { for (String subclassURI : wDaoFact.getVClassDao().getAllSubClassURIs(classUri)) {

View file

@ -8,6 +8,7 @@ import edu.cornell.mannlib.vitro.webapp.application.VitroHomeDirectory;
import edu.cornell.mannlib.vitro.webapp.modules.fileStorage.FileStorage; import edu.cornell.mannlib.vitro.webapp.modules.fileStorage.FileStorage;
import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor; import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor;
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine; import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine;
import edu.cornell.mannlib.vitro.webapp.modules.tboxreasoner.TBoxReasonerModule;
import edu.cornell.mannlib.vitro.webapp.modules.tripleSource.ConfigurationTripleSource; import edu.cornell.mannlib.vitro.webapp.modules.tripleSource.ConfigurationTripleSource;
import edu.cornell.mannlib.vitro.webapp.modules.tripleSource.ContentTripleSource; import edu.cornell.mannlib.vitro.webapp.modules.tripleSource.ContentTripleSource;
@ -29,6 +30,8 @@ public interface Application {
ConfigurationTripleSource getConfigurationTripleSource(); ConfigurationTripleSource getConfigurationTripleSource();
TBoxReasonerModule getTBoxReasonerModule();
void shutdown(); void shutdown();
public interface Component { public interface Component {

View file

@ -0,0 +1,29 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.modules.tboxreasoner;
import java.util.List;
import com.hp.hpl.jena.ontology.Restriction;
import edu.cornell.mannlib.vitro.webapp.modules.Application;
/**
* A wrapper around the TBox reasoner
*/
public interface TBoxReasonerModule extends Application.Module {
/**
* What is the TBox reasoner doing now?
*/
TBoxReasonerStatus getStatus();
/**
* What restrictions are currently in the reasoner's internal model?
*/
List<Restriction> listRestrictions();
/**
* Wait until the TBox reasoner becomes quiet.
*/
void waitForTBoxReasoning();
}

View file

@ -0,0 +1,29 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.modules.tboxreasoner;
/**
* What is the current state of the TBox reasoner?
*/
public interface TBoxReasonerStatus {
/**
* Is reasoning in progress based on changes to the TBox?
*/
boolean isReasoning();
/**
* Is the TBox free of inconsistency?
*/
boolean isConsistent();
/**
* Did the reasoner fail in its most recent attempt?
*/
boolean isInErrorState();
/**
* A description of the error state, or an empty string (never null).
*/
String getExplanation();
}

View file

@ -97,13 +97,6 @@ public class FileGraphSetup implements ServletContextListener {
OntDocumentManager.getInstance().setProcessImports(false); OntDocumentManager.getInstance().setProcessImports(false);
} }
/*
if (isUpdateRequired(ctx)) {
log.info("mostSpecificType will be computed because a knowledge base migration was performed." );
SimpleReasonerSetup.setMSTComputeRequired(ctx);
} else
*/
if ( (aboxChanged || tboxChanged) && !isUpdateRequired(ctx)) { if ( (aboxChanged || tboxChanged) && !isUpdateRequired(ctx)) {
log.info("a full recompute of the Abox will be performed because" + log.info("a full recompute of the Abox will be performed because" +
" the filegraph abox(s) and/or tbox(s) have changed or are being read for the first time." ); " the filegraph abox(s) and/or tbox(s) have changed or are being read for the first time." );

View file

@ -21,12 +21,7 @@ import org.apache.commons.logging.LogFactory;
import com.hp.hpl.jena.ontology.OntModel; import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.query.Dataset; import com.hp.hpl.jena.query.Dataset;
import com.hp.hpl.jena.rdf.model.Model; import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.vocabulary.OWL;
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
import edu.cornell.mannlib.vitro.webapp.dao.jena.WebappDaoFactoryJena;
import edu.cornell.mannlib.vitro.webapp.dao.jena.pellet.PelletListener;
import edu.cornell.mannlib.vitro.webapp.dao.jena.pellet.ReasonerConfiguration;
import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess; import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess;
import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames; import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService; import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
@ -51,40 +46,9 @@ public class SimpleReasonerSetup implements ServletContextListener {
ServletContext ctx = sce.getServletContext(); ServletContext ctx = sce.getServletContext();
try { try {
// set up Pellet reasoning for the TBox
OntModel tboxAssertionsModel = ModelAccess.on(ctx).getOntModel(ModelNames.TBOX_ASSERTIONS); OntModel tboxAssertionsModel = ModelAccess.on(ctx).getOntModel(ModelNames.TBOX_ASSERTIONS);
OntModel tboxInferencesModel = ModelAccess.on(ctx).getOntModel(ModelNames.TBOX_INFERENCES); OntModel tboxInferencesModel = ModelAccess.on(ctx).getOntModel(ModelNames.TBOX_INFERENCES);
OntModel tboxUnionModel = ModelAccess.on(ctx).getOntModel(ModelNames.TBOX_UNION); OntModel tboxUnionModel = ModelAccess.on(ctx).getOntModel(ModelNames.TBOX_UNION);
log.debug("tboxAssertionsModel=" + tboxAssertionsModel);
log.debug("tboxInferencesModel=" + tboxInferencesModel);
log.debug("tboxUnionModel=" + tboxUnionModel);
WebappDaoFactory wadf = ModelAccess.on(ctx).getWebappDaoFactory();
if (!tboxAssertionsModel.getProfile().NAMESPACE().equals(OWL.NAMESPACE.getNameSpace())) {
log.error("Not connecting Pellet reasoner - the TBox assertions model is not an OWL model");
return;
}
// Set various Pellet options for incremental consistency checking, etc.
//PelletOptions.DL_SAFE_RULES = true;
//PelletOptions.USE_COMPLETION_QUEUE = true;
//PelletOptions.USE_TRACING = true;
//PelletOptions.TRACK_BRANCH_EFFECTS = true;
//PelletOptions.USE_INCREMENTAL_CONSISTENCY = true;
//PelletOptions.USE_INCREMENTAL_DELETION = true;
PelletListener pelletListener = new PelletListener(tboxUnionModel,tboxAssertionsModel,tboxInferencesModel,ReasonerConfiguration.DEFAULT);
sce.getServletContext().setAttribute("pelletListener",pelletListener);
sce.getServletContext().setAttribute("pelletOntModel", pelletListener.getPelletModel());
if (wadf instanceof WebappDaoFactoryJena) {
((WebappDaoFactoryJena) wadf).setPelletListener(pelletListener);
}
log.info("Pellet reasoner connected for the TBox");
waitForTBoxReasoning(sce);
// set up simple reasoning for the ABox // set up simple reasoning for the ABox
@ -139,23 +103,6 @@ public class SimpleReasonerSetup implements ServletContextListener {
} }
} }
public static void waitForTBoxReasoning(ServletContextEvent sce)
throws InterruptedException {
PelletListener pelletListener = (PelletListener) sce.getServletContext().getAttribute("pelletListener");
if (pelletListener == null) {
return ;
}
int sleeps = 0;
// sleep at least once to make sure the TBox reasoning gets started
while ((0 == sleeps) || ((sleeps < 1000) && pelletListener.isReasoning())) {
if (((sleeps - 1) % 10) == 0) { // print message at 10 second intervals
log.info("Waiting for initial TBox reasoning to complete");
}
Thread.sleep(1000);
sleeps++;
}
}
@Override @Override
public void contextDestroyed(ServletContextEvent sce) { public void contextDestroyed(ServletContextEvent sce) {
log.info("received contextDestroyed notification"); log.info("received contextDestroyed notification");
@ -209,17 +156,6 @@ public class SimpleReasonerSetup implements ServletContextListener {
return (RecomputeMode) ctx.getAttribute(RECOMPUTE_REQUIRED_ATTR); return (RecomputeMode) ctx.getAttribute(RECOMPUTE_REQUIRED_ATTR);
} }
private static final String MSTCOMPUTE_REQUIRED_ATTR =
SimpleReasonerSetup.class.getName() + ".MSTComputeRequired";
public static void setMSTComputeRequired(ServletContext ctx) {
ctx.setAttribute(MSTCOMPUTE_REQUIRED_ATTR, true);
}
private static boolean isMSTComputeRequired(ServletContext ctx) {
return (ctx.getAttribute(MSTCOMPUTE_REQUIRED_ATTR) != null);
}
/** /**
* Read the names of the plugin classes classes. * Read the names of the plugin classes classes.
* *
@ -278,6 +214,7 @@ public class SimpleReasonerSetup implements ServletContextListener {
this.simpleReasoner = simpleReasoner; this.simpleReasoner = simpleReasoner;
} }
@Override
public void run() { public void run() {
simpleReasoner.recompute(); simpleReasoner.recompute();
} }

View file

@ -89,7 +89,11 @@ public class UpdateKnowledgeBase implements ServletContextListener {
putReportingPathsIntoSettings(ctx, settings); putReportingPathsIntoSettings(ctx, settings);
putNonReportingPathsIntoSettings(ctx, settings); putNonReportingPathsIntoSettings(ctx, settings);
SimpleReasonerSetup.waitForTBoxReasoning(sce); try {
ApplicationUtils.instance().getTBoxReasonerModule().waitForTBoxReasoning();
} catch (Exception e) {
// Should mean that the reasoner is not even started yet.
}
WebappDaoFactory wadf = ModelAccess.on(ctx).getWebappDaoFactory(); WebappDaoFactory wadf = ModelAccess.on(ctx).getWebappDaoFactory();
settings.setDefaultNamespace(wadf.getDefaultNamespace()); settings.setDefaultNamespace(wadf.getDefaultNamespace());

View file

@ -0,0 +1,349 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.tboxreasoner;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelChangedListener;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.rdf.model.StmtIterator;
import com.hp.hpl.jena.vocabulary.OWL;
import com.hp.hpl.jena.vocabulary.RDF;
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
import edu.cornell.mannlib.vitro.webapp.dao.jena.event.EditEvent;
/**
* Listens for changes on a model. When a change is announced, it is checked for
* worthiness. If worthy, it is added to a change set.
*
* When an ending EditEvent is received, the current change set is passed along
* to the reasoner driver, and a new change set is begun.
*
* Among the criteria for deciding on worthiness is the driving pattern set. In
* the constructor, a map is made from this set, to reduce the number of tests
* made against each statement. I don't know whether this optimization is
* justified.
*
* It is possible to "suspend" the listener, so it will ignore any changes. This
* is useful when the reasoner itself makes changes to the models, so those
* changes do not trigger additional reasoning.
*/
public class ConfiguredReasonerListener implements ModelChangedListener {
private static final Log log = LogFactory
.getLog(ConfiguredReasonerListener.class);
private final ReasonerConfiguration reasonerConfiguration;
private final TBoxReasonerDriver reasonerDriver;
private final DrivingPatternMap drivingPatternMap;
private final AtomicReference<TBoxChanges> changeSet;
private final AtomicBoolean suspended;
public ConfiguredReasonerListener(
ReasonerConfiguration reasonerConfiguration,
TBoxReasonerDriver reasonerDriver) {
this.reasonerConfiguration = reasonerConfiguration;
this.reasonerDriver = reasonerDriver;
this.drivingPatternMap = new DrivingPatternMap(
reasonerConfiguration.getInferenceDrivingPatternAllowSet());
this.changeSet = new AtomicReference<>(new TBoxChanges());
this.suspended = new AtomicBoolean();
}
public Suspension suspend() {
if (!suspended.compareAndSet(false, true)) {
throw new IllegalStateException("Listener is already suspended.");
}
return new Suspension();
}
public class Suspension implements AutoCloseable {
@Override
public void close() {
boolean wasSuspended = suspended.compareAndSet(true, false);
if (!wasSuspended) {
log.warn("Listener was already not suspended.");
}
}
}
/**
* Lists of patterns, mapped by their predicates.
*/
public class DrivingPatternMap extends
HashMap<Property, List<ReasonerStatementPattern>> {
public DrivingPatternMap(Set<ReasonerStatementPattern> patternSet) {
if (patternSet != null) {
for (ReasonerStatementPattern pat : patternSet) {
Property p = pat.getPredicate();
if (!containsKey(p)) {
put(p, new LinkedList<ReasonerStatementPattern>());
}
get(p).add(pat);
}
}
}
}
// ----------------------------------------------------------------------
// Implement the ModelChangedListener methods. Delegate to the methods that
// check criteria.
// ----------------------------------------------------------------------
@Override
public void addedStatement(Statement s) {
tryAdd(s);
}
@Override
public void addedStatements(Statement[] statements) {
for (Statement stmt : statements) {
tryAdd(stmt);
}
}
@Override
public void addedStatements(List<Statement> statements) {
for (Statement stmt : statements) {
tryAdd(stmt);
}
}
@Override
public void addedStatements(StmtIterator statements) {
for (Statement stmt : statements.toList()) {
tryAdd(stmt);
}
}
@Override
public void addedStatements(Model m) {
for (Statement stmt : m.listStatements().toList()) {
tryAdd(stmt);
}
}
@Override
public void removedStatement(Statement s) {
tryRemove(s);
}
@Override
public void removedStatements(Statement[] statements) {
for (Statement stmt : statements) {
tryRemove(stmt);
}
}
@Override
public void removedStatements(List<Statement> statements) {
for (Statement stmt : statements) {
tryRemove(stmt);
}
}
@Override
public void removedStatements(StmtIterator statements) {
for (Statement stmt : statements.toList()) {
tryRemove(stmt);
}
}
@Override
public void removedStatements(Model m) {
for (Statement stmt : m.listStatements().toList()) {
tryRemove(stmt);
}
}
@Override
public void notifyEvent(Model m, Object event) {
if (event instanceof EditEvent) {
EditEvent ee = (EditEvent) event;
if (!ee.getBegin()) {
TBoxChanges changes = changeSet.getAndSet(new TBoxChanges());
this.reasonerDriver.runSynchronizer(changes);
}
}
}
// ----------------------------------------------------------------------
// Check the criteria to determine whether each addition or removal should
// be passed to the reasoner.
//
// When the listener is suspended, nothing is passed on.
// ----------------------------------------------------------------------
public void tryAdd(Statement stmt) {
if (suspended.get()) {
return;
}
if (isDataProperty(stmt)) {
if (reasonOnAllDataProperties() || hasCardinalityPredicate(stmt)) {
addIt(stmt);
return;
} else {
return;
}
}
if (predicateIsInVitroNamespace(stmt)
|| statementMatchesDenyPattern(stmt)) {
return;
}
if (thereAreNoDrivingPatterns() || statementMatchesDrivingPattern(stmt)) {
addIt(stmt);
return;
}
}
public void tryRemove(Statement stmt) {
if (suspended.get()) {
return;
}
if (isDataProperty(stmt)) {
if (reasonOnAllDataProperties() || hasCardinalityPredicate(stmt)) {
removeIt(stmt);
return;
} else {
return;
}
}
if (actOnObjectPropertyDeclarations() && declaresObjectProperty(stmt)) {
deleteObjectProperty(stmt);
return;
}
if (actOnDataPropertyDeclarations() && declaresDataProperty(stmt)) {
deleteDataProperty(stmt);
return;
}
if (statementMatchesDenyPattern(stmt)) {
return;
}
if (thereAreNoDrivingPatterns() || statementMatchesDrivingPattern(stmt)) {
removeIt(stmt);
return;
}
}
private boolean isDataProperty(Statement stmt) {
return stmt.getObject().isLiteral();
}
private boolean reasonOnAllDataProperties() {
return reasonerConfiguration.getReasonOnAllDatatypePropertyStatements();
}
private boolean predicateIsInVitroNamespace(Statement stmt) {
return stmt.getPredicate().getURI().indexOf(VitroVocabulary.vitroURI) == 0;
}
private boolean statementMatchesDenyPattern(Statement stmt) {
Set<ReasonerStatementPattern> denyPatterns = reasonerConfiguration.inferenceDrivingPatternDenySet;
if (denyPatterns == null) {
return false;
}
ReasonerStatementPattern stPat = ReasonerStatementPattern
.objectPattern(stmt);
for (ReasonerStatementPattern pat : denyPatterns) {
if (pat.matches(stPat)) {
return true;
}
}
return false;
}
private boolean thereAreNoDrivingPatterns() {
return reasonerConfiguration.inferenceDrivingPatternAllowSet == null;
}
private boolean statementMatchesDrivingPattern(Statement stmt) {
List<ReasonerStatementPattern> drivePatterns = drivingPatternMap
.get(stmt.getPredicate());
if (drivePatterns == null) {
return false;
}
ReasonerStatementPattern stPat = ReasonerStatementPattern
.objectPattern(stmt);
for (ReasonerStatementPattern pat : drivePatterns) {
if (pat.matches(stPat)) {
return true;
}
}
return false;
}
private boolean actOnObjectPropertyDeclarations() {
return reasonerConfiguration.getQueryForAllObjectProperties();
}
private boolean declaresObjectProperty(Statement stmt) {
return stmt.getPredicate().equals(RDF.type)
&& stmt.getObject().equals(OWL.ObjectProperty);
}
private boolean actOnDataPropertyDeclarations() {
return reasonerConfiguration.getQueryForAllDatatypeProperties();
}
private boolean declaresDataProperty(Statement stmt) {
return stmt.getPredicate().equals(RDF.type)
&& stmt.getObject().equals(OWL.DatatypeProperty);
}
private void addIt(Statement stmt) {
changeSet.get().addStatement(stmt);
}
private void removeIt(Statement stmt) {
changeSet.get().removeStatement(stmt);
}
private void deleteObjectProperty(Statement stmt) {
changeSet.get().deleteObjectProperty(stmt);
}
private void deleteDataProperty(Statement stmt) {
changeSet.get().deleteDataProperty(stmt);
}
// The pattern matching stuff needs to get reworked.
// It originally assumed that only resources would be in object
// position, but cardinality axioms will have e.g. nonNegativeIntegers.
// This is a temporary workaround: all cardinality statements will
// be exposed to Pellet, regardless of configuration patterns.
private boolean hasCardinalityPredicate(Statement stmt) {
return (stmt.getPredicate().equals(OWL.cardinality)
|| stmt.getPredicate().equals(OWL.minCardinality) || stmt
.getPredicate().equals(OWL.maxCardinality));
}
}

View file

@ -0,0 +1,119 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.tboxreasoner;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.Statement;
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.ConfiguredReasonerListener;
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.ConfiguredReasonerListener.Suspension;
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.ReasonerStatementPattern;
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.TBoxReasoner;
import edu.cornell.mannlib.vitro.webapp.utils.jena.criticalsection.LockableModel;
import edu.cornell.mannlib.vitro.webapp.utils.jena.criticalsection.LockableOntModel;
import edu.cornell.mannlib.vitro.webapp.utils.jena.criticalsection.LockedModel;
import edu.cornell.mannlib.vitro.webapp.utils.jena.criticalsection.LockedOntModel;
/**
* A tool that will adjust the inferences model to match the reasoner model,
* after applying the proper filters to both.
*/
public class InferenceModelUpdater {
private static final Log log = LogFactory
.getLog(InferenceModelUpdater.class);
private final TBoxReasoner reasoner;
private final LockableModel lockableInferencesModel;
private final LockableOntModel lockableFullModel;
private final ConfiguredReasonerListener listener;
private int addCount;
private int retractCount;
public int getAddCount() {
return addCount;
}
public int getRetractCount() {
return retractCount;
}
public InferenceModelUpdater(TBoxReasoner reasoner,
LockableModel lockableInferencesModel,
LockableOntModel lockableFullModel,
ConfiguredReasonerListener listener) {
this.reasoner = reasoner;
this.lockableInferencesModel = lockableInferencesModel;
this.lockableFullModel = lockableFullModel;
this.listener = listener;
}
/**
* Synchronize the inferences model with the reasoner model, with this
* proviso:
*
* If a statement exists anywhere in the full TBox, don't bother adding it
* to the inferences model.
*/
public void update(List<ReasonerStatementPattern> patternList) {
List<Statement> filteredReasonerModel = reasoner
.filterResults(patternList);
addNewInferences(filteredReasonerModel);
removeOldInferences(filterInferencesModel(patternList),
filteredReasonerModel);
log.debug("Added: " + addCount + ", Retracted: " + retractCount);
}
private void addNewInferences(List<Statement> filteredReasonerModel) {
for (Statement stmt : filteredReasonerModel) {
if (!fullModelContainsStatement(stmt)) {
try (LockedModel inferenceModel = lockableInferencesModel
.write(); Suspension susp = listener.suspend()) {
inferenceModel.add(stmt);
addCount++;
}
}
}
}
private boolean fullModelContainsStatement(Statement stmt) {
try (LockedOntModel fullModel = lockableFullModel.read()) {
return fullModel.contains(stmt);
}
}
private Model filterInferencesModel(
List<ReasonerStatementPattern> patternList) {
Model filtered = ModelFactory.createDefaultModel();
try (LockedModel inferencesModel = lockableInferencesModel.read()) {
for (ReasonerStatementPattern pattern : patternList) {
filtered.add(pattern.matchStatementsFromModel(inferencesModel));
}
}
log.debug("Filtered inferences model: " + filtered.size());
return filtered;
}
private void removeOldInferences(Model filteredInferencesModel,
List<Statement> filteredReasonerStatements) {
Model filteredReasonerModel = ModelFactory.createDefaultModel();
filteredReasonerModel.add(filteredReasonerStatements);
for (Statement stmt : filteredInferencesModel.listStatements().toList()) {
if (!filteredReasonerModel.contains(stmt)) {
try (LockedModel inferenceModel = lockableInferencesModel
.write(); Suspension susp = listener.suspend()) {
retractCount++;
inferenceModel.remove(stmt);
}
}
}
}
}

View file

@ -0,0 +1,81 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.tboxreasoner;
import static com.hp.hpl.jena.rdf.model.ResourceFactory.createProperty;
import java.util.LinkedList;
import java.util.Set;
import com.hp.hpl.jena.ontology.DatatypeProperty;
import com.hp.hpl.jena.ontology.ObjectProperty;
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.ReasonerConfiguration;
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.ReasonerStatementPattern;
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.TBoxChanges;
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.TBoxReasoner;
/**
* The list of patterns for filtering the models will include:
*
* All patterns specified by the ReasonerConfiguration,
*
* One pattern for each deleted property, to match the use of that property as a
* predicate.
*/
public class PatternListBuilder {
private static final String OWL_NS = "http://www.w3.org/2002/07/owl#";
private final ReasonerConfiguration reasonerConfiguration;
private final TBoxReasoner reasoner;
private final TBoxChanges changes;
public PatternListBuilder(ReasonerConfiguration reasonerConfiguration,
TBoxReasoner reasoner, TBoxChanges changes) {
this.reasonerConfiguration = reasonerConfiguration;
this.reasoner = reasoner;
this.changes = changes;
}
public LinkedList<ReasonerStatementPattern> build() {
LinkedList<ReasonerStatementPattern> patterns = new LinkedList<>();
Set<ReasonerStatementPattern> allowSet = reasonerConfiguration
.getInferenceReceivingPatternAllowSet();
if (allowSet != null) {
patterns.addAll(allowSet);
} else {
patterns.add(ReasonerStatementPattern.ANY_OBJECT_PROPERTY);
}
if (reasonerConfiguration.getQueryForAllObjectProperties()) {
for (ObjectProperty objProp : reasoner.listObjectProperties()) {
if (!(OWL_NS.equals(objProp.getNameSpace()))) {
patterns.add(ReasonerStatementPattern
.objectPattern(objProp));
}
}
for (String uri : changes.getDeletedObjectPropertyUris()) {
patterns.add(ReasonerStatementPattern
.objectPattern(createProperty(uri)));
}
}
if (reasonerConfiguration.getQueryForAllDatatypeProperties()) {
for (DatatypeProperty dataProp : reasoner.listDatatypeProperties()) {
if (!(OWL_NS.equals(dataProp.getNameSpace()))) {
// TODO: THIS WILL WORK, BUT NEED TO GENERALIZE THE
// PATTERN CLASSES
patterns.add(ReasonerStatementPattern
.objectPattern(dataProp));
}
}
for (String uri : changes.getDeletedDataPropertyUris()) {
patterns.add(ReasonerStatementPattern
.objectPattern(createProperty(uri)));
}
}
return patterns;
}
}

View file

@ -1,22 +1,22 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */ /* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.dao.jena.pellet; package edu.cornell.mannlib.vitro.webapp.tboxreasoner;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import org.mindswap.pellet.jena.PelletReasonerFactory;
import com.hp.hpl.jena.ontology.OntModelSpec; import com.hp.hpl.jena.ontology.OntModelSpec;
import com.hp.hpl.jena.vocabulary.OWL; import com.hp.hpl.jena.vocabulary.OWL;
import com.hp.hpl.jena.vocabulary.RDF; import com.hp.hpl.jena.vocabulary.RDF;
import com.hp.hpl.jena.vocabulary.RDFS; import com.hp.hpl.jena.vocabulary.RDFS;
import org.mindswap.pellet.jena.PelletReasonerFactory;
public class ReasonerConfiguration { public class ReasonerConfiguration {
public Set<ObjectPropertyStatementPattern> inferenceDrivingPatternAllowSet; public Set<ReasonerStatementPattern> inferenceDrivingPatternAllowSet;
public Set<ObjectPropertyStatementPattern> inferenceDrivingPatternDenySet; public Set<ReasonerStatementPattern> inferenceDrivingPatternDenySet;
public Set<ObjectPropertyStatementPattern> inferenceReceivingPatternAllowSet; public Set<ReasonerStatementPattern> inferenceReceivingPatternAllowSet;
private boolean queryForAllObjectProperties = false; private boolean queryForAllObjectProperties = false;
private boolean incrementalReasoningEnabled = true; private boolean incrementalReasoningEnabled = true;
@ -58,34 +58,34 @@ public class ReasonerConfiguration {
//ask the reasoner only to classify, realize, and infer disjointWith statements (based on a somewhat incomplete information) //ask the reasoner only to classify, realize, and infer disjointWith statements (based on a somewhat incomplete information)
DEFAULT = new ReasonerConfiguration(); DEFAULT = new ReasonerConfiguration();
HashSet<ObjectPropertyStatementPattern> defaultInferenceDrivingPatternAllowSet = new HashSet<ObjectPropertyStatementPattern>(); HashSet<ReasonerStatementPattern> defaultInferenceDrivingPatternAllowSet = new HashSet<>();
defaultInferenceDrivingPatternAllowSet.add(ObjectPropertyStatementPatternFactory.getPattern(null,RDF.type,null)); defaultInferenceDrivingPatternAllowSet.add(ReasonerStatementPattern.objectPattern(RDF.type));
defaultInferenceDrivingPatternAllowSet.add(ObjectPropertyStatementPatternFactory.getPattern(null,RDFS.subClassOf,null)); defaultInferenceDrivingPatternAllowSet.add(ReasonerStatementPattern.objectPattern(RDFS.subClassOf));
defaultInferenceDrivingPatternAllowSet.add(ObjectPropertyStatementPatternFactory.getPattern(null,RDFS.subPropertyOf,null)); defaultInferenceDrivingPatternAllowSet.add(ReasonerStatementPattern.objectPattern(RDFS.subPropertyOf));
defaultInferenceDrivingPatternAllowSet.add(ObjectPropertyStatementPatternFactory.getPattern(null,OWL.equivalentClass,null)); defaultInferenceDrivingPatternAllowSet.add(ReasonerStatementPattern.objectPattern(OWL.equivalentClass));
defaultInferenceDrivingPatternAllowSet.add(ObjectPropertyStatementPatternFactory.getPattern(null,OWL.unionOf,null)); defaultInferenceDrivingPatternAllowSet.add(ReasonerStatementPattern.objectPattern(OWL.unionOf));
defaultInferenceDrivingPatternAllowSet.add(ObjectPropertyStatementPatternFactory.getPattern(null,OWL.intersectionOf,null)); defaultInferenceDrivingPatternAllowSet.add(ReasonerStatementPattern.objectPattern(OWL.intersectionOf));
defaultInferenceDrivingPatternAllowSet.add(ObjectPropertyStatementPatternFactory.getPattern(null,OWL.complementOf,null)); defaultInferenceDrivingPatternAllowSet.add(ReasonerStatementPattern.objectPattern(OWL.complementOf));
defaultInferenceDrivingPatternAllowSet.add(ObjectPropertyStatementPatternFactory.getPattern(null,OWL.oneOf,null)); defaultInferenceDrivingPatternAllowSet.add(ReasonerStatementPattern.objectPattern(OWL.oneOf));
defaultInferenceDrivingPatternAllowSet.add(ObjectPropertyStatementPatternFactory.getPattern(null,OWL.onProperty,null)); defaultInferenceDrivingPatternAllowSet.add(ReasonerStatementPattern.objectPattern(OWL.onProperty));
defaultInferenceDrivingPatternAllowSet.add(ObjectPropertyStatementPatternFactory.getPattern(null,OWL.someValuesFrom,null)); defaultInferenceDrivingPatternAllowSet.add(ReasonerStatementPattern.objectPattern(OWL.someValuesFrom));
defaultInferenceDrivingPatternAllowSet.add(ObjectPropertyStatementPatternFactory.getPattern(null,OWL.allValuesFrom,null)); defaultInferenceDrivingPatternAllowSet.add(ReasonerStatementPattern.objectPattern(OWL.allValuesFrom));
defaultInferenceDrivingPatternAllowSet.add(ObjectPropertyStatementPatternFactory.getPattern(null,OWL.hasValue,null)); defaultInferenceDrivingPatternAllowSet.add(ReasonerStatementPattern.objectPattern(OWL.hasValue));
defaultInferenceDrivingPatternAllowSet.add(ObjectPropertyStatementPatternFactory.getPattern(null,OWL.minCardinality,null)); defaultInferenceDrivingPatternAllowSet.add(ReasonerStatementPattern.objectPattern(OWL.minCardinality));
defaultInferenceDrivingPatternAllowSet.add(ObjectPropertyStatementPatternFactory.getPattern(null,OWL.maxCardinality,null)); defaultInferenceDrivingPatternAllowSet.add(ReasonerStatementPattern.objectPattern(OWL.maxCardinality));
defaultInferenceDrivingPatternAllowSet.add(ObjectPropertyStatementPatternFactory.getPattern(null,OWL.cardinality,null)); defaultInferenceDrivingPatternAllowSet.add(ReasonerStatementPattern.objectPattern(OWL.cardinality));
defaultInferenceDrivingPatternAllowSet.add(ObjectPropertyStatementPatternFactory.getPattern(null,RDF.first,null)); defaultInferenceDrivingPatternAllowSet.add(ReasonerStatementPattern.objectPattern(RDF.first));
defaultInferenceDrivingPatternAllowSet.add(ObjectPropertyStatementPatternFactory.getPattern(null,RDF.rest,null)); defaultInferenceDrivingPatternAllowSet.add(ReasonerStatementPattern.objectPattern(RDF.rest));
defaultInferenceDrivingPatternAllowSet.add(ObjectPropertyStatementPatternFactory.getPattern(null,OWL.disjointWith,null)); defaultInferenceDrivingPatternAllowSet.add(ReasonerStatementPattern.objectPattern(OWL.disjointWith));
defaultInferenceDrivingPatternAllowSet.add(ObjectPropertyStatementPatternFactory.getPattern(null,OWL.inverseOf,null)); defaultInferenceDrivingPatternAllowSet.add(ReasonerStatementPattern.objectPattern(OWL.inverseOf));
DEFAULT.setInferenceDrivingPatternAllowSet(defaultInferenceDrivingPatternAllowSet); DEFAULT.setInferenceDrivingPatternAllowSet(defaultInferenceDrivingPatternAllowSet);
Set<ObjectPropertyStatementPattern> defaultInferenceReceivingPatternAllowSet = new HashSet<ObjectPropertyStatementPattern>(); Set<ReasonerStatementPattern> defaultInferenceReceivingPatternAllowSet = new HashSet<>();
defaultInferenceReceivingPatternAllowSet.add(ObjectPropertyStatementPatternFactory.getPattern(null,RDF.type,null)); defaultInferenceReceivingPatternAllowSet.add(ReasonerStatementPattern.objectPattern(RDF.type));
defaultInferenceReceivingPatternAllowSet.add(ObjectPropertyStatementPatternFactory.getPattern(null,RDFS.subClassOf,null)); defaultInferenceReceivingPatternAllowSet.add(ReasonerStatementPattern.objectPattern(RDFS.subClassOf));
defaultInferenceReceivingPatternAllowSet.add(ObjectPropertyStatementPatternFactory.getPattern(null,RDFS.subPropertyOf,null)); defaultInferenceReceivingPatternAllowSet.add(ReasonerStatementPattern.objectPattern(RDFS.subPropertyOf));
defaultInferenceReceivingPatternAllowSet.add(ObjectPropertyStatementPatternFactory.getPattern(null,OWL.equivalentClass,null)); defaultInferenceReceivingPatternAllowSet.add(ReasonerStatementPattern.objectPattern(OWL.equivalentClass));
defaultInferenceReceivingPatternAllowSet.add(ObjectPropertyStatementPatternFactory.getPattern(null,OWL.disjointWith,null)); defaultInferenceReceivingPatternAllowSet.add(ReasonerStatementPattern.objectPattern(OWL.disjointWith));
defaultInferenceReceivingPatternAllowSet.add(ObjectPropertyStatementPatternFactory.getPattern(null,OWL.inverseOf,null)); defaultInferenceReceivingPatternAllowSet.add(ReasonerStatementPattern.objectPattern(OWL.inverseOf));
DEFAULT.setInferenceReceivingPatternAllowSet(defaultInferenceReceivingPatternAllowSet); DEFAULT.setInferenceReceivingPatternAllowSet(defaultInferenceReceivingPatternAllowSet);
DEFAULT.setQueryForAllObjectProperties(false); DEFAULT.setQueryForAllObjectProperties(false);
@ -107,36 +107,36 @@ public class ReasonerConfiguration {
COMPLETE.setQueryForAllObjectProperties(true); COMPLETE.setQueryForAllObjectProperties(true);
COMPLETE.setReasonOnAllDatatypePropertyStatements(true); COMPLETE.setReasonOnAllDatatypePropertyStatements(true);
COMPLETE.setQueryForAllDatatypeProperties(true); COMPLETE.setQueryForAllDatatypeProperties(true);
Set<ObjectPropertyStatementPattern> completeInferenceReceivingPatternAllowSet = new HashSet<ObjectPropertyStatementPattern>(); Set<ReasonerStatementPattern> completeInferenceReceivingPatternAllowSet = new HashSet<>();
completeInferenceReceivingPatternAllowSet.addAll(defaultInferenceReceivingPatternAllowSet); completeInferenceReceivingPatternAllowSet.addAll(defaultInferenceReceivingPatternAllowSet);
completeInferenceReceivingPatternAllowSet.add(ObjectPropertyStatementPatternFactory.getPattern(null, OWL.sameAs, null)); completeInferenceReceivingPatternAllowSet.add(ReasonerStatementPattern.objectPattern(OWL.sameAs));
// getting NPEs inside Pellet with differentFrom on 2.0.0-rc7 // getting NPEs inside Pellet with differentFrom on 2.0.0-rc7
//completeInferenceReceivingPatternAllowSet.add(ObjectPropertyStatementPatternFactory.getPattern(null, OWL.differentFrom, null)); //completeInferenceReceivingPatternAllowSet.add(ReasonerStatementPattern.objectPattern( OWL.differentFrom, null));
COMPLETE.setInferenceReceivingPatternAllowSet(completeInferenceReceivingPatternAllowSet); COMPLETE.setInferenceReceivingPatternAllowSet(completeInferenceReceivingPatternAllowSet);
} }
public Set<ObjectPropertyStatementPattern> getInferenceDrivingPatternAllowSet() { public Set<ReasonerStatementPattern> getInferenceDrivingPatternAllowSet() {
return this.inferenceDrivingPatternAllowSet; return this.inferenceDrivingPatternAllowSet;
} }
public void setInferenceDrivingPatternAllowSet(Set<ObjectPropertyStatementPattern> patternSet) { public void setInferenceDrivingPatternAllowSet(Set<ReasonerStatementPattern> patternSet) {
this.inferenceDrivingPatternAllowSet = patternSet; this.inferenceDrivingPatternAllowSet = patternSet;
} }
public Set<ObjectPropertyStatementPattern> getInferenceDrivingPatternDenySet() { public Set<ReasonerStatementPattern> getInferenceDrivingPatternDenySet() {
return this.inferenceDrivingPatternDenySet; return this.inferenceDrivingPatternDenySet;
} }
public void setInferenceDrivingPatternDenySet(Set<ObjectPropertyStatementPattern> patternSet) { public void setInferenceDrivingPatternDenySet(Set<ReasonerStatementPattern> patternSet) {
this.inferenceDrivingPatternDenySet = patternSet; this.inferenceDrivingPatternDenySet = patternSet;
} }
public Set<ObjectPropertyStatementPattern> getInferenceReceivingPatternAllowSet() { public Set<ReasonerStatementPattern> getInferenceReceivingPatternAllowSet() {
return this.inferenceReceivingPatternAllowSet; return this.inferenceReceivingPatternAllowSet;
} }
public void setInferenceReceivingPatternAllowSet(Set<ObjectPropertyStatementPattern> patternSet) { public void setInferenceReceivingPatternAllowSet(Set<ReasonerStatementPattern> patternSet) {
this.inferenceReceivingPatternAllowSet = patternSet; this.inferenceReceivingPatternAllowSet = patternSet;
} }

View file

@ -0,0 +1,94 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.tboxreasoner;
import java.util.List;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.shared.Lock;
/**
* For now, this only models Object Properties.
*
* It should be easy to add Data Property patterns by making this abstract and
* creating two concrete subclasses.
*/
public class ReasonerStatementPattern {
public static final ReasonerStatementPattern ANY_OBJECT_PROPERTY = new ReasonerStatementPattern(
null, null, null);
public static ReasonerStatementPattern objectPattern(Property predicate) {
return new ReasonerStatementPattern(null, predicate, null);
}
public static ReasonerStatementPattern objectPattern(Statement stmt) {
if (!stmt.getObject().isResource()) {
throw new IllegalArgumentException(
"Object of stmt must be a resource.");
}
return new ReasonerStatementPattern(stmt.getSubject(),
stmt.getPredicate(), stmt.getObject().asResource());
}
/**
* Any or all of these may be null, which acts as a wild card.
*/
private final Resource subject;
private final Property predicate;
private final Resource object;
private final String toString;
private ReasonerStatementPattern(Resource subject, Property predicate,
Resource object) {
this.subject = subject;
this.predicate = predicate;
this.object = object;
this.toString = buildToString();
}
public Property getPredicate() {
return predicate;
}
/**
* All fields must match, either by being equal, or by being a wild card.
*/
public boolean matches(ReasonerStatementPattern that) {
boolean sMatch = this.subject == null || that.subject == null
|| this.subject.equals(that.subject);
boolean pMatch = this.predicate == null || that.predicate == null
|| this.predicate.equals(that.predicate);
boolean oMatch = this.object == null || that.object == null
|| this.object.equals(that.object);
return sMatch && pMatch && oMatch;
}
/**
* Get a list of statements from this model that match this pattern.
*/
public List<Statement> matchStatementsFromModel(Model m) {
m.enterCriticalSection(Lock.READ);
try {
return m.listStatements(subject, predicate, object).toList();
} finally {
m.leaveCriticalSection();
}
}
public String buildToString() {
return "ReasonerStatementPattern[subject="
+ (subject == null ? "*" : subject.toString()) + ", predicate="
+ (predicate == null ? "*" : predicate.toString())
+ ", object=" + (object == null ? "*" : object.toString())
+ "]";
}
@Override
public String toString() {
return toString;
}
}

View file

@ -0,0 +1,83 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.tboxreasoner;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Statement;
/**
* Accumulate changes to the TBox as they arrive. Then make them available to
* the TBox reasoner.
*/
public class TBoxChanges {
private final List<Statement> addedStatements = Collections
.synchronizedList(new ArrayList<Statement>());
private final List<Statement> removedStatements = Collections
.synchronizedList(new ArrayList<Statement>());
private final List<String> deletedDataPropertyUris = Collections
.synchronizedList(new ArrayList<String>());
private final List<String> deletedObjectPropertyUris = Collections
.synchronizedList(new ArrayList<String>());
// ----------------------------------------------------------------------
// These methods are called when populating the changeSet. They must be
// thread-safe.
// ----------------------------------------------------------------------
public void addStatement(Statement stmt) {
addedStatements.add(stmt);
}
public void removeStatement(Statement stmt) {
removedStatements.remove(stmt);
}
public void deleteDataProperty(Statement stmt) {
Resource subject = stmt.getSubject();
if (subject.isURIResource()) {
deletedDataPropertyUris.add(subject.getURI());
}
}
public void deleteObjectProperty(Statement stmt) {
Resource subject = stmt.getSubject();
if (subject.isURIResource()) {
deletedObjectPropertyUris.add(subject.getURI());
}
}
// ----------------------------------------------------------------------
// These methods are called when processing the changeSet. By that time, it
// is owned and accessed by a single thread.
// ----------------------------------------------------------------------
public boolean isEmpty() {
return addedStatements.isEmpty() && removedStatements.isEmpty()
&& deletedDataPropertyUris.isEmpty()
&& deletedObjectPropertyUris.isEmpty();
}
public List<Statement> getAddedStatements() {
return addedStatements;
}
public List<Statement> getRemovedStatements() {
return removedStatements;
}
public List<String> getDeletedDataPropertyUris() {
return deletedDataPropertyUris;
}
public List<String> getDeletedObjectPropertyUris() {
return deletedObjectPropertyUris;
}
}

View file

@ -0,0 +1,87 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.tboxreasoner;
import java.util.List;
import com.hp.hpl.jena.ontology.DatatypeProperty;
import com.hp.hpl.jena.ontology.ObjectProperty;
import com.hp.hpl.jena.ontology.Restriction;
import com.hp.hpl.jena.rdf.model.Statement;
/**
* The functionality of a TBox reasoner.
*
* The reasoner will maintain its own TBox model. It will receive updates to
* that model and perform reasoning on it. It will answer queries about the
* contents of the model, when reasoning is complete.
*/
public interface TBoxReasoner {
/**
* Add the additions and remove the removals.
*/
void updateReasonerModel(TBoxChanges changes);
/**
* Chew on it and create the inferences. Report status.
*/
Status performReasoning();
/**
* List all of the ObjectProperties from the reasoner model, after updating
* and reasoning.
*/
List<ObjectProperty> listObjectProperties();
/**
* List all of the DatatypeProperties from the reasoner model, after
* updating and reasoning.
*/
List<DatatypeProperty> listDatatypeProperties();
/**
* List all of the restrictions in the reasoner model, after updating and
* reasoning.
*/
List<Restriction> listRestrictions();
/**
* List all of the statements that satisfy any of these patterns, after
* updating and reasoning.
*/
List<Statement> filterResults(List<ReasonerStatementPattern> patternList);
public static class Status {
public static final Status SUCCESS = new Status(true, false, "");
public static final Status ERROR = new Status(true, true, "");
public static final Status inconsistent(String explanation) {
return new Status(false, false, explanation);
}
private final boolean consistent;
private final boolean inErrorState;
private final String explanation;
private Status(boolean consistent, boolean inErrorState,
String explanation) {
this.consistent = consistent;
this.inErrorState = inErrorState;
this.explanation = explanation;
}
public boolean isConsistent() {
return consistent;
}
public boolean isInErrorState() {
return inErrorState;
}
public String getExplanation() {
return explanation;
}
}
}

View file

@ -0,0 +1,17 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.tboxreasoner;
import edu.cornell.mannlib.vitro.webapp.modules.tboxreasoner.TBoxReasonerStatus;
/**
* What calls can the ConfiguredReasonerListener make to drive the TBox
* reasoner?
*/
public interface TBoxReasonerDriver {
void runSynchronizer(TBoxChanges changeSet);
TBoxReasonerStatus getStatus();
}

View file

@ -0,0 +1,253 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.tboxreasoner.impl;
import static edu.cornell.mannlib.vitro.webapp.utils.threads.VitroBackgroundThread.WorkLevel.IDLE;
import static edu.cornell.mannlib.vitro.webapp.utils.threads.VitroBackgroundThread.WorkLevel.WORKING;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.Resource;
import edu.cornell.mannlib.vitro.webapp.dao.jena.event.EditEvent;
import edu.cornell.mannlib.vitro.webapp.modules.tboxreasoner.TBoxReasonerStatus;
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.ConfiguredReasonerListener;
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.InferenceModelUpdater;
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.PatternListBuilder;
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.ReasonerConfiguration;
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.ReasonerStatementPattern;
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.TBoxChanges;
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.TBoxReasoner;
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.TBoxReasoner.Status;
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.TBoxReasonerDriver;
import edu.cornell.mannlib.vitro.webapp.utils.jena.criticalsection.LockableModel;
import edu.cornell.mannlib.vitro.webapp.utils.jena.criticalsection.LockableOntModel;
import edu.cornell.mannlib.vitro.webapp.utils.jena.criticalsection.LockedOntModel;
import edu.cornell.mannlib.vitro.webapp.utils.threads.VitroBackgroundThread;
/**
* The basic implementation of the TBoxReasonerDriver. It gets help from a
* listener, an executor, and a reasoner.
*
* Create a listener that listens for changes to the TBox, but filters them
* according to a ReasonerConfiguration object. The listener accumulates the
* changes it likes, until it detects an ending EditEvent. Then it passes the
* change set back to the driver.
*
* Each time a change set is received, a task is created and given to the
* executor to run. The executor is single-threaded, so the change sets are
* processed in sequence.
*
* Processing involves the following steps:
*
* 1. Telling the reasoner about the changes, so it can update its own internal
* ontology model.
*
* 2. Telling the reasoner to re-inference its model. A status is returned.
*
* 3. Asking the reasoner for the inferences from its model. As with the initial
* changes, these inferences are filtered according to the
* ReasonerConfiguration.
*
* 4. Synchronizing the applications TBox inferences model with the inferences
* obtained from the reasoner.
*
* ----------------------
*
* Possible optimization: if change sets come in quickly enough that the third
* set is received while the first is still being processed, it would be
* reasonable to merge the second and third sets into one.
*/
public class BasicTBoxReasonerDriver implements TBoxReasonerDriver {
private static final Log log = LogFactory
.getLog(BasicTBoxReasonerDriver.class);
private final LockableOntModel lockableAssertionsModel;
private final LockableModel lockableInferencesModel;
private final LockableOntModel lockableFullModel;
private final ReasonerConfiguration reasonerConfiguration;
private final ConfiguredReasonerListener listener;
private final Set<TBoxChanges> pendingChangeSets;
private final ExecutorService executorService;
private final TBoxReasoner reasoner;
private TBoxReasoner.Status innerStatus;
public BasicTBoxReasonerDriver(OntModel assertionsModel,
Model inferencesModel, OntModel fullModel, TBoxReasoner reasoner,
ReasonerConfiguration reasonerConfiguration) {
this.lockableAssertionsModel = new LockableOntModel(assertionsModel);
this.lockableInferencesModel = new LockableModel(inferencesModel);
this.lockableFullModel = new LockableOntModel(fullModel);
this.reasoner = reasoner;
this.reasonerConfiguration = reasonerConfiguration;
this.listener = new ConfiguredReasonerListener(reasonerConfiguration,
this);
this.pendingChangeSets = Collections
.synchronizedSet(new HashSet<TBoxChanges>());
this.executorService = Executors.newFixedThreadPool(1,
new VitroBackgroundThread.Factory("TBoxReasoner"));
assertionsModel.getBaseModel().register(listener);
fullModel.getBaseModel().register(listener);
doInitialReasoning();
}
private void doInitialReasoning() {
try (LockedOntModel assertionsModel = lockableAssertionsModel.read()) {
for (ReasonerStatementPattern pat : reasonerConfiguration
.getInferenceDrivingPatternAllowSet()) {
listener.addedStatements(assertionsModel.listStatements(
(Resource) null, pat.getPredicate(), (RDFNode) null));
}
}
listener.notifyEvent(null, new EditEvent(null, false));
}
@Override
public TBoxReasonerStatus getStatus() {
return new FullStatus(innerStatus, !pendingChangeSets.isEmpty());
}
@Override
public void runSynchronizer(TBoxChanges changeSet) {
if (!changeSet.isEmpty()) {
executorService.execute(new ReasoningTask(changeSet));
}
}
/**
* Shut down the thread that runs the reasoning tasks. Don't wait longer
* than 1 minute.
*/
public void shutdown() {
executorService.shutdown();
int waited = 0;
while (waited < 60 && !executorService.isTerminated()) {
try {
log.info("Waiting for TBox reasoner to terminate.");
executorService.awaitTermination(5, TimeUnit.SECONDS);
waited += 5;
} catch (InterruptedException e) {
// Should never happen.
e.printStackTrace();
break;
}
}
if (!executorService.isTerminated()) {
log.warn("Forcing TBox reasoner to terminate.");
executorService.shutdownNow();
}
if (!executorService.isTerminated()) {
log.error("TBox reasoner did not terminate.");
}
}
private class ReasoningTask implements Runnable {
private final TBoxChanges changes;
private List<ReasonerStatementPattern> patternList;
public ReasoningTask(TBoxChanges changes) {
this.changes = changes;
pendingChangeSets.add(changes);
}
@Override
public void run() {
try {
setWorking();
reasoner.updateReasonerModel(changes);
innerStatus = reasoner.performReasoning();
buildPatternList();
updateInferencesModel();
setIdle();
} finally {
pendingChangeSets.remove(changes);
}
}
private void setWorking() {
Thread current = Thread.currentThread();
if (current instanceof VitroBackgroundThread) {
((VitroBackgroundThread) current).setWorkLevel(WORKING);
}
}
private void setIdle() {
Thread current = Thread.currentThread();
if (current instanceof VitroBackgroundThread) {
((VitroBackgroundThread) current).setWorkLevel(IDLE);
}
}
private void buildPatternList() {
PatternListBuilder patternListBuilder = new PatternListBuilder(
reasonerConfiguration, reasoner, changes);
this.patternList = patternListBuilder.build();
}
private void updateInferencesModel() {
InferenceModelUpdater inferenceModelUpdater = new InferenceModelUpdater(
reasoner, lockableInferencesModel, lockableFullModel,
listener);
inferenceModelUpdater.update(patternList);
}
}
private static class FullStatus implements TBoxReasonerStatus {
private final TBoxReasoner.Status reasonerStatus;
private final boolean reasoning;
public FullStatus(Status reasonerStatus, boolean reasoning) {
this.reasonerStatus = reasonerStatus;
this.reasoning = reasoning;
}
@Override
public boolean isReasoning() {
return reasoning;
}
@Override
public boolean isConsistent() {
return reasonerStatus.isConsistent();
}
@Override
public boolean isInErrorState() {
return reasonerStatus.isInErrorState();
}
@Override
public String getExplanation() {
String explanation = reasonerStatus.getExplanation();
return explanation == null ? "" : explanation;
}
}
}

View file

@ -0,0 +1,114 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.tboxreasoner.impl.pellet;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mindswap.pellet.exceptions.InconsistentOntologyException;
import org.mindswap.pellet.jena.PelletInfGraph;
import com.hp.hpl.jena.ontology.DatatypeProperty;
import com.hp.hpl.jena.ontology.ObjectProperty;
import com.hp.hpl.jena.ontology.Restriction;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.vocabulary.OWL;
import com.hp.hpl.jena.vocabulary.RDFS;
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.ReasonerConfiguration;
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.ReasonerStatementPattern;
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.TBoxChanges;
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.TBoxReasoner;
import edu.cornell.mannlib.vitro.webapp.utils.jena.criticalsection.LockableOntModel;
import edu.cornell.mannlib.vitro.webapp.utils.jena.criticalsection.LockedOntModel;
/**
* An implementation the TBoxReasonerWrapper for Pellet.
*/
public class PelletTBoxReasoner implements TBoxReasoner {
private static final Log log = LogFactory
.getLog(PelletTBoxReasoner.class);
private final LockableOntModel lockablePelletModel;
public PelletTBoxReasoner(ReasonerConfiguration reasonerConfiguration) {
this.lockablePelletModel = new LockableOntModel(
ModelFactory.createOntologyModel(reasonerConfiguration
.getOntModelSpec()));
}
@Override
public void updateReasonerModel(TBoxChanges changes) {
try (LockedOntModel pelletModel = lockablePelletModel.write()) {
pelletModel.remove(changes.getRemovedStatements());
pelletModel.add(changes.getAddedStatements());
}
}
@Override
public Status performReasoning() {
try (LockedOntModel pelletModel = lockablePelletModel.write()) {
try {
pelletModel.rebind();
pelletModel.prepare();
return Status.SUCCESS;
} catch (InconsistentOntologyException ioe) {
String explanation = ((PelletInfGraph) pelletModel.getGraph())
.getKB().getExplanation();
log.error(ioe);
log.error(explanation);
return Status.inconsistent(explanation);
} catch (Exception e) {
log.error("Exception during inference", e);
return Status.ERROR;
}
}
}
@Override
public List<ObjectProperty> listObjectProperties() {
try (LockedOntModel pelletModel = lockablePelletModel.read()) {
return pelletModel.listObjectProperties().toList();
}
}
@Override
public List<DatatypeProperty> listDatatypeProperties() {
try (LockedOntModel pelletModel = lockablePelletModel.read()) {
return pelletModel.listDatatypeProperties().toList();
}
}
@Override
public List<Statement> filterResults(
List<ReasonerStatementPattern> patternList) {
List<Statement> filtered = new ArrayList<>();
try (LockedOntModel pelletModel = lockablePelletModel.read()) {
for (ReasonerStatementPattern pattern : patternList) {
filtered.addAll(pattern.matchStatementsFromModel(pelletModel));
}
}
for (Iterator<Statement> fit = filtered.iterator(); fit.hasNext(); ) {
Statement stmt = fit.next();
if (stmt.getObject().equals(RDFS.Resource)) {
fit.remove();
} else if (stmt.getSubject().equals(OWL.Nothing)) {
fit.remove();
} else if (stmt.getObject().equals(OWL.Nothing)) {
fit.remove();
}
}
return filtered;
}
@Override
public List<Restriction> listRestrictions() {
try (LockedOntModel pelletModel = lockablePelletModel.read()) {
return pelletModel.listRestrictions().toList();
}
}
}

View file

@ -0,0 +1,102 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.tboxreasoner.impl.pellet;
import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames.TBOX_ASSERTIONS;
import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames.TBOX_INFERENCES;
import static edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames.TBOX_UNION;
import java.util.List;
import javax.servlet.ServletContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.ontology.Restriction;
import com.hp.hpl.jena.rdf.model.Model;
import edu.cornell.mannlib.vitro.webapp.modelaccess.ContextModelAccess;
import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess;
import edu.cornell.mannlib.vitro.webapp.modules.Application;
import edu.cornell.mannlib.vitro.webapp.modules.ComponentStartupStatus;
import edu.cornell.mannlib.vitro.webapp.modules.tboxreasoner.TBoxReasonerModule;
import edu.cornell.mannlib.vitro.webapp.modules.tboxreasoner.TBoxReasonerStatus;
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.ReasonerConfiguration;
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.impl.BasicTBoxReasonerDriver;
/**
* Configure a Pellet reasoner on the TBox.
*/
public class PelletTBoxReasonerModule implements TBoxReasonerModule {
private static final Log log = LogFactory
.getLog(PelletTBoxReasonerModule.class);
private PelletTBoxReasoner reasoner;
private BasicTBoxReasonerDriver driver;
@Override
public void startup(Application application, ComponentStartupStatus ss) {
ServletContext ctx = application.getServletContext();
ContextModelAccess contextModels = ModelAccess.on(ctx);
OntModel tboxAssertionsModel = contextModels
.getOntModel(TBOX_ASSERTIONS);
Model tboxInferencesModel = contextModels.getOntModel(TBOX_INFERENCES)
.getBaseModel();
OntModel tboxUnionModel = contextModels.getOntModel(TBOX_UNION);
reasoner = new PelletTBoxReasoner(ReasonerConfiguration.DEFAULT);
driver = new BasicTBoxReasonerDriver(tboxAssertionsModel,
tboxInferencesModel, tboxUnionModel, reasoner,
ReasonerConfiguration.DEFAULT);
ss.info("Pellet reasoner connected for the TBox");
waitForTBoxReasoning();
}
@Override
public TBoxReasonerStatus getStatus() {
if (driver == null) {
throw new IllegalStateException(
"PelletTBoxReasonerModule has not been started.");
}
return driver.getStatus();
}
@Override
public List<Restriction> listRestrictions() {
if (reasoner == null) {
throw new IllegalStateException(
"PelletTBoxReasonerModule has not been started.");
}
return reasoner.listRestrictions();
}
@Override
public void shutdown(Application application) {
driver.shutdown();
}
@Override
public void waitForTBoxReasoning() {
int sleeps = 0;
// sleep at least once to make sure the TBox reasoning gets started
while ((0 == sleeps)
|| ((sleeps < 1000) && getStatus().isReasoning())) {
if (((sleeps - 1) % 10) == 0) { // print message at 10 second
// intervals
log.info("Waiting for initial TBox reasoning to complete");
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// This should never happen.
e.printStackTrace();
}
sleeps++;
}
}
}

View file

@ -10,6 +10,8 @@ import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
@ -106,4 +108,25 @@ public class VitroBackgroundThread extends Thread {
return flags; return flags;
} }
} }
/**
* A factory class, for use in Executors, that creates threads with
* successive names.
*/
public static class Factory implements ThreadFactory{
private final String threadName;
private final AtomicInteger index;
public Factory(String threadName) {
this.threadName = threadName;
this.index = new AtomicInteger();
}
@Override
public Thread newThread(Runnable r) {
return new VitroBackgroundThread(r, threadName + "_" + index.getAndIncrement());
}
}
} }

View file

@ -2,11 +2,19 @@
package edu.cornell.mannlib.vitro.webapp.controller.api.sparqlquery; package edu.cornell.mannlib.vitro.webapp.controller.api.sparqlquery;
import static org.junit.Assert.*; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.StringReader; import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@ -25,6 +33,20 @@ import edu.cornell.mannlib.vitro.webapp.utils.http.NotAcceptableException;
* formats. * formats.
*/ */
public class SparqlQueryApiExecutorTest extends AbstractTestClass { public class SparqlQueryApiExecutorTest extends AbstractTestClass {
/* SPARQL response types */
private static final String ACCEPT_TEXT = "text/plain";
private static final String ACCEPT_CSV = "text/csv";
private static final String ACCEPT_TSV = "text/tab-separated-values";
private static final String ACCEPT_SPARQL_XML = "application/sparql-results+xml";
private static final String ACCEPT_SPARQL_JSON = "application/sparql-results+json";
/* RDF result types */
private static final String ACCEPT_RDFXML = "application/rdf+xml";
private static final String ACCEPT_N3 = "text/n3";
private static final String ACCEPT_TURTLE = "text/turtle";
private static final String ACCEPT_JSON = "application/json";
private static final String MODEL_CONTENTS_N3 = "" // private static final String MODEL_CONTENTS_N3 = "" //
+ "<http://here.edu/subject> \n" + "<http://here.edu/subject> \n"
+ " <http://here.edu/predicate> <http://here.edu/object> ." + " <http://here.edu/predicate> <http://here.edu/object> ."
@ -201,24 +223,24 @@ public class SparqlQueryApiExecutorTest extends AbstractTestClass {
@Test(expected = NullPointerException.class) @Test(expected = NullPointerException.class)
public void nullRdfService() throws Exception { public void nullRdfService() throws Exception {
SparqlQueryApiExecutor.instance(null, SELECT_ALL_QUERY, "text/plain"); SparqlQueryApiExecutor.instance(null, SELECT_ALL_QUERY, ACCEPT_TEXT);
} }
@Test(expected = NullPointerException.class) @Test(expected = NullPointerException.class)
public void nullQuery() throws Exception { public void nullQuery() throws Exception {
SparqlQueryApiExecutor.instance(rdfService, null, "text/plain"); SparqlQueryApiExecutor.instance(rdfService, null, ACCEPT_TEXT);
fail("nullQuery not implemented"); fail("nullQuery not implemented");
} }
@Test(expected = QueryParseException.class) @Test(expected = QueryParseException.class)
public void emptyQuery() throws Exception { public void emptyQuery() throws Exception {
SparqlQueryApiExecutor.instance(rdfService, "", "text/plain"); SparqlQueryApiExecutor.instance(rdfService, "", ACCEPT_TEXT);
fail("emptyQuery not implemented"); fail("emptyQuery not implemented");
} }
@Test(expected = QueryParseException.class) @Test(expected = QueryParseException.class)
public void cantParseQuery() throws Exception { public void cantParseQuery() throws Exception {
SparqlQueryApiExecutor.instance(rdfService, "BOGUS", "text/plain"); SparqlQueryApiExecutor.instance(rdfService, "BOGUS", ACCEPT_TEXT);
fail("cantParseQuery not implemented"); fail("cantParseQuery not implemented");
} }
@ -237,179 +259,177 @@ public class SparqlQueryApiExecutorTest extends AbstractTestClass {
@Test @Test
public void selectToText() throws Exception { public void selectToText() throws Exception {
executeQuery("select to text", SELECT_ALL_QUERY, "text/plain", executeQuery("select to text", SELECT_ALL_QUERY, ACCEPT_TEXT,
SELECT_RESULT_TEXT); SELECT_RESULT_TEXT);
} }
@Test @Test
public void selectToCsv() throws Exception { public void selectToCsv() throws Exception {
executeQuery("select to csv", SELECT_ALL_QUERY, "text/csv", executeQuery("select to csv", SELECT_ALL_QUERY, ACCEPT_CSV,
SELECT_RESULT_CSV); SELECT_RESULT_CSV);
} }
@Test @Test
public void selectToTsv() throws Exception { public void selectToTsv() throws Exception {
executeQuery("select to tsv", SELECT_ALL_QUERY, executeQuery("select to tsv", SELECT_ALL_QUERY, ACCEPT_TSV,
"text/tab-separated-values", SELECT_RESULT_TSV); SELECT_RESULT_TSV);
} }
@Test @Test
public void selectToXml() throws Exception { public void selectToXml() throws Exception {
executeQuery("select to xml", SELECT_ALL_QUERY, executeQuery("select to xml", SELECT_ALL_QUERY, ACCEPT_SPARQL_XML,
"application/sparql-results+xml", SELECT_RESULT_XML); SELECT_RESULT_XML);
} }
@Test @Test
public void selectToJson() throws Exception { public void selectToJson() throws Exception {
executeQuery("select to json", SELECT_ALL_QUERY, executeQuery("select to json", SELECT_ALL_QUERY, ACCEPT_SPARQL_JSON,
"application/sparql-results+json", SELECT_RESULT_JSON); SELECT_RESULT_JSON);
} }
@Test @Test
public void selectWithInvalidContentType() throws Exception { public void selectWithInvalidContentType() throws Exception {
executeWithInvalidAcceptHeader("select with application/rdf+xml", executeWithInvalidAcceptHeader("select with application/rdf+xml",
SELECT_ALL_QUERY, "application/rdf+xml"); SELECT_ALL_QUERY, ACCEPT_RDFXML);
executeWithInvalidAcceptHeader("select with text/n3", SELECT_ALL_QUERY, executeWithInvalidAcceptHeader("select with text/n3", SELECT_ALL_QUERY,
"text/n3"); ACCEPT_N3);
executeWithInvalidAcceptHeader("select with text/turtle", executeWithInvalidAcceptHeader("select with text/turtle",
SELECT_ALL_QUERY, "text/turtle"); SELECT_ALL_QUERY, ACCEPT_TURTLE);
executeWithInvalidAcceptHeader("select with application/json", executeWithInvalidAcceptHeader("select with application/json",
SELECT_ALL_QUERY, "application/json"); SELECT_ALL_QUERY, ACCEPT_JSON);
} }
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
@Test @Test
public void askToText() throws Exception { public void askToText() throws Exception {
executeQuery("ask to text", ASK_ALL_QUERY, "text/plain", executeQuery("ask to text", ASK_ALL_QUERY, ACCEPT_TEXT, ASK_RESULT_TEXT);
ASK_RESULT_TEXT);
} }
@Test @Test
public void askToCsv() throws Exception { public void askToCsv() throws Exception {
executeQuery("ask to csv", ASK_ALL_QUERY, "text/csv", ASK_RESULT_CSV); executeQuery("ask to csv", ASK_ALL_QUERY, ACCEPT_CSV, ASK_RESULT_CSV);
} }
@Test @Test
public void askToTsv() throws Exception { public void askToTsv() throws Exception {
executeQuery("ask to tsv", ASK_ALL_QUERY, "text/tab-separated-values", executeQuery("ask to tsv", ASK_ALL_QUERY, ACCEPT_TSV, ASK_RESULT_TSV);
ASK_RESULT_TSV);
} }
@Test @Test
public void askToXml() throws Exception { public void askToXml() throws Exception {
executeQuery("ask to xml", ASK_ALL_QUERY, executeQuery("ask to xml", ASK_ALL_QUERY, ACCEPT_SPARQL_XML,
"application/sparql-results+xml", ASK_RESULT_XML); ASK_RESULT_XML);
} }
@Test @Test
public void askToJson() throws Exception { public void askToJson() throws Exception {
executeQuery("ask to json", ASK_ALL_QUERY, executeQuery("ask to json", ASK_ALL_QUERY, ACCEPT_SPARQL_JSON,
"application/sparql-results+json", ASK_RESULT_JSON); ASK_RESULT_JSON);
} }
@Test @Test
public void askWithInvalidAcceptHeader() throws Exception { public void askWithInvalidAcceptHeader() throws Exception {
executeWithInvalidAcceptHeader("ask with application/rdf+xml", executeWithInvalidAcceptHeader("ask with application/rdf+xml",
ASK_ALL_QUERY, "application/rdf+xml"); ASK_ALL_QUERY, ACCEPT_RDFXML);
executeWithInvalidAcceptHeader("ask with text/n3", ASK_ALL_QUERY, executeWithInvalidAcceptHeader("ask with text/n3", ASK_ALL_QUERY,
"text/n3"); ACCEPT_N3);
executeWithInvalidAcceptHeader("ask with text/turtle", ASK_ALL_QUERY, executeWithInvalidAcceptHeader("ask with text/turtle", ASK_ALL_QUERY,
"text/turtle"); ACCEPT_TURTLE);
executeWithInvalidAcceptHeader("ask with application/json", executeWithInvalidAcceptHeader("ask with application/json",
ASK_ALL_QUERY, "application/json"); ASK_ALL_QUERY, ACCEPT_JSON);
} }
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
@Test @Test
public void constructToText() throws Exception { public void constructToText() throws Exception {
executeQuery("construct to text", CONSTRUCT_ALL_QUERY, "text/plain", executeQuery("construct to text", CONSTRUCT_ALL_QUERY, ACCEPT_TEXT,
CONSTRUCT_RESULT_TEXT); CONSTRUCT_RESULT_TEXT);
} }
@Test @Test
public void constructToRdfXml() throws Exception { public void constructToRdfXml() throws Exception {
executeQuery("construct to rdf/xml", CONSTRUCT_ALL_QUERY, executeQuery("construct to rdf/xml", CONSTRUCT_ALL_QUERY,
"application/rdf+xml", CONSTRUCT_RESULT_RDFXML); ACCEPT_RDFXML, CONSTRUCT_RESULT_RDFXML);
} }
@Test @Test
public void constructToN3() throws Exception { public void constructToN3() throws Exception {
executeQuery("construct to n3", CONSTRUCT_ALL_QUERY, "text/n3", executeQuery("construct to n3", CONSTRUCT_ALL_QUERY, ACCEPT_N3,
CONSTRUCT_RESULT_N3); CONSTRUCT_RESULT_N3);
} }
@Test @Test
public void constructToTurtle() throws Exception { public void constructToTurtle() throws Exception {
executeQuery("construct to turtle", CONSTRUCT_ALL_QUERY, "text/turtle", executeQuery("construct to turtle", CONSTRUCT_ALL_QUERY, ACCEPT_TURTLE,
CONSTRUCT_RESULT_TURTLE); CONSTRUCT_RESULT_TURTLE);
} }
@Test @Test
public void constructToJsonld() throws Exception { public void constructToJsonld() throws Exception {
executeQuery("construct to JSON-LD", CONSTRUCT_ALL_QUERY, executeQuery("construct to JSON-LD", CONSTRUCT_ALL_QUERY, ACCEPT_JSON,
"application/json", CONSTRUCT_RESULT_JSONLD); CONSTRUCT_RESULT_JSONLD);
} }
@Test @Test
public void constructWithInvalidAcceptHeader() throws Exception { public void constructWithInvalidAcceptHeader() throws Exception {
executeWithInvalidAcceptHeader("construct with text/csv", executeWithInvalidAcceptHeader("construct with text/csv",
CONSTRUCT_ALL_QUERY, "text/csv"); CONSTRUCT_ALL_QUERY, ACCEPT_CSV);
executeWithInvalidAcceptHeader("construct with text/tsv", executeWithInvalidAcceptHeader("construct with text/tsv",
CONSTRUCT_ALL_QUERY, "text/tsv"); CONSTRUCT_ALL_QUERY, "text/tsv");
executeWithInvalidAcceptHeader( executeWithInvalidAcceptHeader(
"construct with application/sparql-results+xml", "construct with application/sparql-results+xml",
CONSTRUCT_ALL_QUERY, "application/sparql-results+xml"); CONSTRUCT_ALL_QUERY, ACCEPT_SPARQL_XML);
executeWithInvalidAcceptHeader( executeWithInvalidAcceptHeader(
"construct with application/sparql-results+json", "construct with application/sparql-results+json",
CONSTRUCT_ALL_QUERY, "application/sparql-results+json"); CONSTRUCT_ALL_QUERY, ACCEPT_SPARQL_JSON);
} }
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
@Test @Test
public void describeToText() throws Exception { public void describeToText() throws Exception {
executeQuery("describe to text", DESCRIBE_ALL_QUERY, "text/plain", executeQuery("describe to text", DESCRIBE_ALL_QUERY, ACCEPT_TEXT,
DESCRIBE_RESULT_TEXT); DESCRIBE_RESULT_TEXT);
} }
@Test @Test
public void describeToRdfXml() throws Exception { public void describeToRdfXml() throws Exception {
executeQuery("describe to rdf/xml", DESCRIBE_ALL_QUERY, executeQuery("describe to rdf/xml", DESCRIBE_ALL_QUERY, ACCEPT_RDFXML,
"application/rdf+xml", DESCRIBE_RESULT_RDFXML); DESCRIBE_RESULT_RDFXML);
} }
@Test @Test
public void describeToN3() throws Exception { public void describeToN3() throws Exception {
executeQuery("describe to n3", DESCRIBE_ALL_QUERY, "text/n3", executeQuery("describe to n3", DESCRIBE_ALL_QUERY, ACCEPT_N3,
DESCRIBE_RESULT_N3); DESCRIBE_RESULT_N3);
} }
@Test @Test
public void describeToTurtle() throws Exception { public void describeToTurtle() throws Exception {
executeQuery("describe to turtle", DESCRIBE_ALL_QUERY, "text/turtle", executeQuery("describe to turtle", DESCRIBE_ALL_QUERY, ACCEPT_TURTLE,
DESCRIBE_RESULT_TURTLE); DESCRIBE_RESULT_TURTLE);
} }
@Test @Test
public void describeToJsonld() throws Exception { public void describeToJsonld() throws Exception {
executeQuery("describe to JSON-LD", DESCRIBE_ALL_QUERY, executeQuery("describe to JSON-LD", DESCRIBE_ALL_QUERY, ACCEPT_JSON,
"application/json", DESCRIBE_RESULT_JSONLD); DESCRIBE_RESULT_JSONLD);
} }
@Test @Test
public void describeWithInvalidAcceptHeader() throws Exception { public void describeWithInvalidAcceptHeader() throws Exception {
executeWithInvalidAcceptHeader("describe with text/csv", executeWithInvalidAcceptHeader("describe with text/csv",
DESCRIBE_ALL_QUERY, "text/csv"); DESCRIBE_ALL_QUERY, ACCEPT_CSV);
executeWithInvalidAcceptHeader("describe with text/tsv", executeWithInvalidAcceptHeader("describe with text/tsv",
DESCRIBE_ALL_QUERY, "text/tsv"); DESCRIBE_ALL_QUERY, "text/tsv");
executeWithInvalidAcceptHeader( executeWithInvalidAcceptHeader(
"describe with application/sparql-results+xml", "describe with application/sparql-results+xml",
DESCRIBE_ALL_QUERY, "application/sparql-results+xml"); DESCRIBE_ALL_QUERY, ACCEPT_SPARQL_XML);
executeWithInvalidAcceptHeader( executeWithInvalidAcceptHeader(
"describe with application/sparql-results+json", "describe with application/sparql-results+json",
DESCRIBE_ALL_QUERY, "application/sparql-results+json"); DESCRIBE_ALL_QUERY, ACCEPT_SPARQL_JSON);
} }
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
@ -424,7 +444,87 @@ public class SparqlQueryApiExecutorTest extends AbstractTestClass {
rdfService, queryString, acceptHeader); rdfService, queryString, acceptHeader);
executor.executeAndFormat(out); executor.executeAndFormat(out);
assertEquals(message, expected.replaceAll("\\s+", " "), out.toString().replaceAll("\\s+", " ")); if (ACCEPT_RDFXML.equals(acceptHeader)) {
assertEquivalentRdfxml(message, expected, out.toString());
} else if (ACCEPT_TURTLE.equals(acceptHeader)) {
assertEquivalentTurtle(message, expected, out.toString());
} else if (ACCEPT_N3.equals(acceptHeader)) {
assertEquivalentN3(message, expected, out.toString());
} else {
assertEqualsIgnoreWhiteSpace(message, expected, out.toString());
}
}
/**
* RDF/XML namespaces may come in any order, so separate them out and test
* accordingly.
*/
private void assertEquivalentRdfxml(String message, String expected,
String actual) {
assertEquals(message, getRdfxmlNamespaces(expected),
getRdfxmlNamespaces(actual));
assertEqualsIgnoreWhiteSpace(message, omitRdfxmlNamespaces(expected),
omitRdfxmlNamespaces(actual));
}
private Set<String> getRdfxmlNamespaces(String rdfxml) {
Set<String> namespaces = new TreeSet<>();
Pattern p = Pattern.compile("xmlns:\\w+=\\\"[^\\\"]*\\\"");
Matcher m = p.matcher(rdfxml);
while (m.find()) {
namespaces.add(m.group());
}
return namespaces;
}
private String omitRdfxmlNamespaces(String rdfxml) {
return rdfxml.replaceAll("xmlns:\\w+=\\\"[^\\\"]*\\\"", "");
}
/**
* TTL prefix lines may come in any order, so separate them out and test
* accordingly.
*/
private void assertEquivalentTurtle(String message, String expected,
String actual) {
assertEquals(message, getTurtlePrefixes(expected),
getTurtlePrefixes(actual));
assertEqualsIgnoreWhiteSpace(message, getTurtleRemainder(expected),
getTurtleRemainder(actual));
}
/**
* N3 is like TTL, as far as prefix lines are concerned.
*/
private void assertEquivalentN3(String message, String expected,
String actual) {
assertEquivalentTurtle(message, expected, actual);
}
private Set<String> getTurtlePrefixes(String ttl) {
Set<String> prefixes = new TreeSet<>();
for (String line : ttl.split("[\\n\\r]+")) {
if (line.startsWith("@prefix")) {
prefixes.add(line.replaceAll("\\s+", " "));
}
}
return prefixes;
}
private String getTurtleRemainder(String ttl) {
List<String> remainder = new ArrayList<>();
for (String line : ttl.split("[\\n\\r]+")) {
if (!line.startsWith("@prefix")) {
remainder.add(line);
}
}
return StringUtils.join(remainder, "\n");
}
private void assertEqualsIgnoreWhiteSpace(String message, String expected,
String actual) {
assertEquals(message, expected.replaceAll("\\s+", " "),
actual.replaceAll("\\s+", " "));
} }
private void executeWithInvalidAcceptHeader(String message, private void executeWithInvalidAcceptHeader(String message,

View file

@ -970,7 +970,7 @@ public class ConfigurationBeanLoaderTest extends AbstractTestClass {
// Nothing. // Nothing.
} }
public class AGoodApple implements OneBadAppleSpoilsTheBunch { public static class AGoodApple implements OneBadAppleSpoilsTheBunch {
// Nothing // Nothing
} }

View file

@ -10,10 +10,10 @@ import java.io.StringReader;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import org.hamcrest.CoreMatchers;
import org.hamcrest.Matcher; import org.hamcrest.Matcher;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.matchers.JUnitMatchers;
import org.junit.rules.ExpectedException; import org.junit.rules.ExpectedException;
import edu.cornell.mannlib.vitro.testing.AbstractTestClass; import edu.cornell.mannlib.vitro.testing.AbstractTestClass;
@ -71,7 +71,7 @@ public class CustomListViewConfigFileTest extends AbstractTestClass {
@Test @Test
public void invalidXml() throws InvalidConfigurationException { public void invalidXml() throws InvalidConfigurationException {
suppressSyserr(); // catch the error report from the XML parser suppressSyserr(); // catch the error report from the XML parser
expectException(JUnitMatchers expectException(CoreMatchers
.containsString("Config file is not valid XML:")); .containsString("Config file is not valid XML:"));
readConfigFile("<unbalancedTag>"); readConfigFile("<unbalancedTag>");
} }

View file

@ -12,6 +12,7 @@ import edu.cornell.mannlib.vitro.webapp.modules.Application;
import edu.cornell.mannlib.vitro.webapp.modules.fileStorage.FileStorage; import edu.cornell.mannlib.vitro.webapp.modules.fileStorage.FileStorage;
import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor; import edu.cornell.mannlib.vitro.webapp.modules.imageProcessor.ImageProcessor;
import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine; import edu.cornell.mannlib.vitro.webapp.modules.searchEngine.SearchEngine;
import edu.cornell.mannlib.vitro.webapp.modules.tboxreasoner.TBoxReasonerModule;
import edu.cornell.mannlib.vitro.webapp.modules.tripleSource.ConfigurationTripleSource; import edu.cornell.mannlib.vitro.webapp.modules.tripleSource.ConfigurationTripleSource;
import edu.cornell.mannlib.vitro.webapp.modules.tripleSource.ContentTripleSource; import edu.cornell.mannlib.vitro.webapp.modules.tripleSource.ContentTripleSource;
@ -101,4 +102,11 @@ public class ApplicationStub implements Application {
"ApplicationStub.getConfigurationTripleSource() not implemented."); "ApplicationStub.getConfigurationTripleSource() not implemented.");
} }
@Override
public TBoxReasonerModule getTBoxReasonerModule() {
// TODO Auto-generated method stub
throw new RuntimeException("ApplicationStub.getTBoxReasonerModule() not implemented.");
}
} }

View file

@ -34,6 +34,7 @@ edu.cornell.mannlib.vitro.webapp.servlet.setup.RemoveObsoletePermissions
edu.cornell.mannlib.vitro.webapp.servlet.setup.FileGraphSetup edu.cornell.mannlib.vitro.webapp.servlet.setup.FileGraphSetup
edu.cornell.mannlib.vitro.webapp.application.ApplicationImpl$ReasonersSetup
edu.cornell.mannlib.vitro.webapp.servlet.setup.SimpleReasonerSetup edu.cornell.mannlib.vitro.webapp.servlet.setup.SimpleReasonerSetup
#edu.cornell.mannlib.vitro.webapp.servlet.setup.UpdateKnowledgeBase #edu.cornell.mannlib.vitro.webapp.servlet.setup.UpdateKnowledgeBase

View file

@ -6,11 +6,11 @@
<section class="pageBodyGroup" role="region"> <section class="pageBodyGroup" role="region">
<h3>${i18n().ontology_editor}</h3> <h3>${i18n().ontology_editor}</h3>
<#if ontologyEditor.pellet?has_content> <#if ontologyEditor.tboxReasonerStatus?has_content>
<div class="notice"> <div class="notice">
<p>${ontologyEditor.pellet.error}</p> <p>${ontologyEditor.tboxReasonerStatus.error}</p>
<#if ontologyEditor.pellet.explanation?has_content> <#if ontologyEditor.tboxReasonerStatus.explanation?has_content>
<p>${i18n().cause} ${ontologyEditor.pellet.explanation}</p> <p>${i18n().cause} ${ontologyEditor.tboxReasonerStatus.explanation}</p>
</#if> </#if>
</div> </div>
</#if> </#if>