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