VIVO-778 Connect the plumbing properly.

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:
Jim Blake 2014-12-03 16:35:56 -05:00
parent 3d65a708b7
commit fb97bae6af
21 changed files with 565 additions and 326 deletions

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.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);
}
}
}

View file

@ -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,10 +25,9 @@ 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.modules.tboxreasoner.TBoxReasonerStatus;
import edu.cornell.mannlib.vitro.webapp.search.controller.IndexController;
import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus;
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.TBoxReasonerDriver;
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.TBoxReasonerDriver.Status;
public class BaseSiteAdminController extends FreemarkerHttpServlet {
@ -160,24 +160,21 @@ public class BaseSiteAdminController extends FreemarkerHttpServlet {
if (PolicyHelper.isAuthorizedForActions(vreq, SimplePermission.EDIT_ONTOLOGY.ACTION)) {
String pelletError = null;
String pelletExplanation = null;
Object tbrObj = getServletContext().getAttribute("tboxReasoner");
if ( tbrObj instanceof TBoxReasonerDriver) {
Status status = ((TBoxReasonerDriver) tbrObj).getStatus();
String error = null;
String explanation = null;
TBoxReasonerStatus status = ApplicationUtils.instance().getTBoxReasonerModule().getStatus();
if (!status.isConsistent()) {
pelletError = "INCONSISTENT ONTOLOGY: reasoning halted.";
pelletExplanation = status.getExplanation();
error = "INCONSISTENT ONTOLOGY: reasoning halted.";
explanation = status.getExplanation();
} else if ( status.isInErrorState() ) {
pelletError = "An error occurred during reasoning. Reasoning has been halted. See error log for details.";
}
error = "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>();
pellet.put("error", pelletError);
if (pelletExplanation != null) {
pellet.put("explanation", pelletExplanation);
pellet.put("error", error);
if (explanation != null) {
pellet.put("explanation", explanation);
}
map.put("pellet", pellet);
}

View file

@ -44,11 +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.tboxreasoner.TBoxReasoner;
import edu.cornell.mannlib.vitro.webapp.modules.tboxreasoner.TBoxReasonerModule;
public class JenaAdminActions extends BaseEditController {
@ -191,9 +192,8 @@ public class JenaAdminActions extends BaseEditController {
}
private void printRestrictions() {
TBoxReasoner reasoner = (TBoxReasoner) getServletContext().getAttribute("tboxReasonerWrapper");
TBoxReasonerModule reasoner = ApplicationUtils.instance().getTBoxReasonerModule();
for (Restriction rest : reasoner.listRestrictions() ) {
//System.out.println();
if (rest.isAllValuesFromRestriction()) {
log.trace("All values from: ");
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.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,8 +52,8 @@ 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.modules.tboxreasoner.TBoxReasonerStatus;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.TBoxReasonerDriver;
public class DataPropertyDaoJena extends PropertyDaoJena implements
DataPropertyDao {
@ -357,10 +358,8 @@ public class DataPropertyDaoJena extends PropertyDaoJena implements
}
protected boolean reasoningAvailable() {
TBoxReasonerDriver pl = getWebappDaoFactory().getTBoxReasonerDriver();
return !(
( pl == null || !pl.getStatus().isConsistent() || pl.getStatus().isInErrorState() )
);
TBoxReasonerStatus status = ApplicationUtils.instance().getTBoxReasonerModule().getStatus();
return status.isConsistent() && !status.isInErrorState();
}
private String getRequiredDatatypeURI(Individual individual, DataProperty dataprop, List<String> vclassURIs) {

View file

@ -50,7 +50,6 @@ import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactoryConfig;
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;
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.TBoxReasonerDriver;
import edu.cornell.mannlib.vitro.webapp.utils.jena.URIUtils;
public class WebappDaoFactoryJena implements WebappDaoFactory {
@ -70,8 +69,6 @@ public class WebappDaoFactoryJena implements WebappDaoFactory {
protected WebappDaoFactoryConfig config;
protected TBoxReasonerDriver tbrd;
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 setTBoxReasonerDriver(TBoxReasonerDriver tbrd) {
this.tbrd = tbrd;
}
public TBoxReasonerDriver getTBoxReasonerDriver() {
return this.tbrd;
}
@Override
public List<String> getCommentsForResource(String resourceURI) {
List<String> commentList = new LinkedList<String>();

View file

@ -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.edit.n3editing.VTwo.EditConfigurationVTwo;
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.TBoxReasonerDriver;
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) {
TBoxReasonerDriver pl = ((WebappDaoFactoryJena) wDaoFact).getTBoxReasonerDriver();
if (pl != null && pl.getStatus().isConsistent() && !pl.getStatus().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)) {

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.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 {

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

@ -1,95 +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.rdf.model.Model;
import edu.cornell.mannlib.vitro.webapp.dao.WebappDaoFactory;
import edu.cornell.mannlib.vitro.webapp.dao.jena.WebappDaoFactoryJena;
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.BasicTBoxReasonerDriver;
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.ReasonerConfiguration;
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.TBoxReasonerDriver;
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.TBoxReasoner;
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.impl.pellet.PelletTBoxReasoner;
/**
* 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);
Model tboxInferencesModel = contextModels
.getOntModel(TBOX_INFERENCES).getBaseModel();
OntModel tboxUnionModel = contextModels.getOntModel(TBOX_UNION);
WebappDaoFactory wadf = contextModels.getWebappDaoFactory();
TBoxReasoner reasoner = new PelletTBoxReasoner(
ReasonerConfiguration.DEFAULT);
TBoxReasonerDriver driver = new BasicTBoxReasonerDriver(
tboxAssertionsModel, tboxInferencesModel, tboxUnionModel,
reasoner, ReasonerConfiguration.DEFAULT);
sce.getServletContext().setAttribute("tboxReasoner", driver);
sce.getServletContext().setAttribute("tboxReasonerWrapper", reasoner);
if (wadf instanceof WebappDaoFactoryJena) {
((WebappDaoFactoryJena) wadf).setTBoxReasonerDriver(driver);
}
ss.info(this, "Pellet reasoner connected for the TBox");
waitForTBoxReasoning(sce);
}
public static void waitForTBoxReasoning(ServletContextEvent sce) {
TBoxReasonerDriver driver = (TBoxReasonerDriver) sce.getServletContext().getAttribute("tboxReasoner");
if (driver == null) {
return;
}
int sleeps = 0;
// sleep at least once to make sure the TBox reasoning gets started
while ((0 == sleeps)
|| ((sleeps < 1000) && driver.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
}
}

View file

@ -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());

View file

@ -1,135 +0,0 @@
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
package edu.cornell.mannlib.vitro.webapp.tboxreasoner;
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 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.utils.jena.criticalsection.LockableModel;
import edu.cornell.mannlib.vitro.webapp.utils.jena.criticalsection.LockableOntModel;
import edu.cornell.mannlib.vitro.webapp.utils.jena.criticalsection.LockedOntModel;
/**
* The basic implementation of the TBoxReasonerDriver.
*/
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 TBoxReasonerDriver.Status status;
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);
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 Status getStatus() {
return status;
}
@Override
public boolean isReasoning() {
return !pendingChangeSets.isEmpty();
}
@Override
public void runSynchronizer(TBoxChanges changeSet) {
if (!changeSet.isEmpty()) {
executorService.execute(new ReasoningTask(changeSet));
}
}
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 {
reasoner.updateReasonerModel(changes);
status = reasoner.performReasoning();
buildPatternList();
updateInferencesModel();
} finally {
pendingChangeSets.remove(changes);
}
}
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);
}
}
}

View file

@ -67,7 +67,7 @@ public class InferenceModelUpdater {
addNewInferences(filteredReasonerModel);
removeOldInferences(filterInferencesModel(patternList),
filteredReasonerModel);
log.warn("Added: " + addCount + ", Retracted: " + retractCount);
log.debug("Added: " + addCount + ", Retracted: " + retractCount);
}
private void addNewInferences(List<Statement> filteredReasonerModel) {
@ -96,7 +96,7 @@ public class InferenceModelUpdater {
filtered.add(pattern.matchStatementsFromModel(inferencesModel));
}
}
log.warn("Filtered inferences model: " + filtered.size());
log.debug("Filtered inferences model: " + filtered.size());
return filtered;
}

View file

@ -9,8 +9,6 @@ import com.hp.hpl.jena.ontology.ObjectProperty;
import com.hp.hpl.jena.ontology.Restriction;
import com.hp.hpl.jena.rdf.model.Statement;
import edu.cornell.mannlib.vitro.webapp.tboxreasoner.TBoxReasonerDriver.Status;
/**
* The functionality of a TBox reasoner.
*
@ -54,4 +52,36 @@ public interface TBoxReasoner {
*/
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

@ -2,6 +2,8 @@
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
@ -11,40 +13,5 @@ public interface TBoxReasonerDriver {
void runSynchronizer(TBoxChanges changeSet);
boolean isReasoning();
Status getStatus();
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;
}
}
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

@ -22,7 +22,6 @@ 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.TBoxReasonerDriver.Status;
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;

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.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());
}
}
}

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.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.");
}
}

View file

@ -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