VIVO-778 Restructure the driver and connect the plumbing properly.
Replace PelletListener with BasicTBoxReasonerDriver, and associated classes. Run the background thread from an ExecutorService. Since nobody was using the FOREGROUND flag, and since its logic was flawed, skip it. Make TBoxReasonerModule a module on the Application. Make the reasoner status available through the module. Initialize it in startup_listeners.txt Create a concrete class for PelletTBoxReasonerModule, and configure it in applicationSetup.n3 Get rid of PelletReasonerSetup. Make it so the ExecutorService in BasicTBoxReasonerDriver uses a VitroBackgroundThread.,
This commit is contained in:
commit
1c66271101
27 changed files with 3610 additions and 570 deletions
2543
webapp/rdf/tbox/savedInferences.n3
Normal file
2543
webapp/rdf/tbox/savedInferences.n3
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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.imageProcessor.ImageProcessor;
|
||||
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.ContentTripleSource;
|
||||
import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.RDFServiceUtils;
|
||||
|
@ -41,6 +42,7 @@ public class ApplicationImpl implements Application {
|
|||
private FileStorage fileStorage;
|
||||
private ContentTripleSource contentTripleSource;
|
||||
private ConfigurationTripleSource configurationTripleSource;
|
||||
private TBoxReasonerModule tboxReasonerModule;
|
||||
|
||||
public void setServletContext(ServletContext 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
|
||||
public void validate() throws Exception {
|
||||
if (searchEngine == null) {
|
||||
|
@ -162,6 +180,10 @@ public class ApplicationImpl implements Application {
|
|||
throw new IllegalStateException(
|
||||
"Configuration did not include a ConfigurationTripleSource.");
|
||||
}
|
||||
if (tboxReasonerModule == null) {
|
||||
throw new IllegalStateException(
|
||||
"Configuration did not include a TBoxReasonerModule.");
|
||||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.apache.commons.logging.LogFactory;
|
|||
|
||||
import edu.cornell.mannlib.vedit.beans.Option;
|
||||
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.policy.PolicyHelper;
|
||||
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.TemplateResponseValues;
|
||||
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.startup.StartupStatus;
|
||||
|
||||
|
@ -159,26 +160,23 @@ public class BaseSiteAdminController extends FreemarkerHttpServlet {
|
|||
|
||||
if (PolicyHelper.isAuthorizedForActions(vreq, SimplePermission.EDIT_ONTOLOGY.ACTION)) {
|
||||
|
||||
String pelletError = null;
|
||||
String pelletExplanation = null;
|
||||
Object plObj = getServletContext().getAttribute("pelletListener");
|
||||
if ( (plObj != null) && (plObj instanceof PelletListener) ) {
|
||||
PelletListener pelletListener = (PelletListener) plObj;
|
||||
if (!pelletListener.isConsistent()) {
|
||||
pelletError = "INCONSISTENT ONTOLOGY: reasoning halted.";
|
||||
pelletExplanation = pelletListener.getExplanation();
|
||||
} else if ( pelletListener.isInErrorState() ) {
|
||||
pelletError = "An error occurred during reasoning. Reasoning has been halted. See error log for details.";
|
||||
}
|
||||
String error = null;
|
||||
String explanation = null;
|
||||
TBoxReasonerStatus status = ApplicationUtils.instance().getTBoxReasonerModule().getStatus();
|
||||
if (!status.isConsistent()) {
|
||||
error = "INCONSISTENT ONTOLOGY: reasoning halted.";
|
||||
explanation = status.getExplanation();
|
||||
} else if ( status.isInErrorState() ) {
|
||||
error = "An error occurred during reasoning. Reasoning has been halted. See error log for details.";
|
||||
}
|
||||
|
||||
if (pelletError != null) {
|
||||
Map<String, String> pellet = new HashMap<String, String>();
|
||||
pellet.put("error", pelletError);
|
||||
if (pelletExplanation != null) {
|
||||
pellet.put("explanation", pelletExplanation);
|
||||
if (error != null) {
|
||||
Map<String, String> tboxReasonerStatus = new HashMap<String, String>();
|
||||
tboxReasonerStatus.put("error", error);
|
||||
if (explanation != null) {
|
||||
tboxReasonerStatus.put("explanation", explanation);
|
||||
}
|
||||
map.put("pellet", pellet);
|
||||
map.put("tboxReasonerStatus", tboxReasonerStatus);
|
||||
}
|
||||
|
||||
Map<String, String> urls = new HashMap<String, String>();
|
||||
|
|
|
@ -44,10 +44,12 @@ import com.hp.hpl.jena.vocabulary.RDF;
|
|||
import com.hp.hpl.jena.vocabulary.RDFS;
|
||||
|
||||
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.controller.VitroRequest;
|
||||
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
|
||||
import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess;
|
||||
import edu.cornell.mannlib.vitro.webapp.modules.tboxreasoner.TBoxReasonerModule;
|
||||
|
||||
public class JenaAdminActions extends BaseEditController {
|
||||
|
||||
|
@ -190,9 +192,8 @@ public class JenaAdminActions extends BaseEditController {
|
|||
}
|
||||
|
||||
private void printRestrictions() {
|
||||
OntModel memoryModel = (OntModel) getServletContext().getAttribute("pelletOntModel");
|
||||
for (Restriction rest : memoryModel.listRestrictions().toList() ) {
|
||||
//System.out.println();
|
||||
TBoxReasonerModule reasoner = ApplicationUtils.instance().getTBoxReasonerModule();
|
||||
for (Restriction rest : reasoner.listRestrictions() ) {
|
||||
if (rest.isAllValuesFromRestriction()) {
|
||||
log.trace("All values from: ");
|
||||
AllValuesFromRestriction avfr = rest.asAllValuesFromRestriction();
|
||||
|
|
|
@ -41,6 +41,7 @@ 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.application.ApplicationUtils;
|
||||
import edu.cornell.mannlib.vitro.webapp.beans.BaseResourceBean;
|
||||
import edu.cornell.mannlib.vitro.webapp.beans.DataProperty;
|
||||
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.VitroVocabulary;
|
||||
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;
|
||||
|
||||
public class DataPropertyDaoJena extends PropertyDaoJena implements
|
||||
|
@ -357,10 +358,8 @@ public class DataPropertyDaoJena extends PropertyDaoJena implements
|
|||
}
|
||||
|
||||
protected boolean reasoningAvailable() {
|
||||
PelletListener pl = getWebappDaoFactory().getPelletListener();
|
||||
return !(
|
||||
( pl == null || !pl.isConsistent() || pl.isInErrorState() )
|
||||
);
|
||||
TBoxReasonerStatus status = ApplicationUtils.instance().getTBoxReasonerModule().getStatus();
|
||||
return status.isConsistent() && !status.isInErrorState();
|
||||
}
|
||||
|
||||
private String getRequiredDatatypeURI(Individual individual, DataProperty dataprop, List<String> vclassURIs) {
|
||||
|
|
|
@ -18,7 +18,6 @@ import org.apache.commons.logging.Log;
|
|||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.jena.iri.IRI;
|
||||
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.graph.Node;
|
||||
|
@ -55,6 +54,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_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());
|
||||
|
||||
/* ******************* static constants ****************** */
|
||||
|
@ -1090,7 +1091,7 @@ public class JenaBaseDao extends JenaBaseDaoCon {
|
|||
}
|
||||
|
||||
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()) {
|
||||
Resource imp = impIt.next();
|
||||
boolean removeMe = false;
|
||||
|
|
|
@ -47,7 +47,6 @@ import edu.cornell.mannlib.vitro.webapp.dao.VClassGroupDao;
|
|||
import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary;
|
||||
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
|
||||
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.rdfservice.RDFService;
|
||||
import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.jena.model.RDFServiceModel;
|
||||
|
@ -70,8 +69,6 @@ public class WebappDaoFactoryJena implements WebappDaoFactory {
|
|||
|
||||
protected WebappDaoFactoryConfig config;
|
||||
|
||||
protected PelletListener pelletListener;
|
||||
|
||||
protected String userURI;
|
||||
|
||||
private Map<String,String> properties = new HashMap<String,String>();
|
||||
|
@ -238,18 +235,6 @@ public class WebappDaoFactoryJena implements WebappDaoFactory {
|
|||
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
|
||||
public List<String> getCommentsForResource(String resourceURI) {
|
||||
List<String> commentList = new LinkedList<String>();
|
||||
|
|
|
@ -1,400 +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.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
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.Model;
|
||||
import com.hp.hpl.jena.rdf.model.ModelFactory;
|
||||
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.shared.Lock;
|
||||
import com.hp.hpl.jena.util.iterator.ClosableIterator;
|
||||
import com.hp.hpl.jena.vocabulary.OWL;
|
||||
import com.hp.hpl.jena.vocabulary.RDFS;
|
||||
|
||||
import edu.cornell.mannlib.vitro.webapp.dao.jena.event.EditEvent;
|
||||
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.ConfiguredReasonerListener;
|
||||
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.ConfiguredReasonerListener.Suspension;
|
||||
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.TBoxReasonerDriver;
|
||||
|
||||
public class PelletListener implements TBoxReasonerDriver {
|
||||
|
||||
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<ReasonerStatementPattern> inferenceDrivingPatternAllowSet;
|
||||
private Set<ReasonerStatementPattern> inferenceDrivingPatternDenySet;
|
||||
private Set<ReasonerStatementPattern> inferenceReceivingPatternAllowSet;
|
||||
|
||||
private final ConfiguredReasonerListener listener;
|
||||
|
||||
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 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 final List<TBoxChanges> pendingChangeSets = Collections.synchronizedList(new ArrayList<TBoxChanges>());
|
||||
|
||||
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();
|
||||
|
||||
listener = new ConfiguredReasonerListener(reasonerConfiguration, this);
|
||||
|
||||
this.mainModel.enterCriticalSection(Lock.READ);
|
||||
try {
|
||||
for (ReasonerStatementPattern pat : this.inferenceDrivingPatternAllowSet) {
|
||||
listener.addedStatements(mainModel.listStatements((Resource) null, pat.getPredicate(), (RDFNode) null));
|
||||
}
|
||||
if (!skipReasoningUponInitialization) {
|
||||
this.foreground = foreground;
|
||||
listener.notifyEvent(null,new EditEvent(null,false));
|
||||
} else if (inferenceModel.size() == 0){
|
||||
foreground = true;
|
||||
listener.notifyEvent(null,new EditEvent(null,false));
|
||||
this.foreground = foreground;
|
||||
}
|
||||
} finally {
|
||||
this.mainModel.leaveCriticalSection();
|
||||
}
|
||||
|
||||
this.fullModel.getBaseModel().register(listener);
|
||||
this.mainModel.getBaseModel().register(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runSynchronizer(TBoxChanges changeSet) {
|
||||
if (!changeSet.isEmpty()) {
|
||||
pendingChangeSets.add(changeSet);
|
||||
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 InferenceGetter implements Runnable {
|
||||
|
||||
private PelletListener pelletListener;
|
||||
private TBoxChanges changeSet;
|
||||
|
||||
public InferenceGetter(PelletListener pelletListener, TBoxChanges changeSet) {
|
||||
this.pelletListener = pelletListener;
|
||||
this.changeSet = changeSet;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
while (pelletListener.isDirty()) {
|
||||
//pipeOpen = false;
|
||||
try {
|
||||
pelletListener.setDirty(false);
|
||||
inferenceRounds++;
|
||||
log.info("Getting new inferences");
|
||||
long startTime = System.currentTimeMillis();
|
||||
LinkedList<ReasonerStatementPattern> irpl = new LinkedList<>();
|
||||
|
||||
if (inferenceReceivingPatternAllowSet != null) {
|
||||
irpl.addAll(inferenceReceivingPatternAllowSet);
|
||||
} else {
|
||||
irpl.add(ReasonerStatementPattern.ANY_OBJECT_PROPERTY);
|
||||
}
|
||||
|
||||
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(ReasonerStatementPattern.objectPattern(objProp));
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
closeIt.close();
|
||||
}
|
||||
} finally {
|
||||
pelletModel.leaveCriticalSection();
|
||||
}
|
||||
for (String uri: changeSet.getDeletedObjectPropertyUris()) {
|
||||
irpl.add(ReasonerStatementPattern.objectPattern(ResourceFactory.createProperty(uri)));
|
||||
}
|
||||
}
|
||||
|
||||
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(ReasonerStatementPattern.objectPattern(dataProp));
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
closeIt.close();
|
||||
}
|
||||
} finally {
|
||||
pelletModel.leaveCriticalSection();
|
||||
}
|
||||
for (String uri: changeSet.getDeletedDataPropertyUris()) {
|
||||
irpl.add(ReasonerStatementPattern.objectPattern(ResourceFactory.createProperty(uri)));
|
||||
}
|
||||
}
|
||||
|
||||
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<ReasonerStatementPattern> patIt = irpl.iterator(); patIt.hasNext(); ) {
|
||||
ReasonerStatementPattern pat = patIt.next();
|
||||
log.debug("Querying for "+pat);
|
||||
|
||||
Model tempModel = ModelFactory.createDefaultModel();
|
||||
|
||||
pelletModel.enterCriticalSection(Lock.READ);
|
||||
try {
|
||||
for(Statement stmt : pat.matchStatementsFromModel(pelletModel)) {
|
||||
|
||||
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);
|
||||
try (Suspension susp = listener.suspend()) {
|
||||
inferenceModel.add(stmt);
|
||||
addCount++;
|
||||
} finally {
|
||||
fullModel.leaveCriticalSection();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} 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>();
|
||||
for (Statement stmt : pat.matchStatementsFromModel(inferenceModel)) {
|
||||
if (!tempModel.contains(stmt)) {
|
||||
localRemovalQueue.add(stmt);
|
||||
}
|
||||
}
|
||||
for (Iterator<Statement> i = localRemovalQueue.iterator(); i.hasNext(); ) {
|
||||
fullModel.enterCriticalSection(Lock.WRITE);
|
||||
try (Suspension susp = listener.suspend()) {
|
||||
retractCount++;
|
||||
inferenceModel.remove(i.next());
|
||||
} finally {
|
||||
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(TBoxChanges changeSet) {
|
||||
this.setDirty(true);
|
||||
if ( this.checkAndStartReasoning() ){
|
||||
if (foreground) {
|
||||
(new InferenceGetter(this, changeSet)).run();
|
||||
} else {
|
||||
new Thread(new InferenceGetter(this, changeSet), "PelletListener.InferenceGetter").start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class PelletSynchronizer implements Runnable {
|
||||
public void run() {
|
||||
try {
|
||||
isSynchronizing = true;
|
||||
while (!pendingChangeSets.isEmpty()) {
|
||||
TBoxChanges changeSet = pendingChangeSets.remove(0);
|
||||
|
||||
pelletModel.enterCriticalSection(Lock.WRITE);
|
||||
try {
|
||||
pelletModel.remove(changeSet.getRemovedStatements());
|
||||
pelletModel.add(changeSet.getAddedStatements());
|
||||
} finally {
|
||||
pelletModel.leaveCriticalSection();
|
||||
}
|
||||
|
||||
getInferences(changeSet);
|
||||
|
||||
}
|
||||
} finally {
|
||||
isSynchronizing = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public OntModel getPelletModel() {
|
||||
return this.pelletModel;
|
||||
}
|
||||
|
||||
}
|
|
@ -8,12 +8,12 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
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.dao.IndividualDao;
|
||||
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.modules.tboxreasoner.TBoxReasonerStatus;
|
||||
|
||||
public class IndividualsViaVClassOptions implements FieldOptions {
|
||||
|
||||
|
@ -101,20 +101,13 @@ public class IndividualsViaVClassOptions implements FieldOptions {
|
|||
return individualMap;
|
||||
}
|
||||
|
||||
protected boolean isReasoningAvailable( WebappDaoFactory wDaoFact){
|
||||
boolean inferenceAvailable = false;
|
||||
if (wDaoFact instanceof WebappDaoFactoryJena) {
|
||||
PelletListener pl = ((WebappDaoFactoryJena) wDaoFact).getPelletListener();
|
||||
if (pl != null && pl.isConsistent() && !pl.isInErrorState()
|
||||
&& !pl.isReasoning()) {
|
||||
inferenceAvailable = true;
|
||||
}
|
||||
}
|
||||
return inferenceAvailable;
|
||||
protected boolean isReasoningAvailable(){
|
||||
TBoxReasonerStatus status = ApplicationUtils.instance().getTBoxReasonerModule().getStatus();
|
||||
return status.isConsistent() && !status.isInErrorState();
|
||||
}
|
||||
|
||||
protected Map<String, Individual> addWhenMissingInference( String classUri , WebappDaoFactory wDaoFact ){
|
||||
boolean inferenceAvailable = isReasoningAvailable(wDaoFact);
|
||||
boolean inferenceAvailable = isReasoningAvailable();
|
||||
Map<String,Individual> individualMap = new HashMap<String,Individual>();
|
||||
if ( !inferenceAvailable ) {
|
||||
for (String subclassURI : wDaoFact.getVClassDao().getAllSubClassURIs(classUri)) {
|
||||
|
|
|
@ -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.imageProcessor.ImageProcessor;
|
||||
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.ContentTripleSource;
|
||||
|
||||
|
@ -29,6 +30,8 @@ public interface Application {
|
|||
|
||||
ConfigurationTripleSource getConfigurationTripleSource();
|
||||
|
||||
TBoxReasonerModule getTBoxReasonerModule();
|
||||
|
||||
void shutdown();
|
||||
|
||||
public interface Component {
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
|
||||
}
|
|
@ -1,106 +0,0 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.webapp.servlet.setup;
|
||||
|
||||
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 javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletContextListener;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import com.hp.hpl.jena.ontology.OntModel;
|
||||
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.modelaccess.ContextModelAccess;
|
||||
import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess;
|
||||
import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus;
|
||||
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.ReasonerConfiguration;
|
||||
|
||||
/**
|
||||
* Start the Pellet reasoner on the TBox.
|
||||
*/
|
||||
public class PelletReasonerSetup implements ServletContextListener {
|
||||
private static final Log log = LogFactory.getLog(PelletReasonerSetup.class);
|
||||
|
||||
@Override
|
||||
public void contextInitialized(ServletContextEvent sce) {
|
||||
ServletContext ctx = sce.getServletContext();
|
||||
StartupStatus ss = StartupStatus.getBean(ctx);
|
||||
|
||||
ContextModelAccess contextModels = ModelAccess.on(ctx);
|
||||
OntModel tboxAssertionsModel = contextModels
|
||||
.getOntModel(TBOX_ASSERTIONS);
|
||||
OntModel tboxInferencesModel = contextModels
|
||||
.getOntModel(TBOX_INFERENCES);
|
||||
OntModel tboxUnionModel = contextModels.getOntModel(TBOX_UNION);
|
||||
WebappDaoFactory wadf = contextModels.getWebappDaoFactory();
|
||||
|
||||
if (!tboxAssertionsModel.getProfile().NAMESPACE()
|
||||
.equals(OWL.NAMESPACE.getNameSpace())) {
|
||||
ss.fatal(this, "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);
|
||||
}
|
||||
|
||||
ss.info(this, "Pellet reasoner connected for the TBox");
|
||||
|
||||
waitForTBoxReasoning(sce);
|
||||
}
|
||||
|
||||
public static void waitForTBoxReasoning(ServletContextEvent sce) {
|
||||
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");
|
||||
}
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
// This should never happen.
|
||||
e.printStackTrace();
|
||||
}
|
||||
sleeps++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contextDestroyed(ServletContextEvent sce) {
|
||||
// Nothing to tear down
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.webapp.servlet.setup;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletContextListener;
|
||||
|
||||
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.OntModelSpec;
|
||||
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.application.ApplicationUtils;
|
||||
import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelAccess;
|
||||
import edu.cornell.mannlib.vitro.webapp.modelaccess.ModelNames;
|
||||
import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus;
|
||||
|
||||
/**
|
||||
* Safety Net:
|
||||
*
|
||||
* Insure that the inferred TBox is the same as it was before we started messing
|
||||
* with things.
|
||||
*
|
||||
* KLUGE -- this shouldn't go into production.
|
||||
*
|
||||
* KLUGE -- in production, startup_listeners shouldn't mention this.
|
||||
*/
|
||||
public class TBoxReasonerSmokeTest implements ServletContextListener {
|
||||
private static final Log log = LogFactory
|
||||
.getLog(TBoxReasonerSmokeTest.class);
|
||||
|
||||
@Override
|
||||
public void contextInitialized(ServletContextEvent sce) {
|
||||
ServletContext ctx = sce.getServletContext();
|
||||
StartupStatus ss = StartupStatus.getBean(ctx);
|
||||
|
||||
OntModel savedInferencesModel = ModelFactory
|
||||
.createOntologyModel(OntModelSpec.OWL_MEM);
|
||||
|
||||
try (InputStream in = new FileInputStream(locateSavedInferencesFile())) {
|
||||
savedInferencesModel.read(in, null, "N3");
|
||||
} catch (IOException e) {
|
||||
ss.fatal(this, "Can't read saved inferences", e);
|
||||
}
|
||||
|
||||
OntModel tboxInferencesModel = ModelAccess.on(sce.getServletContext())
|
||||
.getOntModel(ModelNames.TBOX_INFERENCES);
|
||||
|
||||
if (savedInferencesModel.isIsomorphicWith(tboxInferencesModel)) {
|
||||
ss.info(this, "TBox inferences matches saved.");
|
||||
} else {
|
||||
dumpDifferences(savedInferencesModel, tboxInferencesModel);
|
||||
ss.fatal(this, "TBox inferences does not match saved.");
|
||||
}
|
||||
}
|
||||
|
||||
private void dumpDifferences(OntModel savedInferencesModel,
|
||||
OntModel tboxInferencesModel) {
|
||||
Model missingStatements = ModelFactory.createDefaultModel();
|
||||
for (Statement stmt : savedInferencesModel.listStatements().toList()) {
|
||||
if (!tboxInferencesModel.contains(stmt)) {
|
||||
missingStatements.add(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
Model extraStatements = ModelFactory.createDefaultModel();
|
||||
for (Statement stmt : tboxInferencesModel.listStatements().toList()) {
|
||||
if (!savedInferencesModel.contains(stmt)) {
|
||||
extraStatements.add(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
log.error("inferences: " + tboxInferencesModel.size() + ", saved: "
|
||||
+ savedInferencesModel.size() + ", missing: "
|
||||
+ missingStatements.size() + ", extra: "
|
||||
+ extraStatements.size());
|
||||
|
||||
String missing = "";
|
||||
for (Statement stmt : missingStatements.listStatements().toList()) {
|
||||
missing += "\n " + stmt;
|
||||
}
|
||||
log.error("missing statements:" + missing);
|
||||
|
||||
String extras = "";
|
||||
for (Statement stmt : extraStatements.listStatements().toList()) {
|
||||
extras += "\n " + stmt;
|
||||
}
|
||||
log.error("extra statements:" + extras);
|
||||
}
|
||||
|
||||
private File locateSavedInferencesFile() {
|
||||
String homeDirPath = ApplicationUtils.instance().getHomeDirectory()
|
||||
.getPath().toString();
|
||||
Path savedInferencesPath = Paths.get(homeDirPath, "rdf", "tbox",
|
||||
"savedInferences.n3");
|
||||
return savedInferencesPath.toFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contextDestroyed(ServletContextEvent sce) {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
}
|
|
@ -89,7 +89,11 @@ public class UpdateKnowledgeBase implements ServletContextListener {
|
|||
putReportingPathsIntoSettings(ctx, settings);
|
||||
putNonReportingPathsIntoSettings(ctx, settings);
|
||||
|
||||
PelletReasonerSetup.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();
|
||||
settings.setDefaultNamespace(wadf.getDefaultNamespace());
|
||||
|
|
|
@ -30,6 +30,11 @@ import edu.cornell.mannlib.vitro.webapp.dao.jena.event.EditEvent;
|
|||
* 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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -2,11 +2,16 @@
|
|||
|
||||
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?
|
||||
* What calls can the ConfiguredReasonerListener make to drive the TBox
|
||||
* reasoner?
|
||||
*/
|
||||
public interface TBoxReasonerDriver {
|
||||
void runSynchronizer(TBoxChanges changeSet);
|
||||
|
||||
void runSynchronizer(TBoxChanges changeSet);
|
||||
|
||||
TBoxReasonerStatus getStatus();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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++;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,8 @@ import java.util.Collections;
|
|||
import java.util.Date;
|
||||
import java.util.List;
|
||||
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.LogFactory;
|
||||
|
@ -106,4 +108,25 @@ public class VitroBackgroundThread extends Thread {
|
|||
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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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.imageProcessor.ImageProcessor;
|
||||
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.ContentTripleSource;
|
||||
|
||||
|
@ -101,4 +102,11 @@ public class ApplicationStub implements Application {
|
|||
"ApplicationStub.getConfigurationTripleSource() not implemented.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public TBoxReasonerModule getTBoxReasonerModule() {
|
||||
// TODO Auto-generated method stub
|
||||
throw new RuntimeException("ApplicationStub.getTBoxReasonerModule() not implemented.");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -34,7 +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.PelletListenerSetup
|
||||
edu.cornell.mannlib.vitro.webapp.application.ApplicationImpl$ReasonersSetup
|
||||
edu.cornell.mannlib.vitro.webapp.servlet.setup.SimpleReasonerSetup
|
||||
|
||||
#edu.cornell.mannlib.vitro.webapp.servlet.setup.UpdateKnowledgeBase
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
<section class="pageBodyGroup" role="region">
|
||||
<h3>${i18n().ontology_editor}</h3>
|
||||
|
||||
<#if ontologyEditor.pellet?has_content>
|
||||
<#if ontologyEditor.tboxReasonerStatus?has_content>
|
||||
<div class="notice">
|
||||
<p>${ontologyEditor.pellet.error}</p>
|
||||
<#if ontologyEditor.pellet.explanation?has_content>
|
||||
<p>${i18n().cause} ${ontologyEditor.pellet.explanation}</p>
|
||||
<p>${ontologyEditor.tboxReasonerStatus.error}</p>
|
||||
<#if ontologyEditor.tboxReasonerStatus.explanation?has_content>
|
||||
<p>${i18n().cause} ${ontologyEditor.tboxReasonerStatus.explanation}</p>
|
||||
</#if>
|
||||
</div>
|
||||
</#if>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue