From 6bb8987d9d4206fbef8563a9e6ef1f1164e7301d Mon Sep 17 00:00:00 2001 From: Jim Blake Date: Wed, 3 Dec 2014 10:18:03 -0500 Subject: [PATCH] VIVO-778 Accumulate the TBox changes in a change set object. --- .../dao/jena/pellet/PelletListener.java | 136 ++++-------------- .../ConfiguredReasonerListener.java | 26 ++-- .../webapp/tboxreasoner/TBoxChanges.java | 83 +++++++++++ .../tboxreasoner/TBoxReasonerDriver.java | 11 +- 4 files changed, 127 insertions(+), 129 deletions(-) create mode 100644 webapp/src/edu/cornell/mannlib/vitro/webapp/tboxreasoner/TBoxChanges.java 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..7d0232b52 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,8 +2,11 @@ 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; @@ -32,6 +35,7 @@ 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 { @@ -52,12 +56,6 @@ public class PelletListener implements TBoxReasonerDriver { private final ConfiguredReasonerListener listener; - private Model additionModel; - private Model removalModel; - - private Model deletedObjectProperties; - private Model deletedDataProperties; - private boolean isConsistent = true; private boolean inErrorState = false; private String explanation = ""; @@ -99,6 +97,8 @@ public class PelletListener implements TBoxReasonerDriver { this.dirty = dirt; } + private final List pendingChangeSets = Collections.synchronizedList(new ArrayList()); + private int inferenceRounds = 0; private boolean foreground = false; @@ -127,11 +127,6 @@ public class PelletListener implements TBoxReasonerDriver { this.inferenceDrivingPatternDenySet = reasonerConfiguration.getInferenceDrivingPatternDenySet(); this.inferenceReceivingPatternAllowSet = reasonerConfiguration.getInferenceReceivingPatternAllowSet(); - 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); @@ -156,48 +151,9 @@ public class PelletListener implements TBoxReasonerDriver { } @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)) { + public void runSynchronizer(TBoxChanges changeSet) { + if (!changeSet.isEmpty()) { + pendingChangeSets.add(changeSet); if (!isSynchronizing) { if (foreground) { log.debug("Running Pellet in foreground."); @@ -214,9 +170,11 @@ public class PelletListener implements TBoxReasonerDriver { private class InferenceGetter implements Runnable { private PelletListener pelletListener; + private TBoxChanges changeSet; - public InferenceGetter(PelletListener pelletListener) { + public InferenceGetter(PelletListener pelletListener, TBoxChanges changeSet) { this.pelletListener = pelletListener; + this.changeSet = changeSet; } public void run() { @@ -252,20 +210,8 @@ public class PelletListener implements TBoxReasonerDriver { } finally { pelletModel.leaveCriticalSection(); } - deletedObjectProperties.enterCriticalSection(Lock.WRITE); - try { - ClosableIterator sit = deletedObjectProperties.listSubjects(); - try { - while (sit.hasNext()) { - Resource subj = (Resource) sit.next(); - irpl.add(ReasonerStatementPattern.objectPattern(ResourceFactory.createProperty(subj.getURI()))); - } - } finally { - sit.close(); - } - deletedObjectProperties.removeAll(); - } finally { - deletedObjectProperties.leaveCriticalSection(); + for (String uri: changeSet.getDeletedObjectPropertyUris()) { + irpl.add(ReasonerStatementPattern.objectPattern(ResourceFactory.createProperty(uri))); } } @@ -287,20 +233,8 @@ public class PelletListener implements TBoxReasonerDriver { } finally { pelletModel.leaveCriticalSection(); } - deletedDataProperties.enterCriticalSection(Lock.WRITE); - try { - ClosableIterator sit = deletedDataProperties.listSubjects(); - try { - while (sit.hasNext()) { - Resource subj = (Resource) sit.next(); - irpl.add(ReasonerStatementPattern.objectPattern(ResourceFactory.createProperty(subj.getURI()))); - } - } finally { - sit.close(); - } - deletedDataProperties.removeAll(); - } finally { - deletedDataProperties.leaveCriticalSection(); + for (String uri: changeSet.getDeletedDataPropertyUris()) { + irpl.add(ReasonerStatementPattern.objectPattern(ResourceFactory.createProperty(uri))); } } @@ -424,13 +358,13 @@ public class PelletListener implements TBoxReasonerDriver { } - private void getInferences() { + private void getInferences(TBoxChanges changeSet) { this.setDirty(true); if ( this.checkAndStartReasoning() ){ if (foreground) { - (new InferenceGetter(this)).run(); + (new InferenceGetter(this, changeSet)).run(); } else { - new Thread(new InferenceGetter(this), "PelletListener.InferenceGetter").start(); + new Thread(new InferenceGetter(this, changeSet), "PelletListener.InferenceGetter").start(); } } } @@ -439,38 +373,18 @@ public class PelletListener implements TBoxReasonerDriver { public void run() { try { isSynchronizing = true; - while (removalModel.size()>0 || additionModel.size()>0) { - Model tempModel = ModelFactory.createDefaultModel(); - removalModel.enterCriticalSection(Lock.WRITE); - try { - tempModel.add(removalModel); - removalModel.removeAll(); - } finally { - removalModel.leaveCriticalSection(); - } + while (!pendingChangeSets.isEmpty()) { + TBoxChanges changeSet = pendingChangeSets.remove(0); + pelletModel.enterCriticalSection(Lock.WRITE); try { - pelletModel.remove(tempModel); + pelletModel.remove(changeSet.getRemovedStatements()); + pelletModel.add(changeSet.getAddedStatements()); } finally { pelletModel.leaveCriticalSection(); } - tempModel.removeAll(); - additionModel.enterCriticalSection(Lock.WRITE); - try { - tempModel.add(additionModel); - additionModel.removeAll(); - } finally { - additionModel.leaveCriticalSection(); - } - pelletModel.enterCriticalSection(Lock.WRITE); - try { - pelletModel.add(tempModel); - } finally { - pelletModel.leaveCriticalSection(); - } - tempModel = null; - getInferences(); + getInferences(changeSet); } } finally { diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/tboxreasoner/ConfiguredReasonerListener.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/tboxreasoner/ConfiguredReasonerListener.java index 8f9dc2ea5..f855434e4 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/tboxreasoner/ConfiguredReasonerListener.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/tboxreasoner/ConfiguredReasonerListener.java @@ -7,6 +7,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -23,8 +24,11 @@ import edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary; import edu.cornell.mannlib.vitro.webapp.dao.jena.event.EditEvent; /** - * Listens for changes on a model. When a change is announced, it is passed - * along to the reasoner driver, if the configuration says that it is worthy. + * Listens for changes on a model. When a change is announced, it is checked for + * worthiness. If worthy, it is added to a change set. + * + * When an ending EditEvent is received, the current change set is passed along + * to the reasoner driver, and a new change set is begun. * * 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 @@ -36,9 +40,11 @@ public class ConfiguredReasonerListener implements ModelChangedListener { private final ReasonerConfiguration reasonerConfiguration; private final TBoxReasonerDriver reasonerDriver; + private final DrivingPatternMap drivingPatternMap; - private final AtomicBoolean suspended = new AtomicBoolean(); + private final AtomicReference changeSet; + private final AtomicBoolean suspended; public ConfiguredReasonerListener( ReasonerConfiguration reasonerConfiguration, @@ -48,6 +54,9 @@ public class ConfiguredReasonerListener implements ModelChangedListener { this.drivingPatternMap = new DrivingPatternMap( reasonerConfiguration.getInferenceDrivingPatternAllowSet()); + + this.changeSet = new AtomicReference<>(new TBoxChanges()); + this.suspended = new AtomicBoolean(); } public Suspension suspend() { @@ -162,7 +171,8 @@ public class ConfiguredReasonerListener implements ModelChangedListener { if (event instanceof EditEvent) { EditEvent ee = (EditEvent) event; if (!ee.getBegin()) { - this.reasonerDriver.runSynchronizer(); + TBoxChanges changes = changeSet.getAndSet(new TBoxChanges()); + this.reasonerDriver.runSynchronizer(changes); } } } @@ -305,19 +315,19 @@ public class ConfiguredReasonerListener implements ModelChangedListener { } private void addIt(Statement stmt) { - this.reasonerDriver.addStatement(stmt); + changeSet.get().addStatement(stmt); } private void removeIt(Statement stmt) { - this.reasonerDriver.removeStatement(stmt); + changeSet.get().removeStatement(stmt); } private void deleteObjectProperty(Statement stmt) { - this.reasonerDriver.deleteObjectProperty(stmt); + changeSet.get().deleteObjectProperty(stmt); } private void deleteDataProperty(Statement stmt) { - this.reasonerDriver.deleteDataProperty(stmt); + changeSet.get().deleteDataProperty(stmt); } // The pattern matching stuff needs to get reworked. diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/tboxreasoner/TBoxChanges.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/tboxreasoner/TBoxChanges.java new file mode 100644 index 000000000..efe79d917 --- /dev/null +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/tboxreasoner/TBoxChanges.java @@ -0,0 +1,83 @@ +/* $This file is distributed under the terms of the license in /doc/license.txt$ */ + +package edu.cornell.mannlib.vitro.webapp.tboxreasoner; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import com.hp.hpl.jena.rdf.model.Resource; +import com.hp.hpl.jena.rdf.model.Statement; + +/** + * Accumulate changes to the TBox as they arrive. Then make them available to + * the TBox reasoner. + */ +public class TBoxChanges { + private final List addedStatements = Collections + .synchronizedList(new ArrayList()); + + private final List removedStatements = Collections + .synchronizedList(new ArrayList()); + + private final List deletedDataPropertyUris = Collections + .synchronizedList(new ArrayList()); + + private final List deletedObjectPropertyUris = Collections + .synchronizedList(new ArrayList()); + + // ---------------------------------------------------------------------- + // These methods are called when populating the changeSet. They must be + // thread-safe. + // ---------------------------------------------------------------------- + + public void addStatement(Statement stmt) { + addedStatements.add(stmt); + } + + public void removeStatement(Statement stmt) { + removedStatements.remove(stmt); + } + + public void deleteDataProperty(Statement stmt) { + Resource subject = stmt.getSubject(); + if (subject.isURIResource()) { + deletedDataPropertyUris.add(subject.getURI()); + } + } + + public void deleteObjectProperty(Statement stmt) { + Resource subject = stmt.getSubject(); + if (subject.isURIResource()) { + deletedObjectPropertyUris.add(subject.getURI()); + } + } + + // ---------------------------------------------------------------------- + // These methods are called when processing the changeSet. By that time, it + // is owned and accessed by a single thread. + // ---------------------------------------------------------------------- + + public boolean isEmpty() { + return addedStatements.isEmpty() && removedStatements.isEmpty() + && deletedDataPropertyUris.isEmpty() + && deletedObjectPropertyUris.isEmpty(); + } + + public List getAddedStatements() { + return addedStatements; + } + + public List getRemovedStatements() { + return removedStatements; + } + + public List getDeletedDataPropertyUris() { + return deletedDataPropertyUris; + } + + public List getDeletedObjectPropertyUris() { + return deletedObjectPropertyUris; + } + +} diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/tboxreasoner/TBoxReasonerDriver.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/tboxreasoner/TBoxReasonerDriver.java index a52d8e06a..41175a648 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/tboxreasoner/TBoxReasonerDriver.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/tboxreasoner/TBoxReasonerDriver.java @@ -2,20 +2,11 @@ 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); + void runSynchronizer(TBoxChanges changeSet); }