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 d76547497..4cfc732a4 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 @@ -2,11 +2,8 @@ package edu.cornell.mannlib.vitro.webapp.dao.jena.pellet; -import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; -import java.util.List; -import java.util.Map; import java.util.Queue; import java.util.Set; @@ -19,28 +16,25 @@ import org.mindswap.pellet.jena.PelletReasonerFactory; import com.hp.hpl.jena.ontology.DatatypeProperty; import com.hp.hpl.jena.ontology.ObjectProperty; import com.hp.hpl.jena.ontology.OntModel; -import com.hp.hpl.jena.rdf.model.Literal; import com.hp.hpl.jena.rdf.model.Model; -import com.hp.hpl.jena.rdf.model.ModelChangedListener; import com.hp.hpl.jena.rdf.model.ModelFactory; -import com.hp.hpl.jena.rdf.model.Property; import com.hp.hpl.jena.rdf.model.RDFNode; import com.hp.hpl.jena.rdf.model.Resource; import com.hp.hpl.jena.rdf.model.ResourceFactory; import com.hp.hpl.jena.rdf.model.Statement; -import com.hp.hpl.jena.rdf.model.StmtIterator; import com.hp.hpl.jena.shared.Lock; import com.hp.hpl.jena.util.iterator.ClosableIterator; import com.hp.hpl.jena.vocabulary.OWL; -import com.hp.hpl.jena.vocabulary.RDF; import com.hp.hpl.jena.vocabulary.RDFS; -import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; import edu.cornell.mannlib.vitro.webapp.dao.jena.event.EditEvent; +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; -public class PelletListener implements ModelChangedListener { +public class PelletListener implements TBoxReasonerDriver { private static final Log log = LogFactory.getLog(PelletListener.class.getName()); private boolean isReasoning = false; @@ -56,7 +50,7 @@ public class PelletListener implements ModelChangedListener { private Set inferenceDrivingPatternDenySet; private Set inferenceReceivingPatternAllowSet; - private Map> inferenceDrivingPatternMap; + private final ConfiguredReasonerListener listener; private Model additionModel; private Model removalModel; @@ -64,8 +58,6 @@ public class PelletListener implements ModelChangedListener { private Model deletedObjectProperties; private Model deletedDataProperties; - private boolean pipeOpen; - private boolean isConsistent = true; private boolean inErrorState = false; private String explanation = ""; @@ -86,14 +78,6 @@ public class PelletListener implements ModelChangedListener { return this.isReasoning; } - public void closePipe() { - pipeOpen = false; - } - - public void openPipe() { - pipeOpen = true; - } - public synchronized boolean checkAndStartReasoning(){ if( this.isReasoning ) return false; @@ -143,45 +127,88 @@ public class PelletListener implements ModelChangedListener { this.inferenceDrivingPatternDenySet = reasonerConfiguration.getInferenceDrivingPatternDenySet(); this.inferenceReceivingPatternAllowSet = reasonerConfiguration.getInferenceReceivingPatternAllowSet(); - if (this.inferenceDrivingPatternAllowSet != null) { - this.inferenceDrivingPatternMap = new HashMap<>(); - for (Iterator i = inferenceDrivingPatternAllowSet.iterator(); i.hasNext();) { - ReasonerStatementPattern pat = i.next(); - Property p = pat.getPredicate(); - List patList = inferenceDrivingPatternMap.get(p); - if (patList == null) { - patList = new LinkedList<>(); - patList.add(pat); - inferenceDrivingPatternMap.put(p, patList); - } else { - patList.add(pat); - } - } - } - this.pipeOpen = true; this.additionModel = ModelFactory.createDefaultModel(); this.removalModel = ModelFactory.createDefaultModel(); this.deletedObjectProperties = ModelFactory.createDefaultModel(); this.deletedDataProperties = ModelFactory.createDefaultModel(); + + listener = new ConfiguredReasonerListener(reasonerConfiguration, this); + this.mainModel.enterCriticalSection(Lock.READ); try { for (ReasonerStatementPattern pat : this.inferenceDrivingPatternAllowSet) { - addedStatements(mainModel.listStatements((Resource) null, pat.getPredicate(), (RDFNode) null)); + listener.addedStatements(mainModel.listStatements((Resource) null, pat.getPredicate(), (RDFNode) null)); } if (!skipReasoningUponInitialization) { this.foreground = foreground; - notifyEvent(null,new EditEvent(null,false)); + listener.notifyEvent(null,new EditEvent(null,false)); } else if (inferenceModel.size() == 0){ foreground = true; - notifyEvent(null,new EditEvent(null,false)); + listener.notifyEvent(null,new EditEvent(null,false)); this.foreground = foreground; } } finally { this.mainModel.leaveCriticalSection(); } - this.fullModel.getBaseModel().register(this); - this.mainModel.getBaseModel().register(this); + this.fullModel.getBaseModel().register(listener); + this.mainModel.getBaseModel().register(listener); + } + + @Override + public void addStatement(Statement stmt) { + additionModel.enterCriticalSection(Lock.WRITE); + try { + additionModel.add(stmt); + } finally { + additionModel.leaveCriticalSection(); + } + } + + @Override + public void removeStatement(Statement stmt) { + removalModel.enterCriticalSection(Lock.WRITE); + try { + removalModel.add(stmt); + } finally { + removalModel.leaveCriticalSection(); + } + } + + @Override + public void deleteDataProperty(Statement stmt) { + deletedDataProperties.enterCriticalSection(Lock.WRITE); + try { + deletedDataProperties.add(stmt); + } finally { + deletedDataProperties.leaveCriticalSection(); + } + } + + @Override + public void deleteObjectProperty(Statement stmt) { + deletedObjectProperties.enterCriticalSection(Lock.WRITE); + try { + deletedObjectProperties.add(stmt); + } finally { + deletedObjectProperties.leaveCriticalSection(); + } + } + + @Override + public void runSynchronizer() { + if ((additionModel.size() > 0) || (removalModel.size() > 0)) { + if (!isSynchronizing) { + if (foreground) { + log.debug("Running Pellet in foreground."); + (new PelletSynchronizer()).run(); + } else { + log.debug("Running Pellet in background."); + new Thread(new PelletSynchronizer(), + "PelletListener.PelletSynchronizer").start(); + } + } + } } private class InferenceGetter implements Runnable { @@ -333,12 +360,10 @@ public class PelletListener implements ModelChangedListener { if (!fullModelContainsStatement) { // in theory we should be able to lock only the inference model, but I'm not sure yet if Jena propagates the locking upward fullModel.enterCriticalSection(Lock.WRITE); - closePipe(); - try { + try (Suspension susp = listener.suspend()) { inferenceModel.add(stmt); addCount++; } finally { - openPipe(); fullModel.leaveCriticalSection(); } } @@ -360,12 +385,10 @@ public class PelletListener implements ModelChangedListener { } for (Iterator i = localRemovalQueue.iterator(); i.hasNext(); ) { fullModel.enterCriticalSection(Lock.WRITE); - closePipe(); - try { + try (Suspension susp = listener.suspend()) { retractCount++; inferenceModel.remove(i.next()); } finally { - openPipe(); fullModel.leaveCriticalSection(); } } @@ -412,208 +435,6 @@ public class PelletListener implements ModelChangedListener { } } - // TODO: These next two methods are really ugly; I need to refactor them to remove redundancy. - - private void tryAdd(Statement stmt) { - boolean sendToPellet = false; - if ( pipeOpen && reasonerConfiguration.getReasonOnAllDatatypePropertyStatements() && stmt.getObject().isLiteral() ) { - sendToPellet = true; - } else - if ( pipeOpen && hasCardinalityPredicate(stmt) ) { // see comment on this method - sendToPellet = true; - } else - if ( (stmt.getObject().isResource()) && !((stmt.getPredicate().getURI().indexOf(VitroVocabulary.vitroURI)==0)) ) { - if (pipeOpen) { - sendToPellet = false; - boolean denied = false; - ReasonerStatementPattern stPat = ReasonerStatementPattern.objectPattern(stmt); - if (inferenceDrivingPatternDenySet != null) { - for (Iterator i = inferenceDrivingPatternDenySet.iterator(); i.hasNext(); ){ - ReasonerStatementPattern pat = i.next(); - if (pat.matches(stPat)) { - denied = true; - break; - } - } - } - if (!denied) { - if (inferenceDrivingPatternAllowSet==null) { - sendToPellet = true; - } else { - // TODO: O(1) implementation of this - List patList = this.inferenceDrivingPatternMap.get(stmt.getPredicate()); - if (patList != null) { - for (Iterator i = patList.iterator(); i.hasNext(); ){ - ReasonerStatementPattern pat = i.next(); - if (pat.matches(stPat)) { - sendToPellet = true; - break; - } - } - } - } - } - - } - } - if (sendToPellet) { - //long startTime = System.currentTimeMillis(); - String valueStr = (stmt.getObject().isResource()) ? ((Resource)stmt.getObject()).getLocalName() : ((Literal)stmt.getObject()).getLexicalForm(); - if ( log.isDebugEnabled() ) { - log.debug( "Adding to Pellet: " + renderStatement( stmt ) ); - } - additionModel.enterCriticalSection(Lock.WRITE); - try { - additionModel.add(stmt); - } finally { - additionModel.leaveCriticalSection(); - } - } else { - if ( log.isDebugEnabled() ) { - log.debug( "Not adding to Pellet: " + renderStatement( stmt ) ); - } - } - } - - - private void tryRemove(Statement stmt) { - boolean removeFromPellet = false; - if ( pipeOpen && reasonerConfiguration.getReasonOnAllDatatypePropertyStatements() && stmt.getObject().isLiteral() ) { - removeFromPellet = true; - } else - if ( pipeOpen && hasCardinalityPredicate(stmt) ) { // see comment on this method - removeFromPellet = true; - } else - if ( stmt.getObject().isResource() ) { - if (pipeOpen) { - if (reasonerConfiguration.getQueryForAllObjectProperties() && stmt.getPredicate().equals(RDF.type) && stmt.getObject().equals(OWL.ObjectProperty)) { - deletedObjectProperties.enterCriticalSection(Lock.WRITE); - try { - deletedObjectProperties.add(stmt); - } finally { - deletedObjectProperties.leaveCriticalSection(); - } - } - if (reasonerConfiguration.getQueryForAllDatatypeProperties() && stmt.getPredicate().equals(RDF.type) && stmt.getObject().equals(OWL.DatatypeProperty)) { - deletedDataProperties.enterCriticalSection(Lock.WRITE); - try{ - deletedDataProperties.add(stmt); - } finally { - deletedDataProperties.leaveCriticalSection(); - } - } - removeFromPellet = false; - boolean denied = false; - ReasonerStatementPattern stPat = ReasonerStatementPattern.objectPattern(stmt); - if (inferenceDrivingPatternDenySet != null) { - for (Iterator i = inferenceDrivingPatternDenySet.iterator(); i.hasNext(); ){ - ReasonerStatementPattern pat = i.next(); - if (pat.matches(stPat)) { - denied = true; - break; - } - } - } - if (!denied) { - if (inferenceDrivingPatternAllowSet==null) { - removeFromPellet = true; - } else { - // TODO: O(1) implementation of this - List patList = this.inferenceDrivingPatternMap.get(stmt.getPredicate()); - if (patList != null) { - for (Iterator i = patList.iterator(); i.hasNext(); ){ - ReasonerStatementPattern pat = i.next(); - if (pat.matches(stPat)) { - removeFromPellet = true; - break; - } - } - } - } - } - } - } - if (removeFromPellet) { - String valueStr = (stmt.getObject().isResource()) ? ((Resource)stmt.getObject()).getLocalName() : ((Literal)stmt.getObject()).getLexicalForm(); - log.info("Removing from Pellet: "+stmt.getSubject().getLocalName()+" "+stmt.getPredicate().getLocalName()+" "+valueStr); - removalModel.enterCriticalSection(Lock.WRITE); - try { - removalModel.add(stmt); - } finally { - removalModel.leaveCriticalSection(); - } - } - } - - // The pattern matching stuff needs to get reworked. - // It originally assumed that only resources would be in object - // position, but cardinality axioms will have e.g. nonNegativeIntegers. - // This is a temporary workaround: all cardinality statements will - // be exposed to Pellet, regardless of configuration patterns. - private boolean hasCardinalityPredicate(Statement stmt) { - return ( - stmt.getPredicate().equals(OWL.cardinality) || - stmt.getPredicate().equals(OWL.minCardinality) || - stmt.getPredicate().equals(OWL.maxCardinality) - ) ; - } - - - public void addedStatement(Statement arg0) { - tryAdd(arg0); - } - - - public void addedStatements(Statement[] arg0) { - for (int i=0; i 0) || (removalModel.size()>0) ) { - if (!isSynchronizing) { - if (foreground) { - log.debug("Running Pellet in foreground."); - (new PelletSynchronizer()).run(); - } else { - log.debug("Running Pellet in background."); - new Thread(new PelletSynchronizer(), "PelletListener.PelletSynchronizer").start(); - } - } - } - } - } - } - private class PelletSynchronizer implements Runnable { public void run() { try { @@ -657,54 +478,9 @@ public class PelletListener implements ModelChangedListener { } } } - - public void removedStatement(Statement arg0) { - tryRemove(arg0); - } - - - public void removedStatements(Statement[] arg0) { - for (int i=0; i> { + + public DrivingPatternMap(Set patternSet) { + if (patternSet != null) { + for (ReasonerStatementPattern pat : patternSet) { + Property p = pat.getPredicate(); + if (!containsKey(p)) { + put(p, new LinkedList()); + } + get(p).add(pat); + } + } + } + } + + // ---------------------------------------------------------------------- + // Implement the ModelChangedListener methods. Delegate to the methods that + // check criteria. + // ---------------------------------------------------------------------- + + @Override + public void addedStatement(Statement s) { + tryAdd(s); + } + + @Override + public void addedStatements(Statement[] statements) { + for (Statement stmt : statements) { + tryAdd(stmt); + } + } + + @Override + public void addedStatements(List statements) { + for (Statement stmt : statements) { + tryAdd(stmt); + } + } + + @Override + public void addedStatements(StmtIterator statements) { + for (Statement stmt : statements.toList()) { + tryAdd(stmt); + } + } + + @Override + public void addedStatements(Model m) { + for (Statement stmt : m.listStatements().toList()) { + tryAdd(stmt); + } + } + + @Override + public void removedStatement(Statement s) { + tryRemove(s); + } + + @Override + public void removedStatements(Statement[] statements) { + for (Statement stmt : statements) { + tryRemove(stmt); + } + } + + @Override + public void removedStatements(List statements) { + for (Statement stmt : statements) { + tryRemove(stmt); + } + } + + @Override + public void removedStatements(StmtIterator statements) { + for (Statement stmt : statements.toList()) { + tryRemove(stmt); + } + } + + @Override + public void removedStatements(Model m) { + for (Statement stmt : m.listStatements().toList()) { + tryRemove(stmt); + } + } + + @Override + public void notifyEvent(Model m, Object event) { + if (event instanceof EditEvent) { + EditEvent ee = (EditEvent) event; + if (!ee.getBegin()) { + this.reasonerDriver.runSynchronizer(); + } + } + } + + // ---------------------------------------------------------------------- + // Check the criteria to determine whether each addition or removal should + // be passed to the reasoner. + // + // When the listener is suspended, nothing is passed on. + // ---------------------------------------------------------------------- + + public void tryAdd(Statement stmt) { + if (suspended.get()) { + return; + } + + if (isDataProperty(stmt)) { + if (reasonOnAllDataProperties() || hasCardinalityPredicate(stmt)) { + addIt(stmt); + return; + } else { + return; + } + } + + if (predicateIsInVitroNamespace(stmt) + || statementMatchesDenyPattern(stmt)) { + return; + } + + if (thereAreNoDrivingPatterns() || statementMatchesDrivingPattern(stmt)) { + addIt(stmt); + return; + } + } + + public void tryRemove(Statement stmt) { + if (suspended.get()) { + return; + } + + if (isDataProperty(stmt)) { + if (reasonOnAllDataProperties() || hasCardinalityPredicate(stmt)) { + removeIt(stmt); + return; + } else { + return; + } + } + + if (actOnObjectPropertyDeclarations() && declaresObjectProperty(stmt)) { + deleteObjectProperty(stmt); + return; + } + + if (actOnDataPropertyDeclarations() && declaresDataProperty(stmt)) { + deleteDataProperty(stmt); + return; + } + + if (statementMatchesDenyPattern(stmt)) { + return; + } + + if (thereAreNoDrivingPatterns() || statementMatchesDrivingPattern(stmt)) { + removeIt(stmt); + return; + } + } + + private boolean isDataProperty(Statement stmt) { + return stmt.getObject().isLiteral(); + } + + private boolean reasonOnAllDataProperties() { + return reasonerConfiguration.getReasonOnAllDatatypePropertyStatements(); + } + + private boolean predicateIsInVitroNamespace(Statement stmt) { + return stmt.getPredicate().getURI().indexOf(VitroVocabulary.vitroURI) == 0; + } + + private boolean statementMatchesDenyPattern(Statement stmt) { + Set denyPatterns = reasonerConfiguration.inferenceDrivingPatternDenySet; + if (denyPatterns == null) { + return false; + } + + ReasonerStatementPattern stPat = ReasonerStatementPattern + .objectPattern(stmt); + + for (ReasonerStatementPattern pat : denyPatterns) { + if (pat.matches(stPat)) { + return true; + } + } + + return false; + } + + private boolean thereAreNoDrivingPatterns() { + return reasonerConfiguration.inferenceDrivingPatternAllowSet == null; + } + + private boolean statementMatchesDrivingPattern(Statement stmt) { + List drivePatterns = drivingPatternMap + .get(stmt.getPredicate()); + if (drivePatterns == null) { + return false; + } + + ReasonerStatementPattern stPat = ReasonerStatementPattern + .objectPattern(stmt); + + for (ReasonerStatementPattern pat : drivePatterns) { + if (pat.matches(stPat)) { + return true; + } + } + + return false; + } + + private boolean actOnObjectPropertyDeclarations() { + return reasonerConfiguration.getQueryForAllObjectProperties(); + } + + private boolean declaresObjectProperty(Statement stmt) { + return stmt.getPredicate().equals(RDF.type) + && stmt.getObject().equals(OWL.ObjectProperty); + } + + private boolean actOnDataPropertyDeclarations() { + return reasonerConfiguration.getQueryForAllDatatypeProperties(); + } + + private boolean declaresDataProperty(Statement stmt) { + return stmt.getPredicate().equals(RDF.type) + && stmt.getObject().equals(OWL.DatatypeProperty); + } + + private void addIt(Statement stmt) { + this.reasonerDriver.addStatement(stmt); + } + + private void removeIt(Statement stmt) { + this.reasonerDriver.removeStatement(stmt); + } + + private void deleteObjectProperty(Statement stmt) { + this.reasonerDriver.deleteObjectProperty(stmt); + } + + private void deleteDataProperty(Statement stmt) { + this.reasonerDriver.deleteDataProperty(stmt); + } + + // The pattern matching stuff needs to get reworked. + // It originally assumed that only resources would be in object + // position, but cardinality axioms will have e.g. nonNegativeIntegers. + // This is a temporary workaround: all cardinality statements will + // be exposed to Pellet, regardless of configuration patterns. + private boolean hasCardinalityPredicate(Statement stmt) { + return (stmt.getPredicate().equals(OWL.cardinality) + || stmt.getPredicate().equals(OWL.minCardinality) || stmt + .getPredicate().equals(OWL.maxCardinality)); + } + +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/tboxreasoner/TBoxReasonerDriver.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/tboxreasoner/TBoxReasonerDriver.java new file mode 100644 index 000000000..a52d8e06a --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/tboxreasoner/TBoxReasonerDriver.java @@ -0,0 +1,21 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.tboxreasoner; + +import com.hp.hpl.jena.rdf.model.Statement; + +/** + * What calls can the ConfiguredReasonerListener make to drive the TBox reasoner? + */ +public interface TBoxReasonerDriver { + void runSynchronizer(); + + void addStatement(Statement stmt); + + void removeStatement(Statement stmt); + + void deleteDataProperty(Statement stmt); + + void deleteObjectProperty(Statement stmt); + +}