You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by de...@apache.org on 2019/06/30 09:18:58 UTC
[jena] 01/01: JENA-1719: Work around for iterators being held
across transactions
This is an automated email from the ASF dual-hosted git repository.
der pushed a commit to branch JENA-1719
in repository https://gitbox.apache.org/repos/asf/jena.git
commit ddbaeaa0653fa0b60da500b8d14cfa6ea8cefe02
Author: der <da...@epimorphics.com>
AuthorDate: Sun Jun 30 10:18:22 2019 +0100
JENA-1719: Work around for iterators being held across transactions
---
.../jena/reasoner/rulesys/impl/LPInterpreter.java | 2 +-
.../reasoner/rulesys/impl/SafeTripleIterator.java | 56 +++++++++
.../rulesys/impl/TopLevelTripleMatchFrame.java | 4 +-
.../reasoner/rulesys/impl/TripleMatchFrame.java | 4 +-
.../rulesys/impl/TestRestartableLBRule.java | 137 +++++++++++++++++++++
.../jena/reasoner/rulesys/test/TestPackage.java | 2 +
jena2.log | 0
7 files changed, 200 insertions(+), 5 deletions(-)
diff --git a/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/impl/LPInterpreter.java b/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/impl/LPInterpreter.java
index 909b829..ddb1796 100644
--- a/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/impl/LPInterpreter.java
+++ b/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/impl/LPInterpreter.java
@@ -166,7 +166,7 @@ public class LPInterpreter {
isComplete = true;
if (cpFrame != null) cpFrame.close();
engine.detach(this);
-
+ if (topTMFrame != null) topTMFrame.close();
}
/**
diff --git a/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/impl/SafeTripleIterator.java b/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/impl/SafeTripleIterator.java
new file mode 100644
index 0000000..48563a5
--- /dev/null
+++ b/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/impl/SafeTripleIterator.java
@@ -0,0 +1,56 @@
+package org.apache.jena.reasoner.rulesys.impl;
+
+import org.apache.jena.graph.Triple;
+import org.apache.jena.reasoner.TriplePattern;
+import org.apache.jena.reasoner.rulesys.BackwardRuleInfGraphI;
+import org.apache.jena.util.iterator.ClosableIterator;
+import org.apache.jena.util.iterator.ExtendedIterator;
+
+/**
+ * Wrapper around triple searches using in the back chainer to attempt a restart
+ * in the case of a failure such as cross-transaction query.
+ */
+public class SafeTripleIterator implements ClosableIterator<Triple> {
+
+ protected ExtendedIterator<Triple> matchIterator;
+ protected long offset = 0;
+ protected TriplePattern goal;
+ protected BackwardRuleInfGraphI infGraph;
+
+ public SafeTripleIterator(LPInterpreter interpreter, TriplePattern goal) {
+ this.infGraph = interpreter.getEngine().getInfGraph();
+ this.goal = goal;
+ restart();
+ }
+
+ protected void restart() {
+ matchIterator = infGraph.findDataMatches(goal);
+ for (int i = 0; i < offset; i++) matchIterator.next();
+ }
+
+ @Override
+ public void close() {
+ matchIterator.close();
+ }
+
+ @Override
+ public boolean hasNext() {
+ try {
+ return matchIterator.hasNext();
+ } catch (Exception e) {
+ restart();
+ return matchIterator.hasNext();
+ }
+ }
+
+ @Override
+ public Triple next() {
+ try {
+ offset++;
+ return matchIterator.next();
+ } catch (Exception e) {
+ restart();
+ return matchIterator.next();
+ }
+ }
+}
diff --git a/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/impl/TopLevelTripleMatchFrame.java b/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/impl/TopLevelTripleMatchFrame.java
index 0caf8b2..b0aecf3 100644
--- a/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/impl/TopLevelTripleMatchFrame.java
+++ b/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/impl/TopLevelTripleMatchFrame.java
@@ -29,7 +29,7 @@ public class TopLevelTripleMatchFrame extends GenericChoiceFrame {
protected Triple lastMatch;
/** An iterator over triples matching a goal */
- ExtendedIterator<Triple> matchIterator;
+ SafeTripleIterator matchIterator;
/** Used for debug/tracing only */
protected TriplePattern goal;
@@ -42,7 +42,7 @@ public class TopLevelTripleMatchFrame extends GenericChoiceFrame {
*/
public TopLevelTripleMatchFrame(LPInterpreter interpreter, TriplePattern goal) {
init(interpreter);
- this.matchIterator = interpreter.getEngine().getInfGraph().findDataMatches(goal);
+ this.matchIterator = new SafeTripleIterator(interpreter, goal);
this.goal = goal;
}
diff --git a/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/impl/TripleMatchFrame.java b/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/impl/TripleMatchFrame.java
index 74b1a1b..6287a2e 100644
--- a/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/impl/TripleMatchFrame.java
+++ b/jena-core/src/main/java/org/apache/jena/reasoner/rulesys/impl/TripleMatchFrame.java
@@ -32,7 +32,7 @@ import org.apache.jena.util.iterator.ExtendedIterator ;
public class TripleMatchFrame extends GenericTripleMatchFrame {
/** An iterator over triples matching a goal */
- ExtendedIterator<Triple> matchIterator;
+ protected SafeTripleIterator matchIterator;
/**
* Constructor.
@@ -65,7 +65,7 @@ public class TripleMatchFrame extends GenericTripleMatchFrame {
*/
@Override public void init(LPInterpreter interpreter) {
super.init(interpreter);
- this.matchIterator = interpreter.getEngine().getInfGraph().findDataMatches(goal);
+ this.matchIterator = new SafeTripleIterator(interpreter, goal);
}
/**
diff --git a/jena-core/src/test/java/org/apache/jena/reasoner/rulesys/impl/TestRestartableLBRule.java b/jena-core/src/test/java/org/apache/jena/reasoner/rulesys/impl/TestRestartableLBRule.java
new file mode 100644
index 0000000..f332cca
--- /dev/null
+++ b/jena-core/src/test/java/org/apache/jena/reasoner/rulesys/impl/TestRestartableLBRule.java
@@ -0,0 +1,137 @@
+package org.apache.jena.reasoner.rulesys.impl;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.apache.jena.graph.Graph;
+import org.apache.jena.graph.TransactionHandler;
+import org.apache.jena.graph.Triple;
+import org.apache.jena.graph.impl.GraphBase;
+import org.apache.jena.graph.impl.TransactionHandlerBase;
+import org.apache.jena.mem.GraphMem;
+import org.apache.jena.rdf.model.*;
+import org.apache.jena.reasoner.rulesys.GenericRuleReasoner;
+import org.apache.jena.reasoner.rulesys.Rule;
+import org.apache.jena.shared.JenaException;
+import org.apache.jena.util.iterator.ExtendedIterator;
+import org.apache.jena.util.iterator.WrappedIterator;
+import org.apache.jena.vocabulary.RDF;
+import org.junit.Test;
+
+import java.util.Iterator;
+
+public class TestRestartableLBRule extends TestCase {
+
+ public static TestSuite suite() {
+ return new TestSuite(TestRestartableLBRule.class);
+ }
+
+ Resource Senator = ResourceFactory.createResource("http://example.com/ns/Senator");
+ Resource Person = ResourceFactory.createResource("http://example.com/ns/Person");
+ Resource Politician = ResourceFactory.createResource("http://example.com/ns/Politician");
+
+ @Test
+ public void testCrossTransactionQueryBug() {
+ DummyTxnGraph graph = new DummyTxnGraph();
+ Model data = ModelFactory.createModelForGraph( graph );
+ for(int i = 0; i < 1000; i++) {
+ data.add( data.createResource("http://example.com/ns/i-" + i), RDF.type, Senator);
+ }
+
+ String rules = "-> tableAll(). " +
+ "[r1: (?x rdf:type <http://example.com/ns/Person>) <- (?x rdf:type <http://example.com/ns/Politician>)] " +
+ "[r2: (?x rdf:type <http://example.com/ns/Politician>) <- (?x rdf:type <http://example.com/ns/Senator>)] " +
+ "[r3: (?x rdf:type <http://example.com/ns/Person>) <- (?x rdf:type <http://example.com/ns/Senator>)] ";
+ GenericRuleReasoner reasoner = new GenericRuleReasoner(Rule.parseRules(rules));
+
+ InfModel infmodel = ModelFactory.createInfModel(reasoner, data);
+
+ assertTrue( queryN(infmodel, Person, 10) );
+ assertTrue( queryN(infmodel, Politician, 1000) );
+ assertTrue( queryN(infmodel, Person, 1000) );
+ }
+
+ private boolean queryN(Model model, Resource c, int n) {
+ model.begin();
+ StmtIterator si = model.listStatements(null, RDF.type, c);
+ try {
+ for (int i = 0; i < n; i++) {
+ if (si.hasNext()) {
+ si.next();
+ } else {
+ return false;
+ }
+ }
+ return true;
+ } finally {
+ si.close();
+ model.commit();
+ }
+ }
+
+ class DummyTxnHandler extends TransactionHandlerBase implements TransactionHandler {
+ int transaction = 0;
+ boolean inTransaction = false;
+
+ @Override
+ public boolean transactionsSupported() { return true; }
+
+ @Override
+ public void begin() { if (inTransaction) throw new JenaException("Transaction violation"); else {inTransaction = true; transaction++; } }
+
+ @Override
+ public void abort() { inTransaction = false; }
+
+ @Override
+ public void commit() { inTransaction = false; }
+
+ public int getTransactionNumber() { if (!inTransaction) throw new JenaException("Transaction violation"); else return transaction; }
+ }
+
+ class DummyTxnGraph extends GraphBase implements Graph {
+ TransactionHandler th = new DummyTxnHandler();
+ Graph base = new GraphMem();
+
+ @Override
+ public void performAdd( Triple t ) { base.add(t); }
+
+ @Override
+ public void performDelete( Triple t ) { base.delete(t); }
+
+ @Override
+ public TransactionHandler getTransactionHandler() { return th; }
+
+ @Override
+ protected ExtendedIterator<Triple> graphBaseFind(Triple triplePattern) {
+ return new DummyTxnIterator(base.find(triplePattern), this);
+ }
+ }
+
+ class DummyTxnIterator extends WrappedIterator<Triple> implements ExtendedIterator<Triple> {
+ DummyTxnGraph graph;
+ int transaction;
+
+ public DummyTxnIterator(Iterator<? extends Triple> base, DummyTxnGraph graph) {
+ super(base);
+ this.graph = graph;
+ this.transaction = currentTransaction();
+
+ }
+
+ private int currentTransaction() {
+ return ((DummyTxnHandler)graph.getTransactionHandler()).getTransactionNumber();
+ }
+
+ @Override
+ public boolean hasNext() {
+ if (transaction != currentTransaction()) throw new JenaException("Transaction violation");
+ return base.hasNext();
+ }
+
+ @Override
+ public Triple next() {
+ if (transaction != currentTransaction()) throw new JenaException("Transaction violation");
+ return base.next();
+ }
+
+ }
+}
diff --git a/jena-core/src/test/java/org/apache/jena/reasoner/rulesys/test/TestPackage.java b/jena-core/src/test/java/org/apache/jena/reasoner/rulesys/test/TestPackage.java
index 49b3cb9..a082d47 100755
--- a/jena-core/src/test/java/org/apache/jena/reasoner/rulesys/test/TestPackage.java
+++ b/jena-core/src/test/java/org/apache/jena/reasoner/rulesys/test/TestPackage.java
@@ -24,6 +24,7 @@ import junit.framework.TestSuite ;
import org.apache.jena.reasoner.rulesys.TestRuleUtil;
import org.apache.jena.reasoner.rulesys.impl.TestLPBRuleEngine;
import org.apache.jena.reasoner.rulesys.impl.TestLPBRuleEngineLeak;
+import org.apache.jena.reasoner.rulesys.impl.TestRestartableLBRule;
import org.slf4j.Logger ;
import org.slf4j.LoggerFactory ;
@@ -55,6 +56,7 @@ public class TestPackage extends TestSuite {
addTest( "TestLPDerivation", TestLPDerivation.suite() );
addTest( "TestLPBRuleEngine", TestLPBRuleEngine.suite() );
+ addTest( "TestRestartableLBRule", TestRestartableLBRule.suite() );
addTest( "TestFBRules", TestFBRules.suite() );
addTest( "TestGenericRules", TestGenericRules.suite() );
addTest( "TestRETE", TestRETE.suite() );
diff --git a/jena2.log b/jena2.log
new file mode 100644
index 0000000..e69de29