From d513dcf9aeba7be12f53dcd016c4c03555d8995b Mon Sep 17 00:00:00 2001 From: Jim Blake Date: Mon, 1 Dec 2014 16:52:07 -0500 Subject: [PATCH] VIVO-778 Break out just the task of syncing the inference model to the reasoner model. --- .../jena/pellet/InferenceModelUpdater.java | 139 ++++++++++++++++++ .../dao/jena/pellet/PelletListener.java | 94 +----------- .../servlet/setup/TBoxReasonerSmokeTest.java | 43 ++++++ 3 files changed, 189 insertions(+), 87 deletions(-) create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/pellet/InferenceModelUpdater.java diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/pellet/InferenceModelUpdater.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/pellet/InferenceModelUpdater.java new file mode 100644 index 000000000..36de3a795 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/pellet/InferenceModelUpdater.java @@ -0,0 +1,139 @@ +/* $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.LinkedList; + +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.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.ConfiguredReasonerListener; +import edu.cornell.mannlib.vitro.webapp.tboxreasoner.ConfiguredReasonerListener.Suspension; +import edu.cornell.mannlib.vitro.webapp.tboxreasoner.ReasonerStatementPattern; +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 LockableOntModel lockableReasonerModel; + private final LockableModel lockableInferenceModel; + 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(OntModel reasonerModel, Model inferenceModel, + OntModel fullModel, ConfiguredReasonerListener listener) { + this.lockableReasonerModel = new LockableOntModel(reasonerModel); + this.lockableInferenceModel = new LockableModel(inferenceModel); + this.lockableFullModel = new LockableOntModel(fullModel); + this.listener = listener; + } + + /** + * Synchronize the inferences model with the reasoner model, with these + * provisos: + * + * Statements in the reasoner model about RDFS.Resource or OWL.Nothing are + * ignored. + * + * If a statement exists anywhere in the full TBox, don't bother adding it + * to the inferences model. + */ + public void update(LinkedList patternList) { + Model filteredReasonerModel = filterReasonerModel(patternList); + addNewInferences(filteredReasonerModel); + removeOldInferences(filterInferencesModel(patternList), + filteredReasonerModel); + log.warn("Added: " + addCount + ", Retracted: " + retractCount); + } + + private Model filterReasonerModel( + LinkedList patternList) { + Model filtered = ModelFactory.createDefaultModel(); + try (LockedOntModel reasonerModel = lockableReasonerModel.read()) { + for (ReasonerStatementPattern pattern : patternList) { + filtered.add(pattern.matchStatementsFromModel(reasonerModel)); + } + } + for (Statement stmt : filtered.listStatements().toList()) { + if (stmt.getObject().equals(RDFS.Resource)) { + filtered.remove(stmt); + } else if (stmt.getSubject().equals(OWL.Nothing)) { + filtered.remove(stmt); + } else if (stmt.getObject().equals(OWL.Nothing)) { + filtered.remove(stmt); + } + } + log.warn("Filtered reasoner model: " + filtered.size()); + return filtered; + } + + private void addNewInferences(Model filteredReasonerModel) { + for (Statement stmt : filteredReasonerModel.listStatements().toList()) { + if (!fullModelContainsStatement(stmt)) { + try (LockedModel inferenceModel = lockableInferenceModel + .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( + LinkedList patternList) { + Model filtered = ModelFactory.createDefaultModel(); + try (LockedOntModel reasonerModel = lockableReasonerModel.read()) { + for (ReasonerStatementPattern pattern : patternList) { + filtered.add(pattern.matchStatementsFromModel(reasonerModel)); + } + } + log.warn("Filtered inferences model: " + filtered.size()); + return filtered; + } + + private void removeOldInferences(Model filteredInferencesModel, + Model filteredReasonerModel) { + for (Statement stmt : filteredInferencesModel.listStatements().toList()) { + if (!filteredReasonerModel.contains(stmt)) { + try (LockedModel inferenceModel = lockableInferenceModel + .write(); Suspension susp = listener.suspend()) { + retractCount++; + inferenceModel.remove(stmt); + } + } + } + } + +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/pellet/PelletListener.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/pellet/PelletListener.java index 4cfc732a4..fc44d2d14 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/pellet/PelletListener.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/dao/jena/pellet/PelletListener.java @@ -4,7 +4,6 @@ package edu.cornell.mannlib.vitro.webapp.dao.jena.pellet; import java.util.Iterator; import java.util.LinkedList; -import java.util.Queue; import java.util.Set; import org.apache.commons.logging.Log; @@ -24,12 +23,9 @@ 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.TBoxReasonerDriver; @@ -303,10 +299,7 @@ public class PelletListener implements TBoxReasonerDriver { deletedDataProperties.leaveCriticalSection(); } } - - int addCount = 0; - int retractCount = 0; - + // force new reasoner (disabled) if (false && !reasonerConfiguration.isIncrementalReasoningEnabled()) { Model baseModel = pelletModel.getBaseModel(); @@ -323,88 +316,16 @@ public class PelletListener implements TBoxReasonerDriver { pelletModel.leaveCriticalSection(); } - for (Iterator 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 localRemovalQueue = new LinkedList(); - for (Statement stmt : pat.matchStatementsFromModel(inferenceModel)) { - if (!tempModel.contains(stmt)) { - localRemovalQueue.add(stmt); - } - } - for (Iterator 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; - } + InferenceModelUpdater inferenceModelUpdater = new InferenceModelUpdater( + pelletModel, inferenceModel, fullModel, listener); + inferenceModelUpdater.update(irpl); + 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("Added "+inferenceModelUpdater.getAddCount()+" statements entailed by assertions"); + log.info("Retracted "+inferenceModelUpdater.getRetractCount()+" statements no longer entailed by assertions"); log.info("Done getting new inferences: "+(System.currentTimeMillis()-startTime)/1000+" seconds"); } } catch (InconsistentOntologyException ioe) { @@ -421,7 +342,6 @@ public class PelletListener implements TBoxReasonerDriver { } } } - } private void getInferences() { diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/TBoxReasonerSmokeTest.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/TBoxReasonerSmokeTest.java index 033fd0ef9..1fe7109d0 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/TBoxReasonerSmokeTest.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/servlet/setup/TBoxReasonerSmokeTest.java @@ -13,9 +13,14 @@ 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; @@ -33,6 +38,9 @@ import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus; * 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(); @@ -53,10 +61,45 @@ public class TBoxReasonerSmokeTest implements ServletContextListener { 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();