From e58cbcb043ed0c18f9df0f26b2da66c8745e3963 Mon Sep 17 00:00:00 2001 From: Andy Seaborne Date: Mon, 26 Feb 2018 16:42:03 +0000 Subject: [PATCH 1/8] Align to the style of sparql.core.TransactionalLock. --- ...java => TransactionalComponentByLock.java} | 48 ++++++++----------- 1 file changed, 19 insertions(+), 29 deletions(-) rename jena-db/jena-dboe-transaction/src/main/java/org/apache/jena/dboe/transaction/txn/{TransactionalMRSW.java => TransactionalComponentByLock.java} (71%) diff --git a/jena-db/jena-dboe-transaction/src/main/java/org/apache/jena/dboe/transaction/txn/TransactionalMRSW.java b/jena-db/jena-dboe-transaction/src/main/java/org/apache/jena/dboe/transaction/txn/TransactionalComponentByLock.java similarity index 71% rename from jena-db/jena-dboe-transaction/src/main/java/org/apache/jena/dboe/transaction/txn/TransactionalMRSW.java rename to jena-db/jena-dboe-transaction/src/main/java/org/apache/jena/dboe/transaction/txn/TransactionalComponentByLock.java index 0aa741ae18b..879548d0eca 100644 --- a/jena-db/jena-dboe-transaction/src/main/java/org/apache/jena/dboe/transaction/txn/TransactionalMRSW.java +++ b/jena-db/jena-dboe-transaction/src/main/java/org/apache/jena/dboe/transaction/txn/TransactionalComponentByLock.java @@ -19,24 +19,23 @@ package org.apache.jena.dboe.transaction.txn; import java.nio.ByteBuffer ; -import java.util.concurrent.locks.Lock ; -import java.util.concurrent.locks.ReadWriteLock ; -import java.util.concurrent.locks.ReentrantReadWriteLock ; import org.apache.jena.atlas.logging.Log ; import org.apache.jena.query.ReadWrite ; +import org.apache.jena.shared.Lock; +import org.apache.jena.shared.LockMRSW; +// ** Not used currently ** /** Implementation of the component interface for {@link TransactionalComponent}. * Useful for in-memory transactions that do not provide durability or abort (undo). * When retro fitting to other systems, that may be the best that can be done. */ -public class TransactionalMRSW extends TransactionalComponentLifecycle { - // MRSW implementation of TransactionMVCC - // XXX Update to Jena style TransactionalLock - private ReadWriteLock lock = new ReentrantReadWriteLock() ; +public class TransactionalComponentByLock extends TransactionalComponentLifecycle { + //See org.apache.jena.sparql.core.TransactionalLock + private Lock lock = new LockMRSW(); - public TransactionalMRSW(ComponentId componentId) { + private TransactionalComponentByLock(ComponentId componentId) { super(componentId) ; } @@ -55,15 +54,8 @@ public void finishRecovery() { } @Override public void cleanStart() {} - private Lock getLock() { - return ( ReadWrite.WRITE.equals(getReadWriteMode()) ) ? lock.writeLock() : lock.readLock() ; - } - @Override protected Object _begin(ReadWrite readWrite, TxnId thisTxnId) { - Lock lock = getLock() ; - // This is the point that makes this MRSW (readers OR writer), not MR+SW (readers and a writer) - lock.lock(); if ( isWriteTxn() ) startWriteTxn(); else @@ -79,22 +71,22 @@ private Object createState() { protected Object _promote(TxnId txnId, Object state) { // We have a read lock, the transaction coordinator has said // it's OK (from it's point-of-view) to promote so this should succeed. - // We have a read lock - theer are no other writers. - boolean b = lock.writeLock().tryLock(); - if ( ! b ) { - Log.warn(this, "Failed to promote"); - return false; + // We have a read lock - there are no other writers. + + // No lock promotion. + // Best we can do is unlock and lock again:-( + // This is "read committed" + if ( isReadTxn() ) { + finishReadTxn(); + startWriteTxn(); } - lock.readLock().unlock(); return createState(); } - // Checks. - - protected void startReadTxn() {} - protected void startWriteTxn() {} - protected void finishReadTxn() {} - protected void finishWriteTxn() {} + protected void startReadTxn() { lock.enterCriticalSection(Lock.READ); } + protected void startWriteTxn() { lock.enterCriticalSection(Lock.WRITE); } + protected void finishReadTxn() { lock.leaveCriticalSection(); } + protected void finishWriteTxn() { lock.leaveCriticalSection(); } @Override protected ByteBuffer _commitPrepare(TxnId txnId, Object obj) { @@ -126,12 +118,10 @@ protected void _shutdown() { } private void clearup() { - Lock lock = getLock() ; if ( isWriteTxn() ) finishWriteTxn(); else finishReadTxn(); - lock.unlock(); } } From 7a11a6b8090e0218ab02f364e2c3153edbced5b8 Mon Sep 17 00:00:00 2001 From: Andy Seaborne Date: Mon, 26 Feb 2018 16:42:32 +0000 Subject: [PATCH 2/8] isTransactionType -> isTransactionMode --- .../org/apache/jena/sparql/core/DatasetGraphMap.java | 2 +- .../apache/jena/sparql/core/DatasetGraphWithLock.java | 10 ++++++++-- .../org/apache/jena/sparql/core/DatasetGraphZero.java | 1 + .../org/apache/jena/sparql/core/TransactionalLock.java | 8 ++++---- .../java/org/apache/jena/sparql/graph/GraphZero.java | 9 +++++---- 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphMap.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphMap.java index c01f2503272..6eebbcf1c62 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphMap.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphMap.java @@ -152,7 +152,7 @@ public Graph getGraph(Node graphNode) { if ( g == null ) { g = getGraphCreate(graphNode); if ( g != null ) - graphs.put(graphNode, g); + addGraph(graphNode, g); } return g; } diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphWithLock.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphWithLock.java index ff79e977077..ff173b06870 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphWithLock.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphWithLock.java @@ -91,8 +91,14 @@ public boolean isInTransaction() { return transactional.isInTransaction() ; } - protected boolean isTransactionType(ReadWrite readWriteType) { - return transactional.isTransactionType(readWriteType) ; + protected boolean isTransactionMode(ReadWrite readWriteMode) { + return transactional.isTransactionMode(readWriteMode) ; + } + + /** @deprecated Use {@link #isTransactionMode} */ + @Deprecated + protected boolean isTransactionType(ReadWrite readWriteMode) { + return transactional.isTransactionMode(readWriteMode) ; } @Override diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphZero.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphZero.java index 46121790e7b..702f7bb9937 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphZero.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphZero.java @@ -34,6 +34,7 @@ */ public class DatasetGraphZero extends DatasetGraphBaseFind { + // Invariant DatasetGraph; it does have tarnsaction state so new object here. public static DatasetGraph create() { return new DatasetGraphZero(); } private Graph dftGraph = GraphZero.instance(); diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/TransactionalLock.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/TransactionalLock.java index 3db630ee870..930f9e202a5 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/core/TransactionalLock.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/TransactionalLock.java @@ -89,7 +89,7 @@ public static TransactionalLock createMutex() { return create(new LockMutex()) ; } - private TransactionalLock(Lock lock) { + protected TransactionalLock(Lock lock) { this.lock = lock ; } @@ -124,7 +124,7 @@ public void begin(TxnType txnType) { return Lib.readThreadLocal(txnType) ; } - // Lock propmotion required (Ok for mutex) + // Lock promotion required (Ok for mutex) @Override public boolean promote(Promote txnType) { @@ -148,7 +148,7 @@ public boolean isInTransaction() { return inTransaction.get(); } - public boolean isTransactionType(ReadWrite mode) { + public boolean isTransactionMode(ReadWrite mode) { if ( ! isInTransaction() ) return false; return Lib.readThreadLocal(txnMode) == mode; @@ -156,7 +156,7 @@ public boolean isTransactionType(ReadWrite mode) { @Override public void end() { - if ( isTransactionType(ReadWrite.WRITE) ) + if ( isTransactionMode(ReadWrite.WRITE) ) error("Write transaction - no commit or abort before end()") ; endOnce() ; } diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/graph/GraphZero.java b/jena-arq/src/main/java/org/apache/jena/sparql/graph/GraphZero.java index aba5cacdb28..479340b0018 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/graph/GraphZero.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/graph/GraphZero.java @@ -28,14 +28,14 @@ import org.apache.jena.util.iterator.ExtendedIterator; import org.apache.jena.util.iterator.NullIterator; -/** Immutable empty graph. +/** Invariant empty graph. * @see GraphSink */ public class GraphZero extends GraphBase { - private static Graph graph = new GraphZero(); public static Graph instance() { - return graph; + // It has transaction state do unsafe to share one object on one thread. + return new GraphZero(); } private GraphZero() {} @@ -45,10 +45,11 @@ protected ExtendedIterator graphBaseFind(Triple triplePattern) { return NullIterator.instance(); } + private TransactionHandler transactionHandler = new TransactionHandlerNull(); @Override public TransactionHandler getTransactionHandler() { - return new TransactionHandlerNull(); + return transactionHandler; } @Override From 4a819f41ac8d19cb9bc043e58f9997b55143f54a Mon Sep 17 00:00:00 2001 From: Andy Seaborne Date: Tue, 27 Feb 2018 23:22:01 +0000 Subject: [PATCH 3/8] Skolemization function --- .../src/main/java/org/apache/jena/riot/system/RiotLib.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/jena-arq/src/main/java/org/apache/jena/riot/system/RiotLib.java b/jena-arq/src/main/java/org/apache/jena/riot/system/RiotLib.java index 769f8af553c..8966735a78c 100644 --- a/jena-arq/src/main/java/org/apache/jena/riot/system/RiotLib.java +++ b/jena-arq/src/main/java/org/apache/jena/riot/system/RiotLib.java @@ -50,6 +50,7 @@ import org.apache.jena.sparql.core.DatasetGraph; import org.apache.jena.sparql.core.DatasetGraphFactory; import org.apache.jena.sparql.core.Quad; +import org.apache.jena.sparql.expr.nodevalue.NodeFunctions; import org.apache.jena.sparql.util.Context; import org.apache.jena.util.iterator.ExtendedIterator; @@ -74,6 +75,11 @@ public static Node createIRIorBNode(String iri) } return NodeFactory.createURI(iri); } + + /** "Skolemize": BlankNode to IRI else return node unchanged. */ + public static Node blankNodeToIri(Node node) { + return NodeFunctions.blankNodeToIri(node); + } /** Test whether a IRI is a ARQ-encoded blank node. */ public static boolean isBNodeIRI(String iri) { From 4e724c2f82f7b9ef38dbeca4294c42ea6aa919a1 Mon Sep 17 00:00:00 2001 From: Andy Seaborne Date: Sat, 3 Mar 2018 16:25:48 +0000 Subject: [PATCH 4/8] JENA-1492: Pass transactions down from general datasets --- .../arq/examples/riot/ExJsonLD.java | 14 +- .../org/apache/jena/query/DatasetFactory.java | 32 ++- .../jena/query/QueryExecutionFactory.java | 4 +- .../org/apache/jena/riot/system/RiotLib.java | 19 +- .../apache/jena/sparql/algebra/Algebra.java | 4 +- .../jena/sparql/core/DatasetGraphFactory.java | 21 +- .../jena/sparql/core/DatasetGraphMap.java | 2 +- .../jena/sparql/core/DatasetGraphMapLink.java | 101 ++++---- .../jena/sparql/core/DatasetGraphOne.java | 22 +- .../apache/jena/sparql/core/DatasetImpl.java | 6 +- .../apache/jena/sparql/core/DatasetOne.java | 84 +++++++ .../jena/sparql/core/TxnDataset2Graph.java | 224 ++++++++++++++++++ .../core/assembler/DatasetAssemblerVocab.java | 3 + .../assembler/DatasetGraphOneAssembler.java | 82 +++++++ .../jena/sparql/util/QueryExecUtils.java | 2 +- .../query/TestParameterizedSparqlString.java | 2 +- .../jena/riot/lang/ParserTestBaseLib.java | 2 +- .../jena/riot/writer/TestJsonLDWriter.java | 4 +- .../api/TestQueryExecutionTimeout1.java | 2 +- .../api/TestQueryExecutionTimeout2.java | 2 +- .../transaction/TestTransactionSupport.java | 4 +- .../jena/dboe/trans/bplustree/BPTreeNode.java | 18 -- .../jena/tdb2/store/DatasetGraphTDB.java | 17 +- .../jena/tdb2/store/GraphViewSwitchable.java | 11 + .../test/txn/TS_TranactionIntegration.java | 31 +++ .../jena/test/txn/TestDataset2Graph.java | 157 ++++++++++++ .../jena/rdfconnection/RDFConnection.java | 2 - .../rdfconnection/RDFConnectionLocal.java | 3 +- 28 files changed, 750 insertions(+), 125 deletions(-) create mode 100644 jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetOne.java create mode 100644 jena-arq/src/main/java/org/apache/jena/sparql/core/TxnDataset2Graph.java create mode 100644 jena-arq/src/main/java/org/apache/jena/sparql/core/assembler/DatasetGraphOneAssembler.java create mode 100644 jena-integration-tests/src/test/java/org/apache/jena/test/txn/TS_TranactionIntegration.java create mode 100644 jena-integration-tests/src/test/java/org/apache/jena/test/txn/TestDataset2Graph.java diff --git a/jena-arq/src-examples/arq/examples/riot/ExJsonLD.java b/jena-arq/src-examples/arq/examples/riot/ExJsonLD.java index 677e58478be..21e5d675441 100644 --- a/jena-arq/src-examples/arq/examples/riot/ExJsonLD.java +++ b/jena-arq/src-examples/arq/examples/riot/ExJsonLD.java @@ -23,6 +23,8 @@ import java.io.OutputStream; import java.io.StringReader; +import com.github.jsonldjava.core.JsonLdOptions; + import org.apache.jena.query.DatasetFactory; import org.apache.jena.rdf.model.Model ; import org.apache.jena.rdf.model.ModelFactory; @@ -38,8 +40,6 @@ import org.apache.jena.vocabulary.RDF; import org.apache.jena.vocabulary.RDFS; -import com.github.jsonldjava.core.JsonLdOptions; - /** Example writing as JSON-LD */ public class ExJsonLD { @@ -123,7 +123,7 @@ public void moreControl() { // the write method takes a DatasetGraph as input to represent the data that we want to output // Let's create one from our model: - DatasetGraph g = DatasetFactory.create(m).asDatasetGraph(); + DatasetGraph g = DatasetFactory.wrap(m).asDatasetGraph(); // and let's use the write method to output the data in json-ld compact format, // passing a null Context for the moment @@ -161,7 +161,7 @@ void controllingAtContext() { Model m = aSimpleModel(); m.setNsPrefix("ex", "http://www.ex.com/"); m.setNsPrefix("sh", "http://schema.org/"); - DatasetGraph g = DatasetFactory.create(m).asDatasetGraph(); + DatasetGraph g = DatasetFactory.wrap(m).asDatasetGraph(); JsonLDWriteContext ctx = new JsonLDWriteContext(); // When no value for the "@context" is provided, @@ -199,7 +199,7 @@ void settingAtContextToURI() { // "@context" : "http://schema.org/" Model m = aModelThatOnlyUsesSchemaDotOrg(); - DatasetGraph g = DatasetFactory.create(m).asDatasetGraph(); + DatasetGraph g = DatasetFactory.wrap(m).asDatasetGraph(); JsonLDWriteContext ctx = new JsonLDWriteContext(); // The following should work, but unfortunately it doesn't (with JSONLD-java 0.8.3): @@ -270,7 +270,7 @@ void frame() { m.add(s, m.createProperty(ns + "name"), "Not a person"); m.add(s, RDF.type, m.createResource(ns + "Event")); - DatasetGraph g = DatasetFactory.create(m).asDatasetGraph(); + DatasetGraph g = DatasetFactory.wrap(m).asDatasetGraph(); JsonLDWriteContext ctx = new JsonLDWriteContext(); // only output the persons using a frame @@ -289,7 +289,7 @@ void controllingJsonLDApiOptions() { Model m = aSimpleModel(); m.setNsPrefix("ex", "http://www.ex.com/"); m.setNsPrefix("sh", "http://schema.org/"); - DatasetGraph g = DatasetFactory.create(m).asDatasetGraph(); + DatasetGraph g = DatasetFactory.wrap(m).asDatasetGraph(); JsonLDWriteContext ctx = new JsonLDWriteContext(); JsonLdOptions opts = new JsonLdOptions(); ctx.setOptions(opts); diff --git a/jena-arq/src/main/java/org/apache/jena/query/DatasetFactory.java b/jena-arq/src/main/java/org/apache/jena/query/DatasetFactory.java index 7ec58b802ed..45dac4a552f 100644 --- a/jena-arq/src/main/java/org/apache/jena/query/DatasetFactory.java +++ b/jena-arq/src/main/java/org/apache/jena/query/DatasetFactory.java @@ -28,6 +28,7 @@ import org.apache.jena.sparql.core.DatasetGraph; import org.apache.jena.sparql.core.DatasetGraphFactory; import org.apache.jena.sparql.core.DatasetImpl; +import org.apache.jena.sparql.core.DatasetOne; import org.apache.jena.sparql.core.assembler.DatasetAssembler; import org.apache.jena.sparql.util.DatasetUtils; import org.apache.jena.sparql.util.graph.GraphUtils; @@ -109,9 +110,15 @@ public static Dataset createMem() { } /** - * @param model The model for the default graph - * @return a dataset with the given model as the default graph - */ + * Create a dataset, starting with the model argument as the default graph of the + * dataset. Named graphs can be added. + *

+ * Use {@link #wrap(Model)} to put dataset functionality around a single + * model when named graphs will not be added. + * + * @param model The model for the default graph + * @return a dataset with the given model as the default graph + */ public static Dataset create(Model model) { Objects.requireNonNull(model, "Default model must be provided") ; return new DatasetImpl(model); @@ -135,11 +142,26 @@ public static Dataset create(Dataset dataset) { * @return Dataset */ public static Dataset wrap(DatasetGraph dataset) { - Objects.requireNonNull(dataset, "Can't wrap a null reference") ; + Objects.requireNonNull(dataset, "Can't wrap a null DatasetGraph reference") ; return DatasetImpl.wrap(dataset); } - /** + /** + * Wrap a {@link Model} to make a dataset; the model is the default graph of the RDF Dataset. + * + * This dataset can not have additional models + * added to it, including indirectly through SPARQL Update + * adding new graphs. + * + * @param model + * @return Dataset + */ + public static Dataset wrap(Model model) { + Objects.requireNonNull(model, "Can't wrap a null Model reference") ; + return DatasetOne.create(model); + } + + /** * Wrap a {@link DatasetGraph} to make a dataset * * @param dataset DatasetGraph diff --git a/jena-arq/src/main/java/org/apache/jena/query/QueryExecutionFactory.java b/jena-arq/src/main/java/org/apache/jena/query/QueryExecutionFactory.java index 7e2adf1dece..2414d3ac2b8 100644 --- a/jena-arq/src/main/java/org/apache/jena/query/QueryExecutionFactory.java +++ b/jena-arq/src/main/java/org/apache/jena/query/QueryExecutionFactory.java @@ -126,7 +126,7 @@ static public QueryExecution create(String queryStr, Syntax syntax, Dataset data static public QueryExecution create(Query query, Model model) { checkArg(query) ; checkArg(model) ; - return make(query, DatasetFactory.create(model)) ; + return make(query, DatasetFactory.wrap(model)) ; } /** Create a QueryExecution to execute over the Model. @@ -201,7 +201,7 @@ static public QueryExecution create(String queryStr, Syntax syntax, QuerySolutio */ static public QueryExecution create(Query query, Model model, QuerySolution initialBinding) { checkArg(model) ; - return create(query, DatasetFactory.create(model), initialBinding) ; + return create(query, DatasetFactory.wrap(model), initialBinding) ; } /** Create a QueryExecution to execute over the Model, diff --git a/jena-arq/src/main/java/org/apache/jena/riot/system/RiotLib.java b/jena-arq/src/main/java/org/apache/jena/riot/system/RiotLib.java index 8966735a78c..359f746adfb 100644 --- a/jena-arq/src/main/java/org/apache/jena/riot/system/RiotLib.java +++ b/jena-arq/src/main/java/org/apache/jena/riot/system/RiotLib.java @@ -327,14 +327,17 @@ public static void writePrefixes(IndentedWriter out, PrefixMap prefixMap) { } } - /** Returns dataset that wraps a graph */ - public static DatasetGraph dataset(Graph graph) - { - return DatasetGraphFactory.createOneGraph(graph); + /** Returns dataset that wraps a graph + * @deprecated Use {@link DatasetGraphFactory#wrap(Graph)} + * @param graph + * @return DatasetGraph + */ + @Deprecated + public static DatasetGraph xdataset(Graph graph) { + return DatasetGraphFactory.wrap(graph); } - public static PrefixMap prefixMap(DatasetGraph dsg) - { + public static PrefixMap prefixMap(DatasetGraph dsg) { return PrefixMapFactory.create(dsg.getDefaultGraph().getPrefixMapping()); } @@ -413,10 +416,10 @@ public Lang getLang() @Override public void write(OutputStream out, Graph graph, PrefixMap prefixMap, String baseURI, Context context) - { writer.write(out, RiotLib.dataset(graph), prefixMap, baseURI, context); } + { writer.write(out, DatasetGraphFactory.wrap(graph), prefixMap, baseURI, context); } @Override public void write(Writer out, Graph graph, PrefixMap prefixMap, String baseURI, Context context) - { writer.write(out, RiotLib.dataset(graph), prefixMap, baseURI, context); } + { writer.write(out, DatasetGraphFactory.wrap(graph), prefixMap, baseURI, context); } } } diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/Algebra.java b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/Algebra.java index ed3af579957..7657c6d2ffa 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/algebra/Algebra.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/algebra/Algebra.java @@ -136,7 +136,7 @@ static public QueryIterator exec(Op op, Model model) static public QueryIterator exec(Op op, Graph graph) { - return exec(op, DatasetGraphFactory.createOneGraph(graph)) ; + return exec(op, DatasetGraphFactory.wrap(graph)) ; } static public QueryIterator exec(Op op, DatasetGraph ds) @@ -160,7 +160,7 @@ static public QueryIterator execRef(Op op, Model model) static public QueryIterator execRef(Op op, Graph graph) { - return execRef(op, DatasetGraphFactory.createOneGraph(graph)) ; + return execRef(op, DatasetGraphFactory.wrap(graph)) ; } static public QueryIterator execRef(Op op, DatasetGraph dsg) diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphFactory.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphFactory.java index 18060fc4250..29af4018d47 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphFactory.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphFactory.java @@ -36,7 +36,8 @@ public class DatasetGraphFactory *

* This implementation copies models when {@link Dataset#addNamedModel(String, Model)} is called. *

- * This implementation does not support serialized transactions (it only provides MRSW locking). + * This implementation provides "best effort" transactions; it only provides MRSW locking. + * Use {@link #createTxnMem} for a proper in-memeory transactional {@code DatasetGraph}. * * @see #createTxnMem */ @@ -45,7 +46,7 @@ public static DatasetGraph create() { } /** - * Create an in-memory. transactional {@link Dataset}. + * Create an in-memory, transactional {@link Dataset}. *

* This fully supports transactions, including abort to roll-back changes. * It provides "autocommit" if operations are performed @@ -60,7 +61,7 @@ public static DatasetGraph create() { /** * Create a general-purpose {@link Dataset}.
- * Any graphs needed are in-memory unless explciitly added with {@link Dataset#addNamedModel}. + * Any graphs needed are in-memory unless explicitly added with {@link Dataset#addNamedModel}. *

* This dataset type can contain graphs from any source when added via {@link Dataset#addNamedModel}. * These are held as links to the supplied graph and not copied. @@ -73,7 +74,9 @@ public static DatasetGraph create() { * @see #createTxnMem * @return a general-purpose Dataset */ - public static DatasetGraph createGeneral() { return new DatasetGraphMapLink(graphMakerMem) ; } + public static DatasetGraph createGeneral() { + return new DatasetGraphMapLink(graphMakerMem.create(null), graphMakerMem) ; + } /** Create an in-memory {@link Dataset}. *

@@ -137,7 +140,15 @@ public static DatasetGraph create(Graph graph) { /** * Create a DatasetGraph which only ever has a single default graph. */ - public static DatasetGraph createOneGraph(Graph graph) { return new DatasetGraphOne(graph) ; } + public static DatasetGraph wrap(Graph graph) { return DatasetGraphOne.create(graph) ; } + + + /** + * Create a DatasetGraph which only ever has a single default graph. + * @deprecated Use {#wrap(Graph)} + */ + @Deprecated + public static DatasetGraph createOneGraph(Graph graph) { return wrap(graph) ; } /** Interface for making graphs when a dataset needs to add a new graph. * Return null for no graph created. diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphMap.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphMap.java index 6eebbcf1c62..c01f2503272 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphMap.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphMap.java @@ -152,7 +152,7 @@ public Graph getGraph(Node graphNode) { if ( g == null ) { g = getGraphCreate(graphNode); if ( g != null ) - addGraph(graphNode, g); + graphs.put(graphNode, g); } return g; } diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphMapLink.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphMapLink.java index 90efd0ed2d5..a0d834c45e5 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphMapLink.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphMapLink.java @@ -28,11 +28,12 @@ import org.apache.jena.query.TxnType; import org.apache.jena.sparql.SystemARQ ; import org.apache.jena.sparql.core.DatasetGraphFactory.GraphMaker ; +import org.apache.jena.sparql.graph.GraphFactory; import org.apache.jena.sparql.graph.GraphUnionRead ; +import org.apache.jena.sparql.graph.GraphZero; -/** Implementation of a DatasetGraph as an extensible set of graphs. - *

- * Graphs are held by reference. Care is needed when manipulating their contents +/** Implementation of a DatasetGraph as an extensible set of graphs where graphs are held by reference. + * Care is needed when manipulating their contents * especially if they are also in another {@code DatasetGraph}. *

* See {@link DatasetGraphMap} for an implementation that copies graphs @@ -44,11 +45,13 @@ */ public class DatasetGraphMapLink extends DatasetGraphCollection { - private final GraphMaker graphMaker ; private final Map graphs = new HashMap<>() ; private Graph defaultGraph ; + private final Transactional txn; + private final TxnDataset2Graph txnDsg2Graph; + private static GraphMaker dftGraphMaker = (name) -> GraphFactory.createDefaultGraph(); /** * Create a new {@code DatasetGraph} that copies the dataset structure of default @@ -56,7 +59,7 @@ public class DatasetGraphMapLink extends DatasetGraphCollection * Any new graphs needed are separate from the original dataset and created in-memory. */ public static DatasetGraph cloneStructure(DatasetGraph dsg) { - return new DatasetGraphMapLink(dsg); + return cloneStructure(dsg, dftGraphMaker); } /** @@ -66,64 +69,44 @@ public static DatasetGraph cloneStructure(DatasetGraph dsg) { * to the {@link GraphMaker}. */ public static DatasetGraph cloneStructure(DatasetGraph dsg, GraphMaker graphMaker) { - return new DatasetGraphMapLink(dsg, graphMaker); + DatasetGraphMapLink dsg2 = new DatasetGraphMapLink((Graph)null, graphMaker); + linkGraphs(dsg, dsg2); + return dsg2; } - /** Create a new DatasetGraph that initially shares the graphs of the - * given DatasetGraph. Adding/removing graphs will only affect this - * object, not the argument DatasetGraph but changes to shared - * graphs are seen by both objects. - */ - private DatasetGraphMapLink(DatasetGraph dsg, GraphMaker graphMaker) { - this.graphMaker = graphMaker ; - this.defaultGraph = dsg.getDefaultGraph() ; - for ( Iterator names = dsg.listGraphNodes() ; names.hasNext() ; ) { + private static void linkGraphs(DatasetGraph srcDsg, DatasetGraphMapLink dstDsg) { + dstDsg.defaultGraph = srcDsg.getDefaultGraph(); + for ( Iterator names = srcDsg.listGraphNodes() ; names.hasNext() ; ) { Node gn = names.next() ; - addGraph(gn, dsg.getGraph(gn)) ; + dstDsg.addGraph(gn, srcDsg.getGraph(gn)) ; } } - /** - * A {@code DatasetGraph} with graphs for default and named graphs as given - * but new graphs are created in memory. - */ - private DatasetGraphMapLink(DatasetGraph dsg) { - this(dsg, DatasetGraphFactory.graphMakerMem) ; - } - - private DatasetGraphMapLink(Graph dftGraph, GraphMaker graphMaker) { - this.graphMaker = graphMaker; - this.defaultGraph = dftGraph ; - } - -// /** A {@code DatasetGraph} with in-memory graphs for default and named graphs as needed */ -// private DatasetGraphMapLink() { -// this(DatasetGraphFactory.memGraphMaker) ; -// } - - /** - * A {@code DatasetGraph} with graph from the gve {@link GraphMaker} for default and - * named graphs as needed. This is the constructor used for - * DatasetFactory.createGeneral. - */ - /*package*/ DatasetGraphMapLink(GraphMaker graphMaker) { - this(graphMaker.create(null), graphMaker) ; - } - /** A {@code DatasetGraph} that uses the given graph for the default graph * and create in-memory graphs for named graphs as needed */ public DatasetGraphMapLink(Graph dftGraph) { - this.defaultGraph = dftGraph ; - this.graphMaker = DatasetGraphFactory.graphMakerMem ; + this(dftGraph, dftGraphMaker); } - // ---- - private final Transactional txn = TransactionalLock.createMRSW() ; + // This is the root constructor. + @SuppressWarnings("deprecation") + /*package*/DatasetGraphMapLink(Graph dftGraph, GraphMaker graphMaker) { + this.graphMaker = graphMaker; + this.defaultGraph = dftGraph; + if ( TxnDataset2Graph.TXN_DSG_GRAPH ) { + txnDsg2Graph = new TxnDataset2Graph(dftGraph); + txn = txnDsg2Graph; + } else { + txnDsg2Graph = null; + txn = TransactionalLock.createMRSW(); + } + } @Override public void commit() { - SystemARQ.sync(this); + if ( txnDsg2Graph == null ) + SystemARQ.sync(this); txn.commit() ; } @@ -131,7 +114,7 @@ public void commit() { @Override public void begin(TxnType txnType) { txn.begin(txnType); } @Override public void begin(ReadWrite mode) { txn.begin(mode); } @Override public boolean promote(Promote txnType) { return txn.promote(txnType); } - //@Override public void commit() { txn.commit(); } + //Above: commit() @Override public void abort() { txn.abort(); } @Override public boolean isInTransaction() { return txn.isInTransaction(); } @Override public void end() { txn.end(); } @@ -155,7 +138,7 @@ public Graph getDefaultGraph() { @Override public Graph getGraph(Node graphNode) { - // Same as DatasetMap.getGraph but we inherit differently. + // Same as DatasetGraphMap.getGraph but we inherit differently. if ( Quad.isUnionGraph(graphNode) ) return new GraphUnionRead(this) ; if ( Quad.isDefaultGraph(graphNode)) @@ -165,30 +148,40 @@ public Graph getGraph(Node graphNode) { if ( g == null ) { g = getGraphCreate(graphNode); if ( g != null ) - graphs.put(graphNode, g); + addGraph(graphNode, g); } return g; } - /** Called from getGraph when a nonexistent graph is asked for. + /** + * Called from getGraph when a nonexistent graph is asked for. * Return null for "nothing created as a graph" */ - protected Graph getGraphCreate(Node graphNode) { + protected Graph getGraphCreate(Node graphNode) { return graphMaker.create(graphNode) ; } @Override public void addGraph(Node graphName, Graph graph) { + if ( txnDsg2Graph != null ) + txnDsg2Graph.addGraph(graph); graphs.put(graphName, graph); } @Override public void removeGraph(Node graphName) { - graphs.remove(graphName); + Graph g = graphs.remove(graphName); + if ( g != null && txnDsg2Graph != null ) + txnDsg2Graph.removeGraph(g); } @Override public void setDefaultGraph(Graph g) { + if ( g == null ) + // Always have a default graph of some kind. + g = GraphZero.instance(); + if ( txnDsg2Graph != null ) + txnDsg2Graph.addGraph(g); defaultGraph = g; } diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphOne.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphOne.java index 89d76cbda07..faf37c7b6f5 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphOne.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphOne.java @@ -32,10 +32,11 @@ /** DatasetGraph of a single graph as default graph. *

- * Fixed as one graph (the default) - can not add named graphs. + * Fixed as one graph (the default) - named graphs can notbe added nor the default graph changed, only the contents modified. *

- * Passes transactions down to a nominated backing {@link DatasetGraph} - * + * Ths dataset passes transactions down to a nominated backing {@link DatasetGraph} + *

+ * It is particular suitable for use with an interference graph. */ public class DatasetGraphOne extends DatasetGraphBaseFind { private final Graph graph; @@ -43,21 +44,30 @@ public class DatasetGraphOne extends DatasetGraphBaseFind { private final Transactional txn; private final boolean supportsAbort; - public DatasetGraphOne(Graph graph, DatasetGraph backing) { + public static DatasetGraph create(Graph graph) { + return new DatasetGraphOne(graph); + } + + private DatasetGraphOne(Graph graph, DatasetGraph backing) { this.graph = graph; backingDGS = backing; supportsAbort = backing.supportsTransactionAbort(); txn = backing; } - public DatasetGraphOne(Graph graph) { + @SuppressWarnings("deprecation") + private DatasetGraphOne(Graph graph) { this.graph = graph; if ( graph instanceof GraphView ) { backingDGS = ((GraphView)graph).getDataset(); txn = backingDGS; supportsAbort = backingDGS.supportsTransactionAbort(); } else { - txn = TransactionalLock.createMRSW(); + // JENA-1492 - pass down transactions. + if ( TxnDataset2Graph.TXN_DSG_GRAPH ) + txn = new TxnDataset2Graph(graph); + else + txn = TransactionalLock.createMRSW(); backingDGS = null; supportsAbort = false; } diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetImpl.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetImpl.java index 2a145b18b3a..614b82faaec 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetImpl.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetImpl.java @@ -63,8 +63,7 @@ protected DatasetImpl(DatasetGraph dsg, Transactional transactional) { /** Create a Dataset with the model as default model. * Named models must be explicitly added to identify the storage to be used. */ - public DatasetImpl(Model model) - { + public DatasetImpl(Model model) { this.dsg = DatasetGraphFactory.create(model.getGraph()) ; this.transactional = dsg ; } @@ -73,8 +72,7 @@ public DatasetImpl(Model model) * while sharing the graphs themselves. */ @Deprecated - public DatasetImpl(Dataset ds) - { + public DatasetImpl(Dataset ds) { this(DatasetGraphFactory.cloneStructure(ds.asDatasetGraph())) ; } diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetOne.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetOne.java new file mode 100644 index 00000000000..27b597a384e --- /dev/null +++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetOne.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.sparql.core; + +import org.apache.jena.graph.Node; +import org.apache.jena.graph.NodeFactory; +import org.apache.jena.query.Dataset; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.sparql.core.DatasetGraphOne; +import org.apache.jena.sparql.core.DatasetImpl; + +/** + * A dataset that just hold a single model as the default graph. + * It is particularly appropriate for use with inference models. + * + * @apiNote + * This class makes the use of DatasetImpl with one fixed model clearer. It may + * become useful to have a separate implementation altogether at some time. + */ +public class DatasetOne extends DatasetImpl { + public static Dataset create(Model model) { + return new DatasetOne(model); + } + + private final Model defaultModel; + + public DatasetOne(Model model) { + super(DatasetGraphOne.create(model.getGraph())); + this.defaultModel = model; + } + + + @Override + public Model getDefaultModel() { + return defaultModel; + } + + @Override + public Dataset setDefaultModel(Model model) { + throw new UnsupportedOperationException("Can not set the default model after a DatasetOne has been created"); + } + + @Override + public Model getNamedModel(String uri) { + checkGraphName(uri) ; + Node n = NodeFactory.createURI(uri) ; + return graph2model(dsg.getGraph(n)) ; + } + + @Override + public Dataset addNamedModel(String uri, Model model) { + throw new UnsupportedOperationException("Can not add a named mode to DatasetOne"); + } + + @Override + public Dataset removeNamedModel(String uri) { + return this; + } + + @Override + public Dataset replaceNamedModel(String uri, Model model) { + throw new UnsupportedOperationException("Can not replace a named model in DatasetOne"); + } + @Override + public boolean containsNamedModel(String uri) { + return false; + } +} diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/TxnDataset2Graph.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/TxnDataset2Graph.java new file mode 100644 index 00000000000..d03cc6eb7c7 --- /dev/null +++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/TxnDataset2Graph.java @@ -0,0 +1,224 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.sparql.core; + +import java.util.*; +import java.util.function.Consumer; + +import org.apache.jena.graph.Graph; +import org.apache.jena.graph.TransactionHandler; +import org.apache.jena.query.ReadWrite; +import org.apache.jena.query.TxnType; +import org.apache.jena.reasoner.InfGraph; +import org.apache.jena.shared.LockMRSW; +import org.apache.jena.sparql.JenaTransactionException; + +/** + * A {@link Transactional} that passes the transaction operations down to transactions on + * independent graphs. + *

+ * There are limitations: + *

    + *
  • we can't atomically do all the commits together in the crash situation. + *
  • This {@code Transactional} maintains a MRSW policy because that is all that is + * required of graphs in general. + *
+ * It does cover the important case of one graph ({@link DatasetGraphOne}) where the one + * graph is an InfGraph and should work when the graphs in the dataset is not changing or + * when a new memory graph is added mid-transaction. + *

+ * This is not "nested transactions" - theer is no overall "commit" or "abort". If + * failure/restart occurs, some graphs may have commited and others not. It is the best + * that can be done given for an arbitrary collection of graphs, backed by different + * storage and having different capabilities. + *

+ * Best practice is to change the graph membership outside of any transaction, + * ideally at setup time of the object using this class. (Caution: SPARQL Update + * can create graphs. + * @See {@link DatasetGraphMapLink} + * @See {@link DatasetGraphOne} + */ +public class TxnDataset2Graph extends TransactionalLock { + /** Control whether to pass down transactions from the dataset to the graph in the dataset. + * This should be set to "true". + * This is temporary flag during the transition because the change at Jena 3.7.0 + * needs to be proven in real deployments as well as testing. + * "false" restores the Jena 3.6.0 and before behaviour (transactions not passed down). + * See JENA-1492. + * @deprecated This flag will be removed. + */ + @Deprecated + public static boolean TXN_DSG_GRAPH = true; + + private Graph primary; + // Object key may be a graph or a DSG is the graph is a GraphView. + // This avoids starting a tranasction on the same storage unit twice. + private Map handlers = new HashMap<>(); + + private Object lock = new Object(); + + public TxnDataset2Graph(Graph primaryGraph, Graph ... otherGraphs) { + super(new LockMRSW()); + primary = primaryGraph; + handlers = buildHandlerSet(primary, Arrays.asList(otherGraphs)); + } + + private static Map buildHandlerSet(Graph primary, Collection graphs) { + Map handlers = new HashMap<>(); + addHandler(handlers, primary); + graphs.forEach(g->addHandler(handlers,g)); + return handlers; + } + + private static void addHandler(Map handlers, Graph graph) { + TransactionHandler th = graph.getTransactionHandler(); + if ( ! th.transactionsSupported() ) + return; + Object key = calcKey(graph); + if ( th.transactionsSupported() ) + handlers.put(key, th) ; + } + + // Determine the key - an object that is the unit of transactions. + // For two graphs form the same DatasetGraph, i.e. GraphView, there should be one transaction. + private static Object calcKey(Graph graph) { + if ( graph instanceof GraphView ) + // Use the database as the key so that transactions are started once-per-storage. + // This the case of a graph from some storage being plavced in a general dataset. + return ((GraphView)graph).getDataset(); + if ( graph instanceof InfGraph ) + // InfGraph does actual pass done in its TransactionHandler. + // This allows the base graph to be included in the dataset as well as the InfGraph. + return calcKey(((InfGraph)graph).getRawGraph()); + +// if ( graph instanceof GraphWrapper ) +// return calcKey(((GraphWrapper)graph).get()); +// if ( graph instanceof WrappedGraph ) +// return calcKey(((WrappedGraph)graph).getWrapped()); + return graph; + } + + private static void removeHandler(Map handlers, Graph graph) { + Object key = calcKey(graph); + handlers.remove(graph); + } + + // Attempt to manage the graph transactions during a transaction. + // Imperfect for removal, we don't know whether to call commit() or abort(). + // Works for adding. + // Generally better not to change the graphs during a transaction, just set them once + // on creation. + + public void addGraph(Graph graph) { + checkNotReadMode(); + if ( graph == null ) + return; + if ( ! handlers.containsKey(graph) ) { + // Add if new. + addHandler(handlers, graph) ; + if ( super.isInTransaction() ) { + // If we are in a transaction, start the subtransaction. + TransactionHandler th = handlers.get(graph); + if ( th != null ) + th.begin(); + } + } + } + + public void removeGraph(Graph graph) { + checkNotReadMode(); + if ( graph == null ) + return; + if ( ! super.isInTransaction() ) { + // Not in transaction, do now. + removeHandler(handlers, graph); + return; + } + // Queue to be removed at the end. + Set toBeRemoved = removedGraphs.get(); + if ( toBeRemoved == null ) { + // Lazy set of the HashSet. + toBeRemoved = new HashSet<>(); + removedGraphs.set(toBeRemoved); + } + removedGraphs.get().add(graph); + } + + public void setPrimaryGraph(Graph graph) { + checkNotReadMode(); + if ( graph == null ) + return; + removeGraph(graph); + addGraph(graph); + } + + private void handlers(Consumer action) { + synchronized (lock) { + handlers.forEach((g,th)->action.accept(th)); + } + } + + private void checkNotReadMode() { + if ( !super.isInTransaction() ) + return; + if ( super.isTransactionMode(ReadWrite.READ) ) + throw new JenaTransactionException("In READ mode in transaction"); + } + + private ThreadLocal> removedGraphs = ThreadLocal.withInitial(()->null); + private void start() {} + private void finish() { + if ( ! super.isTransactionMode(ReadWrite.WRITE) ) + return; + // This is called inside the lock of super. + Set toBeRemoved = removedGraphs.get(); + removedGraphs.remove(); + if ( toBeRemoved == null ) + return ; + toBeRemoved.forEach(g->removeHandler(handlers, g)); + } + + // TransactionalLock.begin(ReadWrite) calls begin(TxnType) + @Override + public void begin(TxnType type) { + super.begin(type); + // Whatever the type. Graph Transactions do not allow for "read-only". + start(); + handlers(h->h.begin()); + } + + // The MRSW lock means this isn't possible. See super.promote. +// @Override +// public boolean promote(Promote mode) + + @Override + public void commit() { + handlers(h->h.commit()); + // Before super.commit - we stil hold the lock. + finish(); + super.commit(); + } + + @Override + public void abort() { + handlers(h -> h.abort()); + finish(); + super.abort(); + } +} diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/assembler/DatasetAssemblerVocab.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/assembler/DatasetAssemblerVocab.java index 17d13c0f78a..5d26abe9dff 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/core/assembler/DatasetAssemblerVocab.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/assembler/DatasetAssemblerVocab.java @@ -30,6 +30,9 @@ public class DatasetAssemblerVocab // General dataset public static final Resource tDataset = ResourceFactory.createResource(NS+"RDFDataset") ; + // Dataset to hold exactly one model. + public static final Resource tDatasetOne = ResourceFactory.createResource(NS+"RDFDatasetOne") ; + // In-memory dataset public static final Resource tDatasetTxnMem = ResourceFactory.createResource(NS+"DatasetTxnMem") ; public static final Resource tMemoryDataset = ResourceFactory.createResource(NS+"MemoryDataset") ; diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/assembler/DatasetGraphOneAssembler.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/assembler/DatasetGraphOneAssembler.java new file mode 100644 index 00000000000..ed33ebf867c --- /dev/null +++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/assembler/DatasetGraphOneAssembler.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.sparql.core.assembler; + +import java.util.List; + +import org.apache.jena.assembler.Assembler; +import org.apache.jena.assembler.Mode; +import org.apache.jena.assembler.assemblers.AssemblerBase; +import org.apache.jena.assembler.exceptions.AssemblerException; +import org.apache.jena.query.Dataset; +import org.apache.jena.query.DatasetFactory; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.RDFNode; +import org.apache.jena.rdf.model.Resource; +import org.apache.jena.sparql.graph.GraphFactory; +import org.apache.jena.sparql.util.graph.GraphUtils; + +/** + * An assembler that creates a dataset around a single graph. The dataset created is + * fixed; graphs can not be added or removed. The wrapped graph is the default graph of + * the dataset. + *

+ * General datasets and SPARQL Update can create graphs by inserting a quad. + * The dataset returned by this assembler does not support that. + * + * @see DatasetAssembler {@code DatasetAssembler}, for a general dataset. + * @see InMemDatasetAssembler {@code InMemDatasetAssembler}, for a fully transactional, in-memory dataset. + */ +public class DatasetGraphOneAssembler extends AssemblerBase { + public static Resource getType() { + return DatasetAssemblerVocab.tDatasetOne; + } + + @Override + public Object open(Assembler a, Resource root, Mode mode) { + Dataset ds = createDataset(a, root, mode); + return ds; + } + + public Dataset createDataset(Assembler a, Resource root, Mode mode) { + // Can use ja:graph or ja:defaultGraph but not both. + Resource dftGraphDesc1 = GraphUtils.getResourceValue(root, DatasetAssemblerVocab.pDefaultGraph); + Resource dftGraphDesc2 = GraphUtils.getResourceValue(root, DatasetAssemblerVocab.pGraph); + + if ( dftGraphDesc1 != null && dftGraphDesc2 != null ) + throw new AssemblerException(root, "Found both ja:graph and ja:defaultGraph"); + + Resource graphDesc = ( dftGraphDesc1 != null) ? dftGraphDesc1 : dftGraphDesc2 ; + Model model; + if ( graphDesc != null ) + model = a.openModel(graphDesc); + else + // Assembler description did not define one. + model = GraphFactory.makeDefaultModel(); + Dataset ds = DatasetFactory.wrap(model); + + List nodes = GraphUtils.multiValue(root, DatasetAssemblerVocab.pNamedGraph); + if ( ! nodes.isEmpty() ) { + String x = DatasetAssemblerVocab.tDatasetOne.getLocalName(); + throw new AssemblerException(root, "A "+x+" dataset can only hold a default graph, and no named graphs"); + } + AssemblerUtils.setContext(root, ds.getContext()); + return ds; + } +} diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/util/QueryExecUtils.java b/jena-arq/src/main/java/org/apache/jena/sparql/util/QueryExecUtils.java index 61f273084b1..e2cbcef88d1 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/util/QueryExecUtils.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/util/QueryExecUtils.java @@ -302,7 +302,7 @@ private static void doAskQuery(Prologue prologue, QueryExecution qe, ResultsForm * that one RDFNode */ public static RDFNode getExactlyOne(String qs, Model model) { - return getExactlyOne(qs, DatasetFactory.create(model)) ; + return getExactlyOne(qs, DatasetFactory.wrap(model)) ; } /** diff --git a/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java b/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java index 7335f759c8b..81e4ad7eed3 100644 --- a/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java +++ b/jena-arq/src/test/java/org/apache/jena/query/TestParameterizedSparqlString.java @@ -245,7 +245,7 @@ public void test_param_string_bnode_3() { bnode.addProperty(RDF.type, OWL.Thing); Assert.assertEquals(1, model.size()); - Dataset ds = DatasetFactory.create(model); + Dataset ds = DatasetFactory.wrap(model); // Use a parameterized query to check the data can be found ParameterizedSparqlString pq = new ParameterizedSparqlString(); diff --git a/jena-arq/src/test/java/org/apache/jena/riot/lang/ParserTestBaseLib.java b/jena-arq/src/test/java/org/apache/jena/riot/lang/ParserTestBaseLib.java index fbe32ed9888..00e851ebcae 100644 --- a/jena-arq/src/test/java/org/apache/jena/riot/lang/ParserTestBaseLib.java +++ b/jena-arq/src/test/java/org/apache/jena/riot/lang/ParserTestBaseLib.java @@ -43,7 +43,7 @@ static Graph parseGraph(Lang lang, String ...strings) { static DatasetGraph parseDataset(Lang lang, String ...strings) { DatasetGraph dsg = DatasetGraphFactory.create() ; StreamRDF dest = StreamRDFLib.dataset(dsg) ; - parse(lang, dest, strings) ; + parse(lang, dest, strings); return dsg ; } diff --git a/jena-arq/src/test/java/org/apache/jena/riot/writer/TestJsonLDWriter.java b/jena-arq/src/test/java/org/apache/jena/riot/writer/TestJsonLDWriter.java index cc8aff915cb..77a41fe66f6 100644 --- a/jena-arq/src/test/java/org/apache/jena/riot/writer/TestJsonLDWriter.java +++ b/jena-arq/src/test/java/org/apache/jena/riot/writer/TestJsonLDWriter.java @@ -507,7 +507,7 @@ private Model simpleModel(String ns) { m.setNsPrefix("", ns); - DatasetGraph g = DatasetFactory.create(m).asDatasetGraph(); + DatasetGraph g = DatasetFactory.wrap(m).asDatasetGraph(); PrefixMap pm = RiotLib.prefixMap(g); String base = null; Context jenaContext = null; @@ -551,7 +551,7 @@ private Model simpleModel(String ns) { private String toString(Model m, RDFFormat f, Context jenaContext) { try(ByteArrayOutputStream out = new ByteArrayOutputStream()) { WriterDatasetRIOT w = RDFDataMgr.createDatasetWriter(f) ; - DatasetGraph g = DatasetFactory.create(m).asDatasetGraph(); + DatasetGraph g = DatasetFactory.wrap(m).asDatasetGraph(); PrefixMap pm = RiotLib.prefixMap(g); String base = null; w.write(out, g, pm, base, jenaContext) ; diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/api/TestQueryExecutionTimeout1.java b/jena-arq/src/test/java/org/apache/jena/sparql/api/TestQueryExecutionTimeout1.java index c6fa162977f..c23f08e55b5 100644 --- a/jena-arq/src/test/java/org/apache/jena/sparql/api/TestQueryExecutionTimeout1.java +++ b/jena-arq/src/test/java/org/apache/jena/sparql/api/TestQueryExecutionTimeout1.java @@ -38,7 +38,7 @@ public class TestQueryExecutionTimeout1 extends BaseTest { static Graph g = SSE.parseGraph("(graph (

) (

) (

))") ; - static DatasetGraph dsg = DatasetGraphFactory.createOneGraph(g) ; + static DatasetGraph dsg = DatasetGraphFactory.wrap(g) ; static Dataset ds = DatasetFactory.wrap(dsg) ; private static final String ns = "http://example/ns#" ; diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/api/TestQueryExecutionTimeout2.java b/jena-arq/src/test/java/org/apache/jena/sparql/api/TestQueryExecutionTimeout2.java index 49d562db171..71c0e6143d4 100644 --- a/jena-arq/src/test/java/org/apache/jena/sparql/api/TestQueryExecutionTimeout2.java +++ b/jena-arq/src/test/java/org/apache/jena/sparql/api/TestQueryExecutionTimeout2.java @@ -51,7 +51,7 @@ public class TestQueryExecutionTimeout2 " (

11)" + " (

12)" + ")") ; - static DatasetGraph dsg = DatasetGraphFactory.createOneGraph(g) ; + static DatasetGraph dsg = DatasetGraphFactory.wrap(g) ; static Dataset ds = DatasetFactory.wrap(dsg) ; private static void noException(ResultSet rs) diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/transaction/TestTransactionSupport.java b/jena-arq/src/test/java/org/apache/jena/sparql/transaction/TestTransactionSupport.java index 46c955a5d64..3b8d8801619 100644 --- a/jena-arq/src/test/java/org/apache/jena/sparql/transaction/TestTransactionSupport.java +++ b/jena-arq/src/test/java/org/apache/jena/sparql/transaction/TestTransactionSupport.java @@ -48,9 +48,9 @@ public static Iterable data() { x.add(new Object[] {"create", (Creator)()->DatasetGraphFactory.create(), true, false}) ; - x.add(new Object[] {"createOneGraph" , + x.add(new Object[] {"wrap" , (Creator)()-> - DatasetGraphFactory.createOneGraph(GraphFactory.createDefaultGraph()), + DatasetGraphFactory.wrap(GraphFactory.createDefaultGraph()), true, true}) ; x.add(new Object[] {"createZeroGraph" , (Creator)()->new DatasetGraphZero(), diff --git a/jena-db/jena-dboe-trans-data/src/main/java/org/apache/jena/dboe/trans/bplustree/BPTreeNode.java b/jena-db/jena-dboe-trans-data/src/main/java/org/apache/jena/dboe/trans/bplustree/BPTreeNode.java index e3f05331847..f808cb9b141 100644 --- a/jena-db/jena-dboe-trans-data/src/main/java/org/apache/jena/dboe/trans/bplustree/BPTreeNode.java +++ b/jena-db/jena-dboe-trans-data/src/main/java/org/apache/jena/dboe/trans/bplustree/BPTreeNode.java @@ -138,24 +138,6 @@ private static BPTreeNode create(BPlusTree bpTree, int parent, boolean isLeaf) { // Other set by BPTreeNodeMgr.formatBPTreeNode } - // ---- [[TXN]] ** work for transactions. - void checkTxn() {} - void checkWriteTxn() {} - -// static BPTreeNode xensureModifiableRoot(BPTreeNode root, Object state) { -// BPTreeNode root2 = promote1(root, root, NO_ID)(null, root) ; -// if ( root != root2 && root.getId() != root2.getId() ) { -// System.err.println("Cloned root") ; -// if ( state == null ) -// System.err.println("... no state") ; -// // [[TXN]] ** Root clone -// // get state and update root. -// } -// return root2 ; -// } - - // ---- - @Override public void reset(Block block) { this.block = block ; diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/DatasetGraphTDB.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/DatasetGraphTDB.java index 42690b2089f..2148930911c 100644 --- a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/DatasetGraphTDB.java +++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/DatasetGraphTDB.java @@ -39,6 +39,7 @@ import org.apache.jena.graph.Triple ; import org.apache.jena.query.ReadWrite ; import org.apache.jena.query.TxnType; +import org.apache.jena.sparql.JenaTransactionException; import org.apache.jena.sparql.core.* ; import org.apache.jena.sparql.engine.optimizer.reorder.ReorderTransformation ; import org.apache.jena.tdb2.TDBException; @@ -392,14 +393,28 @@ public void deleteAny(Node g, Node s, Node p, Node o) { public Location getLocation() { return storage.location ; } + /** + * Cause an exception to be thrown if sync is called. + * For TDB2, which is transactional only, so sync isn't a useful operation. + * It is implemented for completness and no more. + */ + public static boolean exceptionOnSync = true ; + @Override public void sync() { + if ( exceptionOnSync ) + throw new JenaTransactionException("sync called"); checkNotClosed(); + syncStorage(); + } + + // Sync for internal puposes. + public void syncStorage() { storage.tripleTable.sync(); storage.quadTable.sync(); storage.prefixes.sync(); } - + @Override public void setDefaultGraph(Graph g) { throw new UnsupportedOperationException("Can't set default graph on a TDB-backed dataset") ; diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/GraphViewSwitchable.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/GraphViewSwitchable.java index cd5f561d1d6..760d9e47e5e 100644 --- a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/GraphViewSwitchable.java +++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/GraphViewSwitchable.java @@ -21,11 +21,13 @@ import java.util.Map ; import org.apache.jena.graph.Node ; +import org.apache.jena.graph.TransactionHandler; import org.apache.jena.shared.PrefixMapping ; import org.apache.jena.shared.impl.PrefixMappingImpl ; import org.apache.jena.sparql.core.DatasetPrefixStorage; import org.apache.jena.sparql.core.GraphView; import org.apache.jena.sparql.core.Quad; +import org.apache.jena.sparql.core.TransactionHandlerView; import org.apache.jena.sparql.expr.nodevalue.NodeFunctions; /** @@ -45,9 +47,13 @@ public static GraphViewSwitchable createUnionGraph(DatasetGraphSwitchable dsg) private final DatasetGraphSwitchable dsgx; protected DatasetGraphSwitchable getx() { return dsgx; } + private TransactionHandler transactionHandler = null; + protected GraphViewSwitchable(DatasetGraphSwitchable dsg, Node gn) { super(dsg, gn) ; this.dsgx = dsg; + // Goes to the switchable DatasetGraph + this.transactionHandler = new TransactionHandlerView(dsgx); } @Override @@ -63,6 +69,11 @@ protected PrefixMapping createPrefixMapping() { } return prefixMapping(gn); } + + @Override + public TransactionHandler getTransactionHandler() { + return transactionHandler; + } /** Return the {@code DatasetGraphSwitchable} we are viewing. */ @Override diff --git a/jena-integration-tests/src/test/java/org/apache/jena/test/txn/TS_TranactionIntegration.java b/jena-integration-tests/src/test/java/org/apache/jena/test/txn/TS_TranactionIntegration.java new file mode 100644 index 00000000000..e390ad2cd1f --- /dev/null +++ b/jena-integration-tests/src/test/java/org/apache/jena/test/txn/TS_TranactionIntegration.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.test.txn; + +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +@RunWith(Suite.class) +@Suite.SuiteClasses( { + TestDataset2Graph.class +}) + +public class TS_TranactionIntegration { + +} diff --git a/jena-integration-tests/src/test/java/org/apache/jena/test/txn/TestDataset2Graph.java b/jena-integration-tests/src/test/java/org/apache/jena/test/txn/TestDataset2Graph.java new file mode 100644 index 00000000000..38e1e5522de --- /dev/null +++ b/jena-integration-tests/src/test/java/org/apache/jena/test/txn/TestDataset2Graph.java @@ -0,0 +1,157 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.test.txn; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.Arrays; +import java.util.Collection; + +import org.apache.jena.atlas.iterator.Iter; +import org.apache.jena.atlas.lib.Creator; +import org.apache.jena.graph.Triple; +import org.apache.jena.query.*; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.ModelFactory; +import org.apache.jena.rdfconnection.RDFConnection; +import org.apache.jena.rdfconnection.RDFConnectionFactory; +import org.apache.jena.reasoner.rulesys.RDFSRuleReasonerFactory; +import org.apache.jena.sparql.core.Quad; +import org.apache.jena.sparql.core.TxnDataset2Graph; +import org.apache.jena.sparql.sse.SSE; +import org.apache.jena.system.Txn; +import org.apache.jena.tdb.TDBFactory; +import org.apache.jena.tdb2.TDB2Factory; +import org.apache.jena.tdb2.store.DatasetGraphTDB; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +// Tests - programmatic construct, assembler construct. Good and bad. +// TestDataset2Graph - TS_Transactions. +// DatasetFactory.wrap +// DatasetOne, DatasetGraphOne. <-- Flag needed. +// DatasetImpl + +/** Additional testing for "Dataset over Graph" transaction mapping */ + +@RunWith(Parameterized.class) +public class TestDataset2Graph { + private static boolean txn_dsg_graph; + private static boolean tdb2_no_sync; + // TXN_DSG_GRAPH must be true. + @SuppressWarnings("deprecation") + @BeforeClass public static void beforeClass() { + txn_dsg_graph = TxnDataset2Graph.TXN_DSG_GRAPH; + TxnDataset2Graph.TXN_DSG_GRAPH = true; + // Make sure sync isn't called. + tdb2_no_sync = DatasetGraphTDB.exceptionOnSync; + DatasetGraphTDB.exceptionOnSync = true; + } + + @SuppressWarnings("deprecation") + @AfterClass public static void afterClass() { + TxnDataset2Graph.TXN_DSG_GRAPH = txn_dsg_graph; + DatasetGraphTDB.exceptionOnSync = tdb2_no_sync; + } + + @Parameters(name = "{index}: {0}") + public static Collection data() { + Creator datasetPlainMaker = ()-> DatasetFactory.createTxnMem() ; + Creator datasetTxnMemMaker = ()-> DatasetFactory.createTxnMem() ; + Creator datasetTDB1 = ()-> TDBFactory.createDataset(); + Creator datasetTDB2 = ()-> TDB2Factory.createDataset(); + return Arrays.asList(new Object[][] { + { "Plain", datasetPlainMaker }, + { "TIM", datasetTxnMemMaker }, + { "TDB1", datasetTDB1 }, + { "TDB2", datasetTDB2 } + }); + } + + private final Creator creator; + + public TestDataset2Graph(String name, Creator creator) { + this.creator = creator; + } + + @Test public void dsgGraph_model() { + testInfModel(creator.create()); + } + + @Test public void dsgGraphTxnTDB_dataset_wrap() { + testOverDS(creator.create(), true); + } + + @Test public void dsgGraphTxnTDB_dataset_create() { + testOverDS(creator.create(), false); + } + private static void testInfModel(Dataset ds0) { + Txn.executeWrite(ds0, ()->{}); + Model baseModel = ds0.getDefaultModel(); + Model model = ModelFactory.createInfModel(RDFSRuleReasonerFactory.theInstance().create(null), baseModel); + if ( model.getGraph().getTransactionHandler().transactionsSupported() ) { + // InfModels do not support transactions per se - they particpate if includes in a suitabel dataset. + model.begin(); + long x = Iter.count(model.listStatements()); + model.commit(); + assertTrue(x > 10); + } + } + + private static void testOverDS(Dataset ds0, boolean wrap) { + // Force to transactions / verify the DSG is transactional. + Txn.executeWrite(ds0, ()->{}); + Model baseModel = ds0.getDefaultModel(); + Model model = ModelFactory.createInfModel(RDFSRuleReasonerFactory.theInstance().create(null), baseModel); + Dataset ds1 = wrap ? DatasetFactory.wrap(model) : DatasetFactory.create(model); + + try ( RDFConnection conn = RDFConnectionFactory.connect(ds1) ) { + + conn.querySelect("SELECT (count(*) AS ?C) { ?s ?p ?o } HAVING (?C = 0)", (qs)-> fail("Didn't expect any query solutions")); + + // Necessary + Txn.exec(conn, TxnType.READ, ()->{ + try ( QueryExecution qExec = conn.query("SELECT * { ?s ?p ?o }") ) { + long x = ResultSetFormatter.consume(qExec.execSelect()); + assertTrue(x > 10); // About106 + } + }); + } + + Triple t = SSE.parseTriple("(:s :p :o)"); + Quad q = Quad.create(Quad.defaultGraphIRI, t); + + // Now write via top. + Txn.executeWrite(ds1, ()->{ + ds1.asDatasetGraph().add(q); + }); + + // And get it back again from storage. + Txn.exec(ds0, TxnType.READ, ()->{ + assertEquals(1, ds0.asDatasetGraph().getDefaultGraph().size()); + assertTrue(ds0.getDefaultModel().getGraph().contains(t)); + }); + } +} diff --git a/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnection.java b/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnection.java index 3c5e3740a19..2cd7e08acb6 100644 --- a/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnection.java +++ b/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnection.java @@ -110,8 +110,6 @@ public default void queryResultSet(Query query, Consumer resultSetAct */ @Override public default void querySelect(String query, Consumer rowAction) { - //Parse local: querySelect(QueryFactory.create(query), rowAction); - // XXX Parse point. Txn.executeRead(this, ()->{ try ( QueryExecution qExec = query(query) ) { qExec.execSelect().forEachRemaining(rowAction); diff --git a/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnectionLocal.java b/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnectionLocal.java index 2db693e9fea..767ed3f39cb 100644 --- a/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnectionLocal.java +++ b/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnectionLocal.java @@ -70,7 +70,8 @@ public RDFConnectionLocal(Dataset dataset, Isolation isolation) { @Override public QueryExecution query(Query query) { checkOpen(); - return Txn.calculateRead(dataset, ()->QueryExecutionFactory.create(query, dataset)); + // There is no point doing this in a transaction because the QueryExecution is passed out. + return QueryExecutionFactory.create(query, dataset); } @Override From 41e2eb1b067f0f8217e665590071e8d18c085685 Mon Sep 17 00:00:00 2001 From: Andy Seaborne Date: Sun, 4 Mar 2018 14:26:13 +0000 Subject: [PATCH 5/8] Trigger promotion when prefix changes are made. --- .../jena/tdb2/setup/AbstractTDBBuilder.java | 2 +- .../apache/jena/tdb2/store/DatasetGraphTDB.java | 15 +++++---------- .../jena/tdb2/store/DatasetPrefixesTDB.java | 8 ++++++++ .../org/apache/jena/tdb2/store/StorageTDB.java | 5 ++--- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/setup/AbstractTDBBuilder.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/setup/AbstractTDBBuilder.java index 6bcb2f5cd2a..82f56c9277b 100644 --- a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/setup/AbstractTDBBuilder.java +++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/setup/AbstractTDBBuilder.java @@ -189,7 +189,7 @@ public DatasetPrefixesTDB buildPrefixTable(NodeTable prefixNodes) { NodeTupleTable prefixTable = new NodeTupleTableConcrete(primary.length(), prefixIndexes, prefixNodes); - DatasetPrefixesTDB prefixes = new DatasetPrefixesTDB(prefixTable); + DatasetPrefixesTDB prefixes = new DatasetPrefixesTDB(prefixTable); log().debug("Prefixes: "+primary+" :: "+String.join(",", indexes)); return prefixes; } diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/DatasetGraphTDB.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/DatasetGraphTDB.java index 2148930911c..904bc50cb48 100644 --- a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/DatasetGraphTDB.java +++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/DatasetGraphTDB.java @@ -73,7 +73,7 @@ public class DatasetGraphTDB extends DatasetGraphTriplesQuads /** Application should not create a {@code DatasetGraphTDB} directly */ public DatasetGraphTDB(TransactionalSystem txnSystem, - TripleTable tripleTable, QuadTable quadTable, DatasetPrefixStorage prefixes, + TripleTable tripleTable, QuadTable quadTable, DatasetPrefixesTDB prefixes, ReorderTransformation transform, Location location, StoreParams params) { reset(txnSystem, tripleTable, quadTable, prefixes, location, params) ; this.transform = transform ; @@ -81,16 +81,9 @@ public DatasetGraphTDB(TransactionalSystem txnSystem, } public void reset(TransactionalSystem txnSystem, - TripleTable tripleTable, QuadTable quadTable, DatasetPrefixStorage prefixes, + TripleTable tripleTable, QuadTable quadTable, DatasetPrefixesTDB prefixes, Location location, StoreParams params) { -// this.tripleTable = tripleTable ; -// this.quadTable = quadTable ; -// this.location = location ; -// this.prefixes = prefixes ; -// this.storeParams = params ; this.txnSystem = txnSystem ; - // XXX Threading? - // XXX (re)set transaction components in TransactionCoordinator?? this.storage = new StorageTDB(tripleTable, quadTable, prefixes, location, params); this.defaultGraphTDB = getDefaultGraphTDB(); } @@ -171,7 +164,7 @@ protected void deleteFromNamedGraph(Node g, Node s, Node p, Node o) { } // Promotion - private void requireWriteTxn() { + /*package*/ void requireWriteTxn() { Transaction txn = txnSystem.getThreadTransaction() ; if ( txn == null ) throw new TransactionException("Not in a transaction") ; @@ -300,6 +293,8 @@ public ReorderTransformation getReorderTransform() { public DatasetPrefixStorage getPrefixes() { checkNotClosed(); + // Need for requireWriteTxn + storage.prefixes.setDatasetGraphTDB(this); return storage.prefixes; } diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/DatasetPrefixesTDB.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/DatasetPrefixesTDB.java index e1b7e7b7922..982e8daa31f 100644 --- a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/DatasetPrefixesTDB.java +++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/DatasetPrefixesTDB.java @@ -44,11 +44,17 @@ public class DatasetPrefixesTDB implements DatasetPrefixStorage static final String unamedGraphURI = "" ; private final NodeTupleTable nodeTupleTable ; + private DatasetGraphTDB dataset = null; public DatasetPrefixesTDB(NodeTupleTable nodeTupleTable) { this.nodeTupleTable = nodeTupleTable ; } + // Needed because DatasetPrefixesTDB is created before DatasetGraphTDB + /*package*/ void setDatasetGraphTDB(DatasetGraphTDB dsg) { + this.dataset = dsg; + } + @Override public void loadPrefixMapping(String graphName, PrefixMapping pmap) { Node g = NodeFactory.createURI(graphName) ; @@ -62,6 +68,7 @@ public void loadPrefixMapping(String graphName, PrefixMapping pmap) { @Override public synchronized void insertPrefix(String graphName, String prefix, String uri) { + dataset.requireWriteTxn(); Node g = NodeFactory.createURI(graphName) ; Node p = NodeFactory.createLiteral(prefix) ; Node u = NodeFactory.createURI(uri) ; @@ -141,6 +148,7 @@ public void removeAllFromPrefixMap(String graphName) { /** Remove by pattern */ private synchronized void removeAll(Node g, Node p, Node uri) { + dataset.requireWriteTxn(); Iterator> iter = nodeTupleTable.find(g, p, uri) ; List> list = Iter.toList(iter) ; // Materialize. Iter.close(iter) ; diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/StorageTDB.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/StorageTDB.java index f4d7ac6cbdf..4998838aafa 100644 --- a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/StorageTDB.java +++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/StorageTDB.java @@ -19,18 +19,17 @@ package org.apache.jena.tdb2.store; import org.apache.jena.dboe.base.file.Location; -import org.apache.jena.sparql.core.DatasetPrefixStorage; import org.apache.jena.tdb2.setup.StoreParams; public class StorageTDB { /*package*/ final TripleTable tripleTable; /*package*/ final QuadTable quadTable; - /*package*/ final DatasetPrefixStorage prefixes; + /*package*/ final DatasetPrefixesTDB prefixes; /*package*/ final Location location; /*package*/ final StoreParams storeParams; - public StorageTDB(TripleTable tripleTable, QuadTable quadTable, DatasetPrefixStorage prefixes, Location location, StoreParams params) { + public StorageTDB(TripleTable tripleTable, QuadTable quadTable, DatasetPrefixesTDB prefixes, Location location, StoreParams params) { super(); this.tripleTable = tripleTable; this.quadTable = quadTable; From 4744cfc82cc355b67152d73192b53ebb12942205 Mon Sep 17 00:00:00 2001 From: Andy Seaborne Date: Sun, 4 Mar 2018 15:40:11 +0000 Subject: [PATCH 6/8] Add assemblers for the lesser cases of Zero and Sink datasets. --- .../jena/sparql/core/TxnDataset2Graph.java | 17 +++--- .../sparql/core/assembler/AssemblerUtils.java | 12 ++-- .../core/assembler/DatasetAssemblerVocab.java | 5 +- .../core/assembler/DatasetNullAssembler.java | 59 +++++++++++++++++++ ...ssembler.java => DatasetOneAssembler.java} | 2 +- .../jena/sparql/resultset/TestResultSet.java | 9 +-- 6 files changed, 87 insertions(+), 17 deletions(-) create mode 100644 jena-arq/src/main/java/org/apache/jena/sparql/core/assembler/DatasetNullAssembler.java rename jena-arq/src/main/java/org/apache/jena/sparql/core/assembler/{DatasetGraphOneAssembler.java => DatasetOneAssembler.java} (98%) diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/TxnDataset2Graph.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/TxnDataset2Graph.java index d03cc6eb7c7..027d1ed1ab0 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/core/TxnDataset2Graph.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/TxnDataset2Graph.java @@ -55,13 +55,16 @@ * @See {@link DatasetGraphOne} */ public class TxnDataset2Graph extends TransactionalLock { - /** Control whether to pass down transactions from the dataset to the graph in the dataset. - * This should be set to "true". - * This is temporary flag during the transition because the change at Jena 3.7.0 - * needs to be proven in real deployments as well as testing. - * "false" restores the Jena 3.6.0 and before behaviour (transactions not passed down). - * See JENA-1492. - * @deprecated This flag will be removed. + /** + * Control whether to pass down transactions from the dataset to the graph in the + * dataset. This should be set to "true"; setting it "false" causes the onld, + * no-transaction passing behaviour. + *

+ * This is temporary flag during the transition because the change at Jena 3.7.0 needs + * to be proven in real deployments as well as testing. "false" restores the Jena + * 3.6.0 and before behaviour (transactions not passed down). See JENA-1492. + * + * @deprecated This flag will be removed. */ @Deprecated public static boolean TXN_DSG_GRAPH = true; diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/assembler/AssemblerUtils.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/assembler/AssemblerUtils.java index d80d02c879c..a9aa7bb0536 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/core/assembler/AssemblerUtils.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/assembler/AssemblerUtils.java @@ -37,6 +37,7 @@ import org.apache.jena.sparql.util.graph.GraphUtils ; import org.apache.jena.system.JenaSystem ; import org.apache.jena.vocabulary.RDFS ; +import static org.apache.jena.sparql.core.assembler.DatasetAssemblerVocab.*; public class AssemblerUtils { @@ -56,9 +57,12 @@ static public void init() if ( initialized ) return ; initialized = true ; - registerDataset(DatasetAssemblerVocab.tDataset, new DatasetAssembler()) ; - registerDataset(DatasetAssemblerVocab.tMemoryDataset, new InMemDatasetAssembler()) ; - registerDataset(DatasetAssemblerVocab.tDatasetTxnMem, new InMemDatasetAssembler()) ; + registerDataset(tDataset, new DatasetAssembler()) ; + registerDataset(tDatasetOne, new DatasetOneAssembler()) ; + registerDataset(tDatasetZero, new DatasetNullAssembler(tDatasetZero)) ; + registerDataset(tDatasetSink, new DatasetNullAssembler(tDatasetSink)) ; + registerDataset(tMemoryDataset, new InMemDatasetAssembler()) ; + registerDataset(tDatasetTxnMem, new InMemDatasetAssembler()) ; } private static Model modelExtras = ModelFactory.createDefaultModel() ; @@ -118,7 +122,7 @@ public static Object build(String assemblerFile, Resource type) { return null ; } catch (TypeNotUniqueException ex) - { throw new ARQException("Multiple types for: "+DatasetAssemblerVocab.tDataset) ; } + { throw new ARQException("Multiple types for: "+tDataset) ; } return Assembler.general.open(root) ; } diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/assembler/DatasetAssemblerVocab.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/assembler/DatasetAssemblerVocab.java index 5d26abe9dff..d55d7d15a15 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/core/assembler/DatasetAssemblerVocab.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/assembler/DatasetAssemblerVocab.java @@ -35,8 +35,11 @@ public class DatasetAssemblerVocab // In-memory dataset public static final Resource tDatasetTxnMem = ResourceFactory.createResource(NS+"DatasetTxnMem") ; + + // Specialised datasets public static final Resource tMemoryDataset = ResourceFactory.createResource(NS+"MemoryDataset") ; - public static final Resource tDatasetNull = ResourceFactory.createResource(NS+"DatasetNull") ; + public static final Resource tDatasetZero = ResourceFactory.createResource(NS+"RDFDatasetZero") ; + public static final Resource tDatasetSink = ResourceFactory.createResource(NS+"RDFDatasetSink") ; public static final Property pDefaultGraph = ResourceFactory.createProperty(NS, "defaultGraph") ; public static final Property pNamedGraph = ResourceFactory.createProperty(NS, "namedGraph") ; diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/assembler/DatasetNullAssembler.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/assembler/DatasetNullAssembler.java new file mode 100644 index 00000000000..f82d8699c15 --- /dev/null +++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/assembler/DatasetNullAssembler.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.sparql.core.assembler; + +import org.apache.jena.assembler.Assembler; +import org.apache.jena.assembler.Mode; +import org.apache.jena.assembler.assemblers.AssemblerBase; +import org.apache.jena.atlas.lib.InternalErrorException; +import org.apache.jena.query.Dataset; +import org.apache.jena.query.DatasetFactory; +import org.apache.jena.rdf.model.Resource; +import org.apache.jena.sparql.core.DatasetGraph; +import org.apache.jena.sparql.core.DatasetGraphSink; +import org.apache.jena.sparql.core.DatasetGraphZero; + +/** + * An assembler that creates datasets that do nothing, either a sink or a always empty one. + + * @see DatasetGraphSink + * @see DatasetGraphZero + */ + +public class DatasetNullAssembler extends AssemblerBase { + private final Resource tDataset; + + public DatasetNullAssembler(Resource tDataset) { + this.tDataset = tDataset; + } + + @Override + public Object open(Assembler a, Resource root, Mode mode) { + DatasetGraph dsg; + if ( DatasetAssemblerVocab.tDatasetSink.equals(tDataset) ) + dsg = new DatasetGraphSink(); + else if ( DatasetAssemblerVocab.tDatasetZero.equals(tDataset) ) + dsg = new DatasetGraphZero(); + else + throw new InternalErrorException(); + Dataset ds = DatasetFactory.wrap(dsg); + AssemblerUtils.setContext(root, ds.getContext()); + return ds; + } +} diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/assembler/DatasetGraphOneAssembler.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/assembler/DatasetOneAssembler.java similarity index 98% rename from jena-arq/src/main/java/org/apache/jena/sparql/core/assembler/DatasetGraphOneAssembler.java rename to jena-arq/src/main/java/org/apache/jena/sparql/core/assembler/DatasetOneAssembler.java index ed33ebf867c..70002d5c496 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/core/assembler/DatasetGraphOneAssembler.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/assembler/DatasetOneAssembler.java @@ -43,7 +43,7 @@ * @see DatasetAssembler {@code DatasetAssembler}, for a general dataset. * @see InMemDatasetAssembler {@code InMemDatasetAssembler}, for a fully transactional, in-memory dataset. */ -public class DatasetGraphOneAssembler extends AssemblerBase { +public class DatasetOneAssembler extends AssemblerBase { public static Resource getType() { return DatasetAssemblerVocab.tDatasetOne; } diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/resultset/TestResultSet.java b/jena-arq/src/test/java/org/apache/jena/sparql/resultset/TestResultSet.java index cca22356912..11c15c3da9d 100644 --- a/jena-arq/src/test/java/org/apache/jena/sparql/resultset/TestResultSet.java +++ b/jena-arq/src/test/java/org/apache/jena/sparql/resultset/TestResultSet.java @@ -166,28 +166,29 @@ public static void teardown() { } // Into some format. + private static String DIR = "testing/ResultSet/"; @Test public void test_RS_7() { - ResultSet rs = ResultSetFactory.load("testing/ResultSet/output.srx") ; + ResultSet rs = ResultSetFactory.load(DIR+"output.srx") ; test_RS_fmt(rs, ResultsFormat.FMT_RS_XML, true) ; } @Test public void test_RS_8() { - ResultSet rs = ResultSetFactory.load("testing/ResultSet/output.srx") ; + ResultSet rs = ResultSetFactory.load(DIR+"output.srx") ; test_RS_fmt(rs, ResultsFormat.FMT_RS_JSON, true) ; } @Test public void test_RS_9() { - ResultSet rs = ResultSetFactory.load("testing/ResultSet/output.srx") ; + ResultSet rs = ResultSetFactory.load(DIR+"output.srx") ; test_RS_fmt(rs, ResultsFormat.FMT_RDF_XML, false) ; } @Test public void test_RS_10() { - ResultSet rs = ResultSetFactory.load("testing/ResultSet/output.srx") ; + ResultSet rs = ResultSetFactory.load(DIR+"output.srx") ; for ( ; rs.hasNext(); rs.next()) { } // We should be able to call hasNext() as many times as we want! assertFalse(rs.hasNext()); From 41eefd40e298b00fa35ff5b4b4cf3ec0aebccdf0 Mon Sep 17 00:00:00 2001 From: Andy Seaborne Date: Sun, 4 Mar 2018 15:41:04 +0000 Subject: [PATCH 7/8] Integration tests for dataset/graphs and some assemblers. --- .../jena/sparql/core/DatasetGraphOne.java | 41 ++--- .../jena/sparql/core/TransactionalLock.java | 4 +- .../jena/sparql/core/TxnDataset2Graph.java | 15 +- .../transaction/TestTransactionSupport.java | 16 +- .../jena/test/assembler/TS_Assembler.java | 31 ++++ .../test/assembler/TestDatasetAssembler.java | 145 ++++++++++++++++++ .../TestRDFConnectionRemote.java | 3 +- .../jena/test/txn/TestDataset2Graph.java | 19 ++- .../testing/Assembler/assem_dsg1_1.ttl | 11 ++ .../testing/Assembler/assem_dsg1_2.ttl | 15 ++ .../testing/Assembler/assem_dsg1_3.ttl | 15 ++ .../testing/Assembler/assem_dsg1_bad_1.ttl | 15 ++ .../testing/Assembler/assem_dsg1_inf_tdb1.ttl | 31 ++++ .../testing/Assembler/assem_dsg1_inf_tdb2.ttl | 30 ++++ .../testing/Assembler/assem_dsg_sink.ttl | 11 ++ .../testing/Assembler/assem_dsg_zero.ttl | 9 ++ .../testing/Assembler/data.ttl | 5 + 17 files changed, 373 insertions(+), 43 deletions(-) create mode 100644 jena-integration-tests/src/test/java/org/apache/jena/test/assembler/TS_Assembler.java create mode 100644 jena-integration-tests/src/test/java/org/apache/jena/test/assembler/TestDatasetAssembler.java create mode 100644 jena-integration-tests/testing/Assembler/assem_dsg1_1.ttl create mode 100644 jena-integration-tests/testing/Assembler/assem_dsg1_2.ttl create mode 100644 jena-integration-tests/testing/Assembler/assem_dsg1_3.ttl create mode 100644 jena-integration-tests/testing/Assembler/assem_dsg1_bad_1.ttl create mode 100644 jena-integration-tests/testing/Assembler/assem_dsg1_inf_tdb1.ttl create mode 100644 jena-integration-tests/testing/Assembler/assem_dsg1_inf_tdb2.ttl create mode 100644 jena-integration-tests/testing/Assembler/assem_dsg_sink.ttl create mode 100644 jena-integration-tests/testing/Assembler/assem_dsg_zero.ttl create mode 100644 jena-integration-tests/testing/Assembler/data.ttl diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphOne.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphOne.java index faf37c7b6f5..29a486b9e89 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphOne.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphOne.java @@ -45,6 +45,12 @@ public class DatasetGraphOne extends DatasetGraphBaseFind { private final boolean supportsAbort; public static DatasetGraph create(Graph graph) { + if ( graph instanceof GraphView ) { + // This becomes a simple class that passes all transaction operations the + // underlying dataset and masks the fact here are other graphs in the storage. + return new DatasetGraphOne(graph, ((GraphView)graph).getDataset()); + } + return new DatasetGraphOne(graph); } @@ -57,30 +63,18 @@ private DatasetGraphOne(Graph graph, DatasetGraph backing) { @SuppressWarnings("deprecation") private DatasetGraphOne(Graph graph) { + // Not GraphView which was hanled in create(Graph). this.graph = graph; - if ( graph instanceof GraphView ) { - backingDGS = ((GraphView)graph).getDataset(); - txn = backingDGS; - supportsAbort = backingDGS.supportsTransactionAbort(); - } else { - // JENA-1492 - pass down transactions. - if ( TxnDataset2Graph.TXN_DSG_GRAPH ) - txn = new TxnDataset2Graph(graph); - else - txn = TransactionalLock.createMRSW(); - backingDGS = null; - supportsAbort = false; - } - } - - public DatasetGraphOne(Graph graph, Transactional transactional) { - this.graph = graph; - backingDGS = null; - if ( transactional == null ) - txn = TransactionalLock.createMRSW(); + // JENA-1492 - pass down transactions. + if ( TxnDataset2Graph.TXN_DSG_GRAPH ) + txn = new TxnDataset2Graph(graph); else - txn = transactional; - supportsAbort = false; + txn = TransactionalLock.createMRSW(); + backingDGS = null; + // Don't advertise the fact but TxnDataset2Graph tries to provide abort. + // We can not guarantee it though because a plain, non-TIM, + // memory graph does not support abort. + supportsAbort = false; } @Override public void begin(TxnType txnType) { txn.begin(txnType); } @@ -93,8 +87,7 @@ public DatasetGraphOne(Graph graph, Transactional transactional) { @Override public ReadWrite transactionMode() { return txn.transactionMode(); } @Override public TxnType transactionType() { return txn.transactionType(); } @Override public boolean supportsTransactions() { return true; } - // Because there are never any changes, abort() means "finish". - @Override public boolean supportsTransactionAbort() { return true; } + @Override public boolean supportsTransactionAbort() { return supportsAbort; } @Override public boolean containsGraph(Node graphNode) { diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/TransactionalLock.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/TransactionalLock.java index 930f9e202a5..1c0ce6f5446 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/core/TransactionalLock.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/TransactionalLock.java @@ -161,7 +161,7 @@ public void end() { endOnce() ; } - private void endOnce() { + protected void endOnce() { if ( isInTransaction() ) { lock.leaveCriticalSection() ; txnMode.set(null); @@ -173,7 +173,7 @@ private void endOnce() { } } - private void error(String msg) { + protected void error(String msg) { throw new JenaTransactionException(msg) ; } } diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/TxnDataset2Graph.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/TxnDataset2Graph.java index 027d1ed1ab0..564c1dbcf58 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/core/TxnDataset2Graph.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/TxnDataset2Graph.java @@ -220,8 +220,21 @@ public void commit() { @Override public void abort() { - handlers(h -> h.abort()); + handlers(h->h.abort()); finish(); super.abort(); } + + @Override + public void end() { + if ( isTransactionMode(ReadWrite.WRITE) ) + error("Write transaction - no commit or abort before end()") ; + // Need to put this in between the two parts of end(). + if ( super.isInTransaction() ) { + // Must be READ at this point. + handlers(h->h.commit()); + finish(); + } + super.endOnce(); + } } diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/transaction/TestTransactionSupport.java b/jena-arq/src/test/java/org/apache/jena/sparql/transaction/TestTransactionSupport.java index 3b8d8801619..2208c6ddb61 100644 --- a/jena-arq/src/test/java/org/apache/jena/sparql/transaction/TestTransactionSupport.java +++ b/jena-arq/src/test/java/org/apache/jena/sparql/transaction/TestTransactionSupport.java @@ -24,6 +24,7 @@ import org.apache.jena.atlas.lib.Creator ; import org.apache.jena.sparql.core.DatasetGraph ; import org.apache.jena.sparql.core.DatasetGraphFactory ; +import org.apache.jena.sparql.core.DatasetGraphSink; import org.apache.jena.sparql.core.DatasetGraphZero; import org.apache.jena.sparql.graph.GraphFactory ; import org.junit.Assert ; @@ -48,16 +49,17 @@ public static Iterable data() { x.add(new Object[] {"create", (Creator)()->DatasetGraphFactory.create(), true, false}) ; - x.add(new Object[] {"wrap" , - (Creator)()-> - DatasetGraphFactory.wrap(GraphFactory.createDefaultGraph()), - true, true}) ; - x.add(new Object[] {"createZeroGraph" , + x.add(new Object[] {"wrap(Graph)" , + (Creator)()->DatasetGraphFactory.wrap(GraphFactory.createDefaultGraph()), + true, false}) ; + x.add(new Object[] {"zero" , (Creator)()->new DatasetGraphZero(), true, true}) ; + x.add(new Object[] {"sink" , + (Creator)()->new DatasetGraphSink(), + true, true}) ; x.add(new Object[] {"create(Graph)", - (Creator)()-> - DatasetGraphFactory.create(GraphFactory.createDefaultGraph()), + (Creator)()->DatasetGraphFactory.create(GraphFactory.createDefaultGraph()), true, false}) ; return x ; } diff --git a/jena-integration-tests/src/test/java/org/apache/jena/test/assembler/TS_Assembler.java b/jena-integration-tests/src/test/java/org/apache/jena/test/assembler/TS_Assembler.java new file mode 100644 index 00000000000..053f322a460 --- /dev/null +++ b/jena-integration-tests/src/test/java/org/apache/jena/test/assembler/TS_Assembler.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.test.assembler; + +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +@RunWith(Suite.class) +@Suite.SuiteClasses( { + TestDatasetAssembler.class +}) + +public class TS_Assembler { + +} diff --git a/jena-integration-tests/src/test/java/org/apache/jena/test/assembler/TestDatasetAssembler.java b/jena-integration-tests/src/test/java/org/apache/jena/test/assembler/TestDatasetAssembler.java new file mode 100644 index 00000000000..c1a8e620dc2 --- /dev/null +++ b/jena-integration-tests/src/test/java/org/apache/jena/test/assembler/TestDatasetAssembler.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jena.test.assembler; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import org.apache.jena.assembler.exceptions.AssemblerException; +import org.apache.jena.atlas.logging.Log; +import org.apache.jena.query.ARQ; +import org.apache.jena.query.Dataset; +import org.apache.jena.query.TxnType; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.Property; +import org.apache.jena.rdf.model.Resource; +import org.apache.jena.rdf.model.Statement; +import org.apache.jena.riot.RDFDataMgr; +import org.apache.jena.sparql.core.DatasetOne; +import org.apache.jena.sparql.core.TxnDataset2Graph; +import org.apache.jena.sparql.core.assembler.AssemblerUtils; +import org.apache.jena.sparql.core.assembler.DatasetAssemblerVocab; +import org.apache.jena.system.JenaSystem; +import org.apache.jena.system.Txn; +import org.apache.jena.test.txn.TestDataset2Graph; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * Tests of building datasets with assemblers. + */ +public class TestDatasetAssembler { + static { JenaSystem.init(); } + + @SuppressWarnings("deprecation") + @BeforeClass public static void beforeClass() { + if ( ! TxnDataset2Graph.TXN_DSG_GRAPH ) + Log.warn(TestDataset2Graph.class, "**** TxnDataset2Graph.TXN_DSG_GRAPH is false in the system setup ****"); + } + + protected static String DIR = "testing/Assembler/"; + + static private Model data = RDFDataMgr.loadModel(DIR + "data.ttl"); + static private Resource s = data.createResource("http://example/data/s"); + static private Property p = data.createProperty("http://example/data/p"); + static private Resource o = data.createResource("http://example/data/o"); + static private Statement stmt = data.createStatement(s, p, o); + + // See also jena-arq/etc/... + + // ---- Null dataset assemblers + + @Test public void dsg_zero() { + Dataset ds = (Dataset)AssemblerUtils.build(DIR+"assem_dsg_zero.ttl", DatasetAssemblerVocab.tDatasetZero); + assertNotNull(ds); + try { + ds.getDefaultModel().add(stmt); + } catch (UnsupportedOperationException ex) {} + } + + @Test public void dsg_sink() { + Dataset ds = (Dataset)AssemblerUtils.build(DIR+"assem_dsg_sink.ttl", DatasetAssemblerVocab.tDatasetSink); + assertNotNull(ds); + assertTrue(ds.getContext().isDefined(ARQ.queryTimeout)); + ds.getDefaultModel().add(stmt); + assertEquals(0, ds.getDefaultModel().size()); + } + + // ---- DatasetOneAssembler + + @Test public void dsg1_1() { + Dataset ds = (Dataset)AssemblerUtils.build(DIR+"assem_dsg1_1.ttl", DatasetAssemblerVocab.tDatasetOne); + assertNotNull(ds); + assertNotNull(ds.getDefaultModel()); + assertTrue(ds instanceof DatasetOne); + useIt(ds); + } + + @Test public void dsg1_2() { + Dataset ds = (Dataset)AssemblerUtils.build(DIR+"assem_dsg1_2.ttl", DatasetAssemblerVocab.tDatasetOne); + assertNotNull(ds); + assertNotNull(ds.getDefaultModel()); + assertTrue(ds instanceof DatasetOne); + readIt(ds); + } + + @Test public void dsg1_3() { + Dataset ds = (Dataset)AssemblerUtils.build(DIR+"assem_dsg1_3.ttl", DatasetAssemblerVocab.tDatasetOne); + assertNotNull(ds); + assertNotNull(ds.getDefaultModel()); + assertTrue(ds instanceof DatasetOne); + readIt(ds); + } + + @Test(expected=AssemblerException.class) + public void dsg1_bad_1() { + Dataset ds = (Dataset)AssemblerUtils.build(DIR+"assem_dsg1_bad_1.ttl", DatasetAssemblerVocab.tDatasetOne); + assertNotNull(ds); + } + + @Test public void dsg1_inf_tdb1_1() { + Dataset ds = (Dataset)AssemblerUtils.build(DIR+"assem_dsg1_inf_tdb1.ttl", DatasetAssemblerVocab.tDatasetOne); + assertNotNull(ds); + assertNotNull(ds.getDefaultModel()); + assertTrue(ds instanceof DatasetOne); + useIt(ds); + } + + @Test public void dsg1_inf_tdb1_2() { + Dataset ds = (Dataset)AssemblerUtils.build(DIR+"assem_dsg1_inf_tdb2.ttl", DatasetAssemblerVocab.tDatasetOne); + assertNotNull(ds); + assertNotNull(ds.getDefaultModel()); + assertTrue(ds instanceof DatasetOne); + useIt(ds); + } + + private void readIt(Dataset ds) { + Txn.exec(ds, TxnType.READ, ()->{ + assertTrue(ds.getDefaultModel().contains(stmt)); + }); + } + + private void useIt(Dataset ds) { + Txn.executeWrite(ds, ()->{ + ds.getDefaultModel().add(data); + }); + readIt(ds); + } +} diff --git a/jena-integration-tests/src/test/java/org/apache/jena/test/rdfconnection/TestRDFConnectionRemote.java b/jena-integration-tests/src/test/java/org/apache/jena/test/rdfconnection/TestRDFConnectionRemote.java index ef8675540d4..d064b6da967 100644 --- a/jena-integration-tests/src/test/java/org/apache/jena/test/rdfconnection/TestRDFConnectionRemote.java +++ b/jena-integration-tests/src/test/java/org/apache/jena/test/rdfconnection/TestRDFConnectionRemote.java @@ -35,10 +35,11 @@ public class TestRDFConnectionRemote extends AbstractTestRDFConnection { private static FusekiServer server ; private static DatasetGraph serverdsg = DatasetGraphFactory.createTxnMem() ; - protected static int PORT = FusekiLib.choosePort(); + protected static int PORT; @BeforeClass public static void beforeClass() { + PORT = FusekiLib.choosePort(); server = FusekiServer.create() .setPort(PORT) .add("/ds", serverdsg) diff --git a/jena-integration-tests/src/test/java/org/apache/jena/test/txn/TestDataset2Graph.java b/jena-integration-tests/src/test/java/org/apache/jena/test/txn/TestDataset2Graph.java index 38e1e5522de..4d515b6a7f7 100644 --- a/jena-integration-tests/src/test/java/org/apache/jena/test/txn/TestDataset2Graph.java +++ b/jena-integration-tests/src/test/java/org/apache/jena/test/txn/TestDataset2Graph.java @@ -20,13 +20,13 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; import java.util.Arrays; import java.util.Collection; import org.apache.jena.atlas.iterator.Iter; import org.apache.jena.atlas.lib.Creator; +import org.apache.jena.atlas.logging.Log; import org.apache.jena.graph.Triple; import org.apache.jena.query.*; import org.apache.jena.rdf.model.Model; @@ -63,6 +63,9 @@ public class TestDataset2Graph { // TXN_DSG_GRAPH must be true. @SuppressWarnings("deprecation") @BeforeClass public static void beforeClass() { + if ( ! TxnDataset2Graph.TXN_DSG_GRAPH ) + Log.warn(TestDataset2Graph.class, "**** TxnDataset2Graph.TXN_DSG_GRAPH is false in the system setup ****"); + txn_dsg_graph = TxnDataset2Graph.TXN_DSG_GRAPH; TxnDataset2Graph.TXN_DSG_GRAPH = true; // Make sure sync isn't called. @@ -78,13 +81,13 @@ public class TestDataset2Graph { @Parameters(name = "{index}: {0}") public static Collection data() { - Creator datasetPlainMaker = ()-> DatasetFactory.createTxnMem() ; + Creator datasetPlainMaker = ()-> DatasetFactory.createGeneral() ; Creator datasetTxnMemMaker = ()-> DatasetFactory.createTxnMem() ; Creator datasetTDB1 = ()-> TDBFactory.createDataset(); Creator datasetTDB2 = ()-> TDB2Factory.createDataset(); return Arrays.asList(new Object[][] { - { "Plain", datasetPlainMaker }, - { "TIM", datasetTxnMemMaker }, + { "Plain", datasetPlainMaker }, + { "TIM", datasetTxnMemMaker }, { "TDB1", datasetTDB1 }, { "TDB2", datasetTDB2 } }); @@ -96,15 +99,15 @@ public TestDataset2Graph(String name, Creator creator) { this.creator = creator; } - @Test public void dsgGraph_model() { + @Test public void dsgGraphTxn_model() { testInfModel(creator.create()); } - @Test public void dsgGraphTxnTDB_dataset_wrap() { + @Test public void dsgGraphTxn_dataset_wrap() { testOverDS(creator.create(), true); } - @Test public void dsgGraphTxnTDB_dataset_create() { + @Test public void dsgGraphTxn_dataset_create() { testOverDS(creator.create(), false); } private static void testInfModel(Dataset ds0) { @@ -129,7 +132,7 @@ private static void testOverDS(Dataset ds0, boolean wrap) { try ( RDFConnection conn = RDFConnectionFactory.connect(ds1) ) { - conn.querySelect("SELECT (count(*) AS ?C) { ?s ?p ?o } HAVING (?C = 0)", (qs)-> fail("Didn't expect any query solutions")); + //conn.querySelect("SELECT (count(*) AS ?C) { ?s ?p ?o } HAVING (?C = 0)", (qs)-> fail("Didn't expect any query solutions")); // Necessary Txn.exec(conn, TxnType.READ, ()->{ diff --git a/jena-integration-tests/testing/Assembler/assem_dsg1_1.ttl b/jena-integration-tests/testing/Assembler/assem_dsg1_1.ttl new file mode 100644 index 00000000000..046659ae09d --- /dev/null +++ b/jena-integration-tests/testing/Assembler/assem_dsg1_1.ttl @@ -0,0 +1,11 @@ +## Licensed under the terms of http://www.apache.org/licenses/LICENSE-2.0 + +PREFIX : <#> +PREFIX rdf: +PREFIX rdfs: +PREFIX tdb: +PREFIX ja: + +# No default graph specified means add an in-memory one. +<#dataset> rdf:type ja:RDFDatasetOne . + diff --git a/jena-integration-tests/testing/Assembler/assem_dsg1_2.ttl b/jena-integration-tests/testing/Assembler/assem_dsg1_2.ttl new file mode 100644 index 00000000000..00c33248240 --- /dev/null +++ b/jena-integration-tests/testing/Assembler/assem_dsg1_2.ttl @@ -0,0 +1,15 @@ +## Licensed under the terms of http://www.apache.org/licenses/LICENSE-2.0 + +PREFIX : <#> +PREFIX rdf: +PREFIX rdfs: +PREFIX tdb: +PREFIX ja: + +<#dataset> rdf:type ja:RDFDatasetOne ; + ja:defaultGraph <#graph> . + +<#graph> a ja:MemoryModel ; + ja:content [ja:externalContent ] ; + . + diff --git a/jena-integration-tests/testing/Assembler/assem_dsg1_3.ttl b/jena-integration-tests/testing/Assembler/assem_dsg1_3.ttl new file mode 100644 index 00000000000..af8d98c2d70 --- /dev/null +++ b/jena-integration-tests/testing/Assembler/assem_dsg1_3.ttl @@ -0,0 +1,15 @@ +## Licensed under the terms of http://www.apache.org/licenses/LICENSE-2.0 + +PREFIX : <#> +PREFIX rdf: +PREFIX rdfs: +PREFIX tdb: +PREFIX ja: + +<#dataset> rdf:type ja:RDFDatasetOne ; + ja:graph <#graph> . + +<#graph> a ja:MemoryModel ; + ja:content [ja:externalContent ] ; + . + diff --git a/jena-integration-tests/testing/Assembler/assem_dsg1_bad_1.ttl b/jena-integration-tests/testing/Assembler/assem_dsg1_bad_1.ttl new file mode 100644 index 00000000000..5ab7911da88 --- /dev/null +++ b/jena-integration-tests/testing/Assembler/assem_dsg1_bad_1.ttl @@ -0,0 +1,15 @@ +## Licensed under the terms of http://www.apache.org/licenses/LICENSE-2.0 + +PREFIX : <#> +PREFIX rdf: +PREFIX rdfs: +PREFIX tdb: +PREFIX ja: + +<#dataset> rdf:type ja:RDFDatasetOne ; + ## Illegal + ja:namedGraph [ ja:graphName ; + ja:graph :data1 ] ; + . + +<#graph> a ja:MemoryModel . diff --git a/jena-integration-tests/testing/Assembler/assem_dsg1_inf_tdb1.ttl b/jena-integration-tests/testing/Assembler/assem_dsg1_inf_tdb1.ttl new file mode 100644 index 00000000000..00cd419e7ae --- /dev/null +++ b/jena-integration-tests/testing/Assembler/assem_dsg1_inf_tdb1.ttl @@ -0,0 +1,31 @@ +## Licensed under the terms of http://www.apache.org/licenses/LICENSE-2.0 + +## Assembler for a dataset of a single inference model, +## with the base storage in TDB1. + +PREFIX : <#> +PREFIX rdf: +PREFIX rdfs: +PREFIX tdb: +PREFIX ja: + +<#dataset> rdf:type ja:RDFDatasetOne ; + ja:defaultGraph <#model_inf> ; + . + +<#model_inf> a ja:InfModel ; + ja:baseModel <#tdbGraph> ; + ja:reasoner [ + ja:reasonerURL + ] . + +## Graph from TDB +<#tdbGraph> rdf:type tdb:GraphTDB ; + tdb:dataset <#tdbDataset> . + + +## Base data in TDB. +<#tdbDataset> rdf:type tdb:DatasetTDB ; + tdb:location "--mem--" ; + . + diff --git a/jena-integration-tests/testing/Assembler/assem_dsg1_inf_tdb2.ttl b/jena-integration-tests/testing/Assembler/assem_dsg1_inf_tdb2.ttl new file mode 100644 index 00000000000..76c50105f0c --- /dev/null +++ b/jena-integration-tests/testing/Assembler/assem_dsg1_inf_tdb2.ttl @@ -0,0 +1,30 @@ +## Licensed under the terms of http://www.apache.org/licenses/LICENSE-2.0 + +## Assembler for a dataset of a single inference model, +## with the base storage in TDB2. + +PREFIX : <#> +PREFIX rdf: +PREFIX rdfs: +PREFIX tdb2: +PREFIX ja: + +<#dataset> rdf:type ja:RDFDatasetOne ; + ja:defaultGraph <#model_inf> ; + . + +<#model_inf> a ja:InfModel ; + ja:baseModel <#tdbGraph> ; + ja:reasoner [ + ja:reasonerURL + ] . + +## Graph from the database +<#tdbGraph> rdf:type tdb2:GraphTDB2 ; + tdb2:dataset <#tdbDataset> . + +## Base data in TDB2. +<#tdbDataset> rdf:type tdb2:DatasetTDB2 ; + tdb2:location "--mem--" ; + . + diff --git a/jena-integration-tests/testing/Assembler/assem_dsg_sink.ttl b/jena-integration-tests/testing/Assembler/assem_dsg_sink.ttl new file mode 100644 index 00000000000..58f608cf181 --- /dev/null +++ b/jena-integration-tests/testing/Assembler/assem_dsg_sink.ttl @@ -0,0 +1,11 @@ +## Licensed under the terms of http://www.apache.org/licenses/LICENSE-2.0 + +PREFIX : <#> +PREFIX rdf: +PREFIX rdfs: +PREFIX tdb: +PREFIX ja: + +<#dataset> rdf:type ja:RDFDatasetSink ; + ja:context [ ja:cxtName "arq:queryTimeout" ; ja:cxtValue "300" ] ; + . diff --git a/jena-integration-tests/testing/Assembler/assem_dsg_zero.ttl b/jena-integration-tests/testing/Assembler/assem_dsg_zero.ttl new file mode 100644 index 00000000000..ba76fc2bf5d --- /dev/null +++ b/jena-integration-tests/testing/Assembler/assem_dsg_zero.ttl @@ -0,0 +1,9 @@ +## Licensed under the terms of http://www.apache.org/licenses/LICENSE-2.0 + +PREFIX : <#> +PREFIX rdf: +PREFIX rdfs: +PREFIX tdb: +PREFIX ja: + +<#dataset> rdf:type ja:RDFDatasetZero . diff --git a/jena-integration-tests/testing/Assembler/data.ttl b/jena-integration-tests/testing/Assembler/data.ttl new file mode 100644 index 00000000000..e0d78e94225 --- /dev/null +++ b/jena-integration-tests/testing/Assembler/data.ttl @@ -0,0 +1,5 @@ +PREFIX : + +:s :p :o . +:s :q 12 . +:s :q 12.0 . From 1f149507a811174b15150b6ed119a748fe48f99b Mon Sep 17 00:00:00 2001 From: Andy Seaborne Date: Mon, 5 Mar 2018 18:39:19 +0000 Subject: [PATCH 8/8] Fix typos --- .../jena/sparql/core/TxnDataset2Graph.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/TxnDataset2Graph.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/TxnDataset2Graph.java index 564c1dbcf58..4d84b700f95 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/core/TxnDataset2Graph.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/TxnDataset2Graph.java @@ -50,15 +50,15 @@ *

* Best practice is to change the graph membership outside of any transaction, * ideally at setup time of the object using this class. (Caution: SPARQL Update - * can create graphs. + * can create graphs.) * @See {@link DatasetGraphMapLink} * @See {@link DatasetGraphOne} */ public class TxnDataset2Graph extends TransactionalLock { /** * Control whether to pass down transactions from the dataset to the graph in the - * dataset. This should be set to "true"; setting it "false" causes the onld, - * no-transaction passing behaviour. + * dataset. This should be set to "true"; setting it "false" causes the old, + * non-transaction passing behaviour. *

* This is temporary flag during the transition because the change at Jena 3.7.0 needs * to be proven in real deployments as well as testing. "false" restores the Jena @@ -99,17 +99,18 @@ private static void addHandler(Map handlers, Graph g } // Determine the key - an object that is the unit of transactions. - // For two graphs form the same DatasetGraph, i.e. GraphView, there should be one transaction. + // For two graphs from the same DatasetGraph, i.e. GraphView, there should be one transaction. private static Object calcKey(Graph graph) { if ( graph instanceof GraphView ) // Use the database as the key so that transactions are started once-per-storage. - // This the case of a graph from some storage being plavced in a general dataset. + // This the case of a graph from some storage being placed in a general dataset. return ((GraphView)graph).getDataset(); if ( graph instanceof InfGraph ) - // InfGraph does actual pass done in its TransactionHandler. - // This allows the base graph to be included in the dataset as well as the InfGraph. + // InfGraph TransactionHandler passes the graph transaction to the base graph. + // This calcKey allows the base graph to be included in the dataset as well as the InfGraph. return calcKey(((InfGraph)graph).getRawGraph()); - + // These should be handled by their respective graph transaction handler + // or the graph is overriding that behaviour for some reason. // if ( graph instanceof GraphWrapper ) // return calcKey(((GraphWrapper)graph).get()); // if ( graph instanceof WrappedGraph ) @@ -213,7 +214,7 @@ public void begin(TxnType type) { @Override public void commit() { handlers(h->h.commit()); - // Before super.commit - we stil hold the lock. + // Before super.commit - we still hold the lock. finish(); super.commit(); } @@ -231,7 +232,7 @@ public void end() { error("Write transaction - no commit or abort before end()") ; // Need to put this in between the two parts of end(). if ( super.isInTransaction() ) { - // Must be READ at this point. + // Must be READ mode at this point. handlers(h->h.commit()); finish(); }