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:57 UTC

[jena] branch JENA-1719 created (now ddbaeaa)

This is an automated email from the ASF dual-hosted git repository.

der pushed a change to branch JENA-1719
in repository https://gitbox.apache.org/repos/asf/jena.git.


      at ddbaeaa  JENA-1719: Work around for iterators being held across transactions

This branch includes the following new commits:

     new ddbaeaa  JENA-1719: Work around for iterators being held across transactions

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[jena] 01/01: JENA-1719: Work around for iterators being held across transactions

Posted by de...@apache.org.
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