You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by an...@apache.org on 2018/01/04 20:46:22 UTC

[08/14] jena git commit: Fix to work with tests. Cleanup.

Fix to work with tests. Cleanup.


Project: http://git-wip-us.apache.org/repos/asf/jena/repo
Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/edab900a
Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/edab900a
Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/edab900a

Branch: refs/heads/master
Commit: edab900abf7fc05d7ffc8e9fcb7b41d1b1647625
Parents: d6770ea
Author: Andy Seaborne <an...@apache.org>
Authored: Sun Dec 31 20:49:18 2017 +0000
Committer: Andy Seaborne <an...@apache.org>
Committed: Tue Jan 2 15:58:30 2018 +0000

----------------------------------------------------------------------
 .../java/org/apache/jena/query/Dataset.java     |   7 -
 .../java/org/apache/jena/query/TxnType.java     |  21 +-
 .../jena/sparql/core/DatasetGraphMap.java       |  18 +-
 .../jena/sparql/core/DatasetGraphMapLink.java   |  17 +-
 .../jena/sparql/core/DatasetGraphOne.java       |  18 +-
 .../jena/sparql/core/DatasetGraphSink.java      |   5 +-
 .../jena/sparql/core/DatasetGraphWithLock.java  |   3 +
 .../jena/sparql/core/DatasetGraphZero.java      |   8 +-
 .../apache/jena/sparql/core/DatasetImpl.java    |  36 +-
 .../apache/jena/sparql/core/Transactional.java  |   4 +-
 .../jena/sparql/core/TransactionalLock.java     |  72 +-
 .../jena/sparql/core/TransactionalMutex.java    |  75 ---
 .../sparql/core/TransactionalNotSupported.java  |  32 +-
 .../core/TransactionalNotSupportedMixin.java    |  17 +-
 .../jena/sparql/core/TransactionalNull.java     |  55 +-
 .../sparql/core/mem/DatasetGraphInMemory.java   |  41 +-
 .../java/org/apache/jena/system/TxnCounter.java | 191 ++++--
 .../org/apache/jena/sparql/core/TS_Core.java    |   1 -
 .../sparql/core/TestDatasetGraphWithLock.java   |  30 -
 .../core/mem/TestDatasetGraphInMemoryLock.java  |   4 +-
 .../transaction/AbstractTestTransPromote.java   |  51 +-
 .../AbstractTestTransactionLifecycle.java       | 187 ++++-
 .../jena/sparql/transaction/TS_Transaction.java |   2 -
 .../TestTransactionDSGWithLockNoAbort.java      |  41 --
 .../TestTransactionDSGWithLockWithAbort.java    |  38 --
 .../transaction/TestTransactionSupport.java     |   6 +-
 .../java/org/apache/jena/atlas/lib/Lib.java     |  11 +
 .../jena/dboe/transaction/txn/Transaction.java  |   5 +-
 .../transaction/txn/TransactionCoordinator.java |  28 +-
 .../dboe/transaction/txn/TransactionalBase.java |  23 +-
 .../transaction/TestTransactionLifecycle.java   | 179 ++++-
 .../transaction/TestTransactionLifecycle2.java  |   8 +-
 .../apache/jena/fuseki/servlets/HttpAction.java |   2 +-
 .../jena/rdfconnection/RDFConnectionLocal.java  |  26 +-
 .../rdfconnection/RDFConnectionModular.java     |  27 +-
 .../jena/rdfconnection/RDFConnectionRemote.java |  71 +-
 .../apache/jena/sdb/store/DatasetGraphSDB.java  |  25 +-
 .../org/apache/jena/tdb/StoreConnection.java    |   8 +-
 .../apache/jena/tdb/store/DatasetGraphTDB.java  |  18 +-
 .../jena/tdb/transaction/BlockMgrJournal.java   |   2 +-
 .../transaction/DatasetGraphTransaction.java    |  52 +-
 .../jena/tdb/transaction/DatasetGraphTxn.java   |   7 +
 .../jena/tdb/transaction/Transaction.java       |   9 +-
 .../tdb/transaction/TransactionManager.java     |  92 ++-
 .../jena/tdb/extra/T_TDBWriteTransaction.java   | 151 -----
 .../jena/tdb/extra/T_TimeoutTDBPattern.java     | 112 ---
 .../apache/jena/tdb/extra/T_TransSystem.java    | 674 -------------------
 .../tdb/extra/T_TransSystemMultiDatasets.java   | 553 ---------------
 .../jena/tdb/extra/T_TxnDeadlockTest.java       | 141 ----
 .../tdb/store/AbstractStoreConnections.java     |  30 +-
 .../AbstractTestObjectFileTrans.java            |   2 +-
 .../AbstractTestObjectFileTransComplex.java     |   2 +-
 .../tdb/transaction/AbstractTestTransSeq.java   |   6 -
 .../tdb/transaction/TestTransPromoteTDB.java    |   5 +-
 .../tdb/transaction/TestTransactionTDB.java     |   3 +-
 55 files changed, 943 insertions(+), 2309 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/edab900a/jena-arq/src/main/java/org/apache/jena/query/Dataset.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/query/Dataset.java b/jena-arq/src/main/java/org/apache/jena/query/Dataset.java
index 38b1c22..6f8bc0e 100644
--- a/jena-arq/src/main/java/org/apache/jena/query/Dataset.java
+++ b/jena-arq/src/main/java/org/apache/jena/query/Dataset.java
@@ -32,13 +32,6 @@ import org.apache.jena.sparql.util.Context ;
 
 public interface Dataset extends Transactional 
 {
-    // TEMP
-
-    @Override
-    public default void begin(TxnType type) { throw new UnsupportedOperationException("Dataset.begin(TxnType)"); }
-
-    
-    
     /** Get the default graph as a Jena Model */
     public Model getDefaultModel() ;
     

http://git-wip-us.apache.org/repos/asf/jena/blob/edab900a/jena-arq/src/main/java/org/apache/jena/query/TxnType.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/query/TxnType.java b/jena-arq/src/main/java/org/apache/jena/query/TxnType.java
index 3679414..39d5dc8 100644
--- a/jena-arq/src/main/java/org/apache/jena/query/TxnType.java
+++ b/jena-arq/src/main/java/org/apache/jena/query/TxnType.java
@@ -18,6 +18,8 @@
 
 package org.apache.jena.query;
 
+import java.util.Objects;
+
 import org.apache.jena.sparql.JenaTransactionException;
 
 public enum TxnType {
@@ -46,7 +48,7 @@ public enum TxnType {
      */
     READ, WRITE, READ_PROMOTE, READ_COMMITTED_PROMOTE
     ;
-    
+    /** Convert a {@link ReadWrite} mode to {@code TxnType} */
     public static TxnType convert(ReadWrite rw) {
         switch(rw) {
             case READ: return READ;
@@ -54,11 +56,22 @@ public enum TxnType {
             default: throw new NullPointerException();
         }
     }
-    public static ReadWrite convert(TxnType mode) {
-        switch(mode) {
+    /** Convert a {@code TxnType} mode to {@link ReadWrite} : "promote" not supported.  */
+    public static ReadWrite convert(TxnType txnType) {
+        Objects.requireNonNull(txnType);
+        switch(txnType) {
             case READ: return ReadWrite.READ;
             case WRITE: return ReadWrite.WRITE;
-            default: throw new JenaTransactionException("Incompatible mode: "+mode);
+            default: throw new JenaTransactionException("Incompatible mode: "+txnType);
         }
     }
+    /** 
+     * Translate a {@code TxnType} to it's initial {@link ReadWrite} mode.
+     * {@code WRITE -> WRITE}, {@code READ* -> READ} regardless of promotion setting. 
+     */ 
+    public static ReadWrite initial(TxnType txnType) {
+        Objects.requireNonNull(txnType);
+        return (txnType == TxnType.WRITE) ? ReadWrite.WRITE : ReadWrite.READ;
+    }
+
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jena/blob/edab900a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphMap.java
----------------------------------------------------------------------
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 24d3e04..d25aad1 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
@@ -30,6 +30,7 @@ import org.apache.jena.graph.Graph ;
 import org.apache.jena.graph.Node ;
 import org.apache.jena.graph.Triple ;
 import org.apache.jena.query.ReadWrite ;
+import org.apache.jena.query.TxnType;
 import org.apache.jena.sparql.ARQException ;
 import org.apache.jena.sparql.core.DatasetGraphFactory.GraphMaker ;
 import org.apache.jena.sparql.graph.GraphUnionRead ;
@@ -69,13 +70,18 @@ public class DatasetGraphMap extends DatasetGraphTriplesQuads
     
     // ----
     private final Transactional txn                     = TransactionalLock.createMRSW() ;
-    @Override public void begin(ReadWrite mode)         { txn.begin(mode) ; }
-    @Override public void commit()                      { txn.commit() ; }
-    @Override public void abort()                       { txn.abort() ; }
-    @Override public boolean isInTransaction()          { return txn.isInTransaction() ; }
+    @Override public void begin()                       { txn.begin(); }
+    @Override public void begin(TxnType txnType)        { txn.begin(txnType); }
+    @Override public void begin(ReadWrite mode)         { txn.begin(mode); }
+    @Override public boolean promote()                  { return txn.promote(); }
+    @Override public void commit()                      { txn.commit(); }
+    @Override public void abort()                       { txn.abort(); }
+    @Override public boolean isInTransaction()          { return txn.isInTransaction(); }
     @Override public void end()                         { txn.end(); }
-    @Override public boolean supportsTransactions()     { return true ; }
-    @Override public boolean supportsTransactionAbort() { return false ; }
+    @Override public ReadWrite transactionMode()        { return txn.transactionMode(); }
+    @Override public TxnType transactionType()          { return txn.transactionType(); }
+    @Override public boolean supportsTransactions()     { return true; }
+    @Override public boolean supportsTransactionAbort() { return false; }
     // ----
     
     @Override

http://git-wip-us.apache.org/repos/asf/jena/blob/edab900a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphMapLink.java
----------------------------------------------------------------------
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 605778a..f429192 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
@@ -25,6 +25,7 @@ import java.util.Map ;
 import org.apache.jena.graph.Graph ;
 import org.apache.jena.graph.Node ;
 import org.apache.jena.query.ReadWrite ;
+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.GraphUnionRead ;
@@ -119,7 +120,6 @@ public class DatasetGraphMapLink extends DatasetGraphCollection
 
     // ----
     private final Transactional txn                     = TransactionalLock.createMRSW() ;
-    @Override public void begin(ReadWrite mode)         { txn.begin(mode) ; }
 
     @Override
     public void commit() {
@@ -127,11 +127,18 @@ public class DatasetGraphMapLink extends DatasetGraphCollection
         txn.commit() ;
     }
 
-    @Override public void abort()                       { txn.abort() ; }
-    @Override public boolean isInTransaction()          { return txn.isInTransaction() ; }
+    @Override public void begin()                       { txn.begin(); }
+    @Override public void begin(TxnType txnType)        { txn.begin(txnType); }
+    @Override public void begin(ReadWrite mode)         { txn.begin(mode); }
+    @Override public boolean promote()                  { return txn.promote(); }
+    //@Override public void commit()                      { txn.commit(); }
+    @Override public void abort()                       { txn.abort(); }
+    @Override public boolean isInTransaction()          { return txn.isInTransaction(); }
     @Override public void end()                         { txn.end(); }
-    @Override public boolean supportsTransactions()     { return true ; }
-    @Override public boolean supportsTransactionAbort() { return false ; }
+    @Override public ReadWrite transactionMode()        { return txn.transactionMode(); }
+    @Override public TxnType transactionType()          { return txn.transactionType(); }
+    @Override public boolean supportsTransactions()     { return true; }
+    @Override public boolean supportsTransactionAbort() { return false; }
     // ----
 
     @Override

http://git-wip-us.apache.org/repos/asf/jena/blob/edab900a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphOne.java
----------------------------------------------------------------------
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 8c487be..601789e 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
@@ -28,6 +28,7 @@ import org.apache.jena.graph.Graph;
 import org.apache.jena.graph.Node;
 import org.apache.jena.graph.Triple;
 import org.apache.jena.query.ReadWrite;
+import org.apache.jena.query.TxnType;
 
 /** DatasetGraph of a single graph as default graph.
  * <p>
@@ -72,14 +73,17 @@ public class DatasetGraphOne extends DatasetGraphBaseFind {
         supportsAbort = false; 
     }
     
-    @Override public void begin(ReadWrite mode)         { txn.begin(mode) ; }
-    @Override public void commit()                      { txn.commit() ; }
-    @Override public void abort()                       { txn.abort() ; }
-    @Override public boolean isInTransaction()          { return txn.isInTransaction() ; }
+    @Override public void begin(TxnType txnType)        { txn.begin(txnType); }
+    @Override public void begin(ReadWrite mode)         { txn.begin(mode); }
+    @Override public void commit()                      { txn.commit(); }
+    @Override public void abort()                       { txn.abort(); }
+    @Override public boolean isInTransaction()          { return txn.isInTransaction(); }
     @Override public void end()                         { txn.end(); }
-    @Override public boolean supportsTransactions()     { return true ; }
-    @Override public boolean supportsTransactionAbort() { return supportsAbort ; }
-
+    @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 containsGraph(Node graphNode) {
         if ( isDefaultGraph(graphNode) )

http://git-wip-us.apache.org/repos/asf/jena/blob/edab900a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphSink.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphSink.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphSink.java
index 64735d0..b8852a1 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphSink.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphSink.java
@@ -24,6 +24,7 @@ import org.apache.jena.atlas.iterator.Iter;
 import org.apache.jena.graph.Graph;
 import org.apache.jena.graph.Node;
 import org.apache.jena.query.ReadWrite;
+import org.apache.jena.query.TxnType;
 import org.apache.jena.sparql.graph.GraphSink;
 
 
@@ -41,13 +42,15 @@ public class DatasetGraphSink extends DatasetGraphBaseFind {
     public DatasetGraphSink() {}
     
     private TransactionalNull txn                       = TransactionalNull.create();
+    @Override public void begin(TxnType txnType)        { txn.begin(txnType); }
     @Override public void begin(ReadWrite mode)         { txn.begin(mode); }
     @Override public void commit()                      { txn.commit(); }
     @Override public void abort()                       { txn.abort(); }
     @Override public boolean isInTransaction()          { return txn.isInTransaction(); }
     @Override public void end()                         { txn.end(); }
+    @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

http://git-wip-us.apache.org/repos/asf/jena/blob/edab900a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphWithLock.java
----------------------------------------------------------------------
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 86ae484..815414e 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
@@ -31,7 +31,10 @@ import org.apache.jena.sparql.util.Context ;
  * behaviour, that is the application see transaction but they are not durable.
  * Only provides multiple-reader OR single-writer, and no write-transaction
  * abort.
+ * @deprecated Will be removed.
  */
+// NOT USED
+@Deprecated
 public class DatasetGraphWithLock extends DatasetGraphTrackActive implements Sync {
     private final ThreadLocal<Boolean> writeTxn = ThreadLocal.withInitial(()->false) ;
     private final DatasetGraph dsg ;

http://git-wip-us.apache.org/repos/asf/jena/blob/edab900a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphZero.java
----------------------------------------------------------------------
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 a5a813b..4ada686 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
@@ -24,6 +24,7 @@ import org.apache.jena.atlas.iterator.Iter;
 import org.apache.jena.graph.Graph;
 import org.apache.jena.graph.Node;
 import org.apache.jena.query.ReadWrite;
+import org.apache.jena.query.TxnType;
 import org.apache.jena.sparql.graph.GraphZero;
 
 /** An always empty {@link DatasetGraph}. 
@@ -40,14 +41,19 @@ public class DatasetGraphZero extends DatasetGraphBaseFind {
     public DatasetGraphZero() {}
     
     private TransactionalNull txn                       = TransactionalNull.create();
+    @Override public void begin()                       { txn.begin(); }
+    @Override public void begin(TxnType txnType)        { txn.begin(txnType); }
     @Override public void begin(ReadWrite mode)         { txn.begin(mode); }
+    @Override public boolean promote()                  { return txn.promote(); }
     @Override public void commit()                      { txn.commit(); }
     @Override public void abort()                       { txn.abort(); }
     @Override public boolean isInTransaction()          { return txn.isInTransaction(); }
     @Override public void end()                         { txn.end(); }
+    @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 Iterator<Node> listGraphNodes() {

http://git-wip-us.apache.org/repos/asf/jena/blob/edab900a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetImpl.java
----------------------------------------------------------------------
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 00e419a..dfb8262 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
@@ -25,6 +25,7 @@ import org.apache.jena.graph.Node ;
 import org.apache.jena.graph.NodeFactory ;
 import org.apache.jena.query.Dataset ;
 import org.apache.jena.query.ReadWrite ;
+import org.apache.jena.query.TxnType;
 import org.apache.jena.rdf.model.Model ;
 import org.apache.jena.rdf.model.ModelFactory ;
 import org.apache.jena.shared.Lock ;
@@ -96,13 +97,15 @@ public class DatasetImpl implements Dataset
     }
     
     @Override
-    public boolean supportsTransactions() {
-        return dsg.supportsTransactions() ;
+    public void begin() {
+        checkTransactional();
+        transactional.begin();
     }
 
     @Override
-    public boolean supportsTransactionAbort() {
-        return dsg.supportsTransactionAbort() ;
+    public void begin(TxnType txnType) {
+        checkTransactional();
+        transactional.begin(txnType);
     }
 
     @Override
@@ -110,6 +113,31 @@ public class DatasetImpl implements Dataset
         checkTransactional();
         transactional.begin(mode);
     }
+
+    @Override
+    public boolean promote() {
+        return transactional.promote();
+    }
+
+    @Override
+    public ReadWrite transactionMode() {
+        return transactional.transactionMode();
+    }
+
+    @Override
+    public TxnType transactionType() {
+        return transactional.transactionType();
+    }
+
+    @Override
+    public boolean supportsTransactions() {
+        return true;
+    }
+
+    @Override
+    public boolean supportsTransactionAbort() {
+        return false;
+    }
     
     /** Say whether a transaction is active */ 
     @Override

http://git-wip-us.apache.org/repos/asf/jena/blob/edab900a/jena-arq/src/main/java/org/apache/jena/sparql/core/Transactional.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/Transactional.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/Transactional.java
index 8425a18..08bc230 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/core/Transactional.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/Transactional.java
@@ -130,8 +130,8 @@ public interface Transactional
     public void end() ;
 
     /** Return the current mode of the transaction - "read" or "write" */ 
-    //public ReadWrite transactionMode();
-    public default ReadWrite transactionMode() { throw new JenaTransactionException("Not implemented"); }
+    public ReadWrite transactionMode();
+    //public default ReadWrite transactionMode()  { throw new JenaTransactionException("Not implemented"); }
 
     /** Return the transaction type used in {@code begin(TxnType)}. */ 
     //public TxnMode transactionMode();

http://git-wip-us.apache.org/repos/asf/jena/blob/edab900a/jena-arq/src/main/java/org/apache/jena/sparql/core/TransactionalLock.java
----------------------------------------------------------------------
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 ad2ad3c..14d04b8 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
@@ -18,8 +18,7 @@
 
 package org.apache.jena.sparql.core;
 
-import java.util.Objects ;
-
+import org.apache.jena.atlas.lib.Lib;
 import org.apache.jena.query.ReadWrite ;
 import org.apache.jena.query.TxnType;
 import org.apache.jena.shared.Lock ;
@@ -35,29 +34,40 @@ import org.apache.jena.sparql.JenaTransactionException ;
  *  <pre>
  *      private final Transactional txn                     = TransactionalLock.createMRSW() ;
  *      {@literal @}Override public void begin(TxnType txnType)        { txn.begin(txnType) ; }
- *      {@literal @}Override public void begin(ReadWrite mode)         { txn.begin(mode) ; }
- *      {@literal @}Override public boolean promote()                  { return txn.promote() ; }
- *      {@literal @}Override public void commit()                      { txn.commit() ; }
- *      {@literal @}Override public void abort()                       { txn.abort() ; }
- *      {@literal @}Override public boolean isInTransaction()          { return txn.isInTransaction() ; }
+ *      {@literal @}Override public void begin()                       { txn.begin(); }
+ *      {@literal @}Override public void begin(TxnType txnType)        { txn.begin(txnType); }
+ *      {@literal @}Override public void begin(ReadWrite mode)         { txn.begin(mode); }
+ *      {@literal @}Override public boolean promote()                  { return txn.promote(); }
+ *      {@literal @}Override public void commit()                      { txn.commit(); }
+ *      {@literal @}Override public void abort()                       { txn.abort(); }
+ *      {@literal @}Override public boolean isInTransaction()          { return txn.isInTransaction(); }
  *      {@literal @}Override public void end()                         { txn.end(); }
- *      {@literal @}Override public boolean supportsTransactions()     { return true ; }
- *      {@literal @}Override public boolean supportsTransactionAbort() { return false ; }
+ *      {@literal @}Override public ReadWrite transactionMode()        { return txn.transactionMode(); }
+ *      {@literal @}Override public TxnType transactionType()          { return txn.transactionType(); }
+ *      {@literal @}Override public boolean supportsTransactions()     { return true; }
+ *      {@literal @}Override public boolean supportsTransactionAbort() { return true; }
  *   </pre>
  */ 
 public class TransactionalLock implements Transactional {
 /*
-       private final Transactional txn                     = TransactionalLock.createMRSW() ;
-       @Override public void begin(ReadWrite mode)         { txn.begin(mode) ; }
-       @Override public void commit()                      { txn.commit() ; }
-       @Override public void abort()                       { txn.abort() ; }
-       @Override public boolean isInTransaction()          { return txn.isInTransaction() ; }
-       @Override public void end()                         { txn.end(); }
-       @Override public boolean supportsTransactions()     { return true ; }
-       @Override public boolean supportsTransactionAbort() { return false ; }
+    private final Transactional txn                     = TransactionalLock.createMRSW() ;
+    @Override public void begin()                       { txn.begin(); }
+    @Override public void begin(TxnType txnType)        { txn.begin(txnType); }
+    @Override public void begin(ReadWrite mode)         { txn.begin(mode); }
+    @Override public boolean promote()                  { return txn.promote(); }
+    @Override public void commit()                      { txn.commit(); }
+    @Override public void abort()                       { txn.abort(); }
+    @Override public boolean isInTransaction()          { return txn.isInTransaction(); }
+    @Override public void end()                         { txn.end(); }
+    @Override public ReadWrite transactionMode()        { return txn.transactionMode(); }
+    @Override public TxnType transactionType()          { return txn.transactionType(); }
+    @Override public boolean supportsTransactions()     { return true; }
+    @Override public boolean supportsTransactionAbort() { return false; }
  */
     
-    private final ThreadLocal<ReadWrite> txnMode  = ThreadLocal.withInitial( ()->null ) ;
+    private ThreadLocal<Boolean> inTransaction = ThreadLocal.withInitial(() -> Boolean.FALSE);
+    private ThreadLocal<TxnType> txnType = ThreadLocal.withInitial(() -> null);
+    private ThreadLocal<ReadWrite> txnMode = ThreadLocal.withInitial(() -> null);
     private final Lock lock ;
 
     /** Create a Transactional using the given lock */
@@ -101,16 +111,24 @@ public class TransactionalLock implements Transactional {
         }
         ReadWrite readWrite = TxnType.convert(txnType);  
         boolean isRead = readWrite.equals(ReadWrite.READ) ;
-        lock.enterCriticalSection(isRead) ;
-        txnMode.set(readWrite) ;
+        lock.enterCriticalSection(isRead);
+        txnMode.set(readWrite);
+    }
+
+    @Override public ReadWrite transactionMode() {
+        return Lib.readThreadLocal(txnMode) ;
     }
 
+    @Override public TxnType transactionType() {
+        return Lib.readThreadLocal(txnType) ;
+    }
+    
     // Lock propmotion required (Ok for mutex) 
     
-//    @Override
-//    public boolean promote() { 
-//        return ??;
-//    }
+    @Override
+    public boolean promote() { 
+        return false;
+    }
 
     @Override
     public void commit() {
@@ -130,11 +148,7 @@ public class TransactionalLock implements Transactional {
     }
 
     public boolean isTransactionType(ReadWrite mode) {
-        return Objects.equals(mode, txnMode.get()) ;
-    }
-
-    private ReadWrite getTransactionType(ReadWrite mode) {
-        return txnMode.get() ;
+        return Lib.readThreadLocal(txnMode) == mode;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/jena/blob/edab900a/jena-arq/src/main/java/org/apache/jena/sparql/core/TransactionalMutex.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/TransactionalMutex.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/TransactionalMutex.java
deleted file mode 100644
index 4f722c7..0000000
--- a/jena-arq/src/main/java/org/apache/jena/sparql/core/TransactionalMutex.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * 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.query.ReadWrite ;
-import org.apache.jena.query.TxnType;
-import org.apache.jena.shared.Lock ;
-
-/** Transactional by mutual exclusion. */
-public class TransactionalMutex implements Transactional
-{
-    private final Lock lock;
-    private ThreadLocal<Boolean> isInTransaction = ThreadLocal.withInitial(()->false) ;
-    
-    private TransactionalMutex(Lock lock) {
-        this.lock = lock ;
-    }
-    
-    @Override
-    public void begin(ReadWrite readWrite) {
-        begin(TxnType.convert(readWrite));
-    }
-    
-    @Override
-    public void begin(TxnType txnType) {
-        lock.enterCriticalSection(false);       // Always take a write lock - i.e. exclusive.
-        isInTransaction.set(true); 
-    }
-
-    @Override
-    public boolean promote() {
-        return true;
-    }
-
-    @Override
-    public void commit() {
-        end() ;
-    }
-
-    @Override
-    public void abort() {
-        end() ;
-    }
-
-    @Override
-    public boolean isInTransaction() { 
-        return isInTransaction == null ? false : isInTransaction.get() ;
-    }
-    
-    @Override
-    public void end() {
-        if ( isInTransaction() ) {
-            isInTransaction.set(false);
-            lock.leaveCriticalSection();
-        }
-        isInTransaction.remove();
-        isInTransaction = null;
-    }
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/edab900a/jena-arq/src/main/java/org/apache/jena/sparql/core/TransactionalNotSupported.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/TransactionalNotSupported.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/TransactionalNotSupported.java
index 287aabd..f2ef1a0 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/core/TransactionalNotSupported.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/TransactionalNotSupported.java
@@ -39,15 +39,21 @@ public class TransactionalNotSupported implements Transactional
     // As an included component. 
     /*
     private final Transactional txn                     = new TransactionalNotSupported() ;
-    @Override public void begin(TxnType txnType)        { txn.begin(type) ; }
-    @Override public void begin(ReadWrite mode)         { txn.begin(mode) ; }
-    @Override public boolean promote()                  {  returntxn.promote() ; }
-    @Override public void commit()                      { txn.commit() ; }
-    @Override public void abort()                       { txn.abort() ; }
-    @Override public boolean isInTransaction()          { return txn.isInTransaction() ; }
+    @Override public void begin()                       { txn.begin(); }
+    @Override public void begin(TxnType txnType)        { txn.begin(txnType); }
+    @Override public void begin(ReadWrite mode)         { txn.begin(mode); }
+    @Override public boolean promote()                  { return txn.promote(); }
+    @Override public void commit()                      { txn.commit(); }
+    @Override public void abort()                       { txn.abort(); }
+    @Override public boolean isInTransaction()          { return txn.isInTransaction(); }
     @Override public void end()                         { txn.end(); }
-    @Override public boolean supportsTransactions()     { return true ; }
-    @Override public boolean supportsTransactionAbort() { return false ; }
+    @Override public ReadWrite transactionMode()        { return txn.transactionMode(); }
+    @Override public TxnType transactionType()          { return txn.transactionType(); }
+    
+     For DatasetGraphs:
+     
+    @Override public boolean supportsTransactions()     { return true; }
+    @Override public boolean supportsTransactionAbort() { return false; }
     */
     
     @Override
@@ -76,6 +82,13 @@ public class TransactionalNotSupported implements Transactional
     @Override
     public boolean isInTransaction()
     { return false ; }
+    
+    @Override public ReadWrite transactionMode()
+    { throw new UnsupportedOperationException("Transactional.transactionMode") ; }
+    
+    @Override public TxnType transactionType()
+    { throw new UnsupportedOperationException("Transactional.transactionType") ; }
+
 
     @Override
     public void end()
@@ -85,9 +98,6 @@ public class TransactionalNotSupported implements Transactional
         return false ;
     }
     
-    /** Declare whether {@link #abort} is supported.
-     *  This goes along with clearing up after exceptions inside application transaction code.
-     */
     public boolean supportsTransactionAbort() {
         return false;
     }

http://git-wip-us.apache.org/repos/asf/jena/blob/edab900a/jena-arq/src/main/java/org/apache/jena/sparql/core/TransactionalNotSupportedMixin.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/TransactionalNotSupportedMixin.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/TransactionalNotSupportedMixin.java
index 4a98044..98fd2e9 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/core/TransactionalNotSupportedMixin.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/TransactionalNotSupportedMixin.java
@@ -20,11 +20,9 @@ package org.apache.jena.sparql.core;
 
 import org.apache.jena.query.ReadWrite ;
 import org.apache.jena.query.TxnType;
-import org.apache.jena.shared.LockMutex ;
 
 /** Mixin interface for the "un-Transactional" interface.
- * Use with {@link LockMutex}.
- * @see TransactionalNotSupportedMixin
+ * @see TransactionalNotSupported
  */ 
 public interface TransactionalNotSupportedMixin extends Transactional
 {
@@ -52,12 +50,19 @@ public interface TransactionalNotSupportedMixin extends Transactional
     { throw new UnsupportedOperationException("Transactional.abort") ; }
 
     @Override
+    public default void end()
+    { throw new UnsupportedOperationException("Transactional.end") ; }
+
+    @Override
     public default boolean isInTransaction()
     { return false ; }
+    
+    @Override default public ReadWrite transactionMode()
+    { throw new UnsupportedOperationException("Transactional.transactionMode") ; }
+    
+    @Override default public TxnType transactionType()
+    { throw new UnsupportedOperationException("Transactional.transactionType") ; }
 
-    @Override
-    public default void end()
-    { throw new UnsupportedOperationException("Transactional.end") ; }
     
     public default boolean supportsTransactions()
     { return false ; }

http://git-wip-us.apache.org/repos/asf/jena/blob/edab900a/jena-arq/src/main/java/org/apache/jena/sparql/core/TransactionalNull.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/TransactionalNull.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/TransactionalNull.java
index 822b77d..4b5ecf8 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/core/TransactionalNull.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/TransactionalNull.java
@@ -28,25 +28,56 @@ import org.apache.jena.sparql.JenaTransactionException;
  * It does provide "abort".   
  */
 public class TransactionalNull implements Transactional {
+    /*
+    @Override public void begin()                       { txn.begin(); }
+    @Override public void begin(TxnType txnType)        { txn.begin(txnType); }
+    @Override public void begin(ReadWrite mode)         { txn.begin(mode); }
+    @Override public boolean promote()                  { return txn.promote(); }
+    @Override public void commit()                      { txn.commit(); }
+    @Override public void abort()                       { txn.abort(); }
+    @Override public boolean isInTransaction()          { return txn.isInTransaction(); }
+    @Override public void end()                         { txn.end(); }
+    @Override public ReadWrite transactionMode()        { return txn.transactionMode(); }
+    @Override public TxnType transactionType()          { return txn.transactionType(); }
+ 
+     For DatasetGraphs:
+     
+    @Override public boolean supportsTransactions()     { return true; }
+    @Override public boolean supportsTransactionAbort() { return false; }
+    */
+    
     public static TransactionalNull create() { return new TransactionalNull(); }
     
     private ThreadLocal<Boolean> inTransaction = ThreadLocal.withInitial(() -> Boolean.FALSE);
+    private ThreadLocal<TxnType> txnType = ThreadLocal.withInitial(() -> null);
+    private ThreadLocal<ReadWrite> txnMode = ThreadLocal.withInitial(() -> null);
 
+    @Override
+    public ReadWrite transactionMode() {
+        return txnMode.get();
+    }
+
+    @Override
+    public void begin(ReadWrite readWrite) {
+        begin(TxnType.convert(readWrite));
+    }
 
     @Override
     public void begin(TxnType type) {
         if ( inTransaction.get() )
             throw new JenaTransactionException("Already in transaction"); 
         inTransaction.set(true);
+        txnType.set(type);
+        txnMode.set(TxnType.initial(type));
     }
 
     @Override
-    public void begin(ReadWrite readWrite) {
-        begin(TxnType.convert(readWrite));
+    public boolean promote() {
+        if ( ! inTransaction.get() )
+            throw new JenaTransactionException("Not in transaction"); 
+        txnMode.set(ReadWrite.WRITE);
+        return true;
     }
-
-    @Override
-    public boolean promote() { return true; } 
     
     @Override
     public void commit() {
@@ -69,14 +100,16 @@ public class TransactionalNull implements Transactional {
 
     @Override
     public void end() {
-        inTransaction.set(false);
+        clearup();
     }
 
-//    @Override
-//    public boolean promote() {
-//        return true;
-//    }
-
+    private void clearup() {
+        inTransaction.set(false);
+        inTransaction.remove();
+        txnType.set(null);
+        txnType.remove();
+    }
+    
     public void remove() {
         inTransaction.remove();
     }

http://git-wip-us.apache.org/repos/asf/jena/blob/edab900a/jena-arq/src/main/java/org/apache/jena/sparql/core/mem/DatasetGraphInMemory.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/mem/DatasetGraphInMemory.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/mem/DatasetGraphInMemory.java
index 1905d05..44dd8db 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/core/mem/DatasetGraphInMemory.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/mem/DatasetGraphInMemory.java
@@ -101,7 +101,7 @@ public class DatasetGraphInMemory extends DatasetGraphTriplesQuads implements Tr
         return transactionType.get();
     }
 
-    private void transactionType(final ReadWrite readWrite) {
+    private void transactionMode(final ReadWrite readWrite) {
         transactionMode.set(readWrite);
     }
 
@@ -139,24 +139,21 @@ public class DatasetGraphInMemory extends DatasetGraphTriplesQuads implements Tr
     public boolean supportsTransactionAbort()   { return true; }
 
     @Override
+    public void begin(final ReadWrite readWrite) {
+        begin(TxnType.convert(readWrite));
+    }
+
+    @Override
     public void begin(TxnType txnType) {
         if (isInTransaction()) 
             throw new JenaTransactionException("Transactions cannot be nested!");
         transactionType.set(txnType);
-        ReadWrite initial = txnType.equals(TxnType.WRITE) ? WRITE : READ;
-        _begin(initial);
+        _begin(txnType, TxnType.initial(txnType));
     }
     
-    @Override
-    public void begin(final ReadWrite readWrite) {
-        if (isInTransaction()) 
-            throw new JenaTransactionException("Transactions cannot be nested!");
-        transactionType.set(TxnType.convert(readWrite));
-        _begin(readWrite) ;
-    }
-
-    private void _begin(ReadWrite readWrite) {
-        startTransaction(readWrite);
+    private void _begin(TxnType txnType, ReadWrite readWrite) {
+        // Takes transactionLock
+        startTransaction(txnType, readWrite);
         withLock(systemLock, () ->{
             quadsIndex().begin(readWrite);
             defaultGraph().begin(readWrite);
@@ -165,9 +162,10 @@ public class DatasetGraphInMemory extends DatasetGraphTriplesQuads implements Tr
     }
     
     /** Called transaction start code at most once per transaction. */ 
-    private void startTransaction(ReadWrite mode) {
+    private void startTransaction(TxnType txnType, ReadWrite mode) {
         transactionLock.enterCriticalSection(mode.equals(ReadWrite.READ)); // get the dataset write lock, if needed.
-        transactionType(mode);
+        transactionType.set(txnType);
+        transactionMode(mode);
         isInTransaction(true);
     }
 
@@ -193,7 +191,7 @@ public class DatasetGraphInMemory extends DatasetGraphTriplesQuads implements Tr
             case WRITE :
                 return true;
             case READ :
-                return false ;
+                throw new JenaTransactionException("Tried to promote READ transaction");
             case READ_COMMITTED_PROMOTE :
                 readCommitted = true;
             case READ_PROMOTE :
@@ -204,9 +202,6 @@ public class DatasetGraphInMemory extends DatasetGraphTriplesQuads implements Tr
                 throw new NullPointerException();
         }
         
-        // XXX Check "mutate".
-        mutate(null, null);
-        
         try {
             _promote(readCommitted);
             return true;
@@ -215,10 +210,6 @@ public class DatasetGraphInMemory extends DatasetGraphTriplesQuads implements Tr
         }
     }
     
-    /*private*/public/*for development*/ static boolean promotion               = false ;
-
-    /*private*/public/*for development*/ static boolean readCommittedPromotion  = true ;
-
     private void _promote(boolean readCommited) {
         //System.err.printf("Promote: version=%d generation=%d\n", version.get() , generation.get()) ;
         
@@ -240,8 +231,8 @@ public class DatasetGraphInMemory extends DatasetGraphTriplesQuads implements Tr
                 throw new JenaTransactionException("Concurrent writer changed the dataset : can't promote") ;
             }
         // We have the lock and we have promoted!
-        transactionType(WRITE);
-        _begin(WRITE) ;
+        transactionMode(WRITE);
+        _begin(transactionType(), ReadWrite.WRITE) ;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/jena/blob/edab900a/jena-arq/src/main/java/org/apache/jena/system/TxnCounter.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/system/TxnCounter.java b/jena-arq/src/main/java/org/apache/jena/system/TxnCounter.java
index 6e93dcd..e641d55 100644
--- a/jena-arq/src/main/java/org/apache/jena/system/TxnCounter.java
+++ b/jena-arq/src/main/java/org/apache/jena/system/TxnCounter.java
@@ -18,15 +18,14 @@
 
 package org.apache.jena.system;
 
-import static org.apache.jena.query.ReadWrite.WRITE ;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.atomic.AtomicLong;
 
-import java.util.concurrent.Semaphore ;
-import java.util.concurrent.atomic.AtomicLong ;
-
-import org.apache.jena.atlas.lib.Lib ;
-import org.apache.jena.query.ReadWrite ;
-import org.apache.jena.sparql.JenaTransactionException ;
-import org.apache.jena.sparql.core.Transactional ;
+import org.apache.jena.atlas.lib.Lib;
+import org.apache.jena.query.ReadWrite;
+import org.apache.jena.query.TxnType;
+import org.apache.jena.sparql.JenaTransactionException;
+import org.apache.jena.sparql.core.Transactional;
 
 /** A MR+SW transactional Counter */ 
 public class TxnCounter implements Transactional {
@@ -35,148 +34,198 @@ public class TxnCounter implements Transactional {
     
     // Semaphore to implement "Single Active Writer" - independent of readers
     // This is not reentrant.
-    private Semaphore writersWaiting = new Semaphore(1, true) ;
+    private Semaphore writersWaiting = new Semaphore(1, true);
     
     private void releaseWriterLock() {
-        int x = writersWaiting.availablePermits() ;
+        int x = writersWaiting.availablePermits();
         if ( x != 0 )
-            throw new JenaTransactionException("TransactionCoordinator: Probably mismatch of enable/disableWriter calls") ;
-        writersWaiting.release() ;
+            throw new JenaTransactionException("TransactionCoordinator: Probably mismatch of enable/disableWriter calls");
+        writersWaiting.release();
     }
     
     private boolean acquireWriterLock(boolean canBlock) {
         if ( ! canBlock )
-            return writersWaiting.tryAcquire() ;
+            return writersWaiting.tryAcquire();
         try { 
-            writersWaiting.acquire() ; 
+            writersWaiting.acquire(); 
             return true;
-        } catch (InterruptedException e) { throw new JenaTransactionException(e) ; }
+        } catch (InterruptedException e) { throw new JenaTransactionException(e); }
     }
     // ---- TransactionCoordinator.
     
     // Transaction state.
     static class IntegerState {
-        long txnValue ;
-        public IntegerState(long v) { this.txnValue = v ; }
+        long txnValue;
+        public IntegerState(long v) { this.txnValue = v; }
     }
     
     // Global state - the exterally visible value and the starting point for any
     // transaction. This is set to a new value when a write transaction commits.
     
-    private final AtomicLong value = new AtomicLong(-1712) ; 
+    private final AtomicLong value = new AtomicLong(-1712); 
+    private final AtomicLong epoch = new AtomicLong(1);
 
     // ---- Transaction state.
     
     // The per-transaction state (inside a transaction). Null outside a transaction
     // cleared by commit or abort in a write transaction.
-    private ThreadLocal<IntegerState> txnValue = ThreadLocal.withInitial(()->null) ;
+    private ThreadLocal<IntegerState> transactionValue = ThreadLocal.withInitial(()->null);
     // The kind of transaction.
-    private ThreadLocal<ReadWrite>    txnMode  = ThreadLocal.withInitial(()->null) ;
+    private ThreadLocal<ReadWrite>    transactionMode  = ThreadLocal.withInitial(()->null);
+    private ThreadLocal<TxnType>      transactionType  = ThreadLocal.withInitial(()->null);
+    private ThreadLocal<Long>         transactionEpoch         = ThreadLocal.withInitial(()->null);
     
-    // Syncrhonization for making changes.  
-    private Object txnLifecycleLock   = new Object() ; 
+    // Synchronization for making changes.  
+    private Object txnLifecycleLock   = new Object(); 
     
     public TxnCounter(long x) {
-        value.set(x) ;
+        value.set(x);
     }
     
     @Override
     public void begin(ReadWrite readWrite) {
-        begin(readWrite, true) ;
+        begin(TxnType.convert(readWrite));
+    }
+    
+    @Override
+    public void begin(TxnType txnType) {
+        begin(txnType, true);
     }
     
-    public void begin(ReadWrite readWrite, boolean canBlock) {
+    public void begin(TxnType txnType, boolean canBlock) {
         // Ensure a single writer. 
         // (Readers never block at this point.)
-        if ( readWrite == WRITE ) {
+        if ( txnType == TxnType.WRITE ) {
             // Writers take a WRITE permit from the semaphore to ensure there
             // is at most one active writer, else the attempt to start the
             // transaction blocks.
             // Released by in commit/abort.
-            acquireWriterLock(canBlock) ;
+            acquireWriterLock(canBlock);
         }
         // at this point, 
         // One writer or one of many readers. 
         
         synchronized(txnLifecycleLock) {
-            if ( txnMode.get() != null )
-                throw new JenaTransactionException("Already in a transaction") ;
-            IntegerState state = new IntegerState(value.get()) ;
-            txnValue.set(state) ;
-            txnMode.set(readWrite);
+            if ( transactionMode.get() != null )
+                throw new JenaTransactionException("Already in a transaction");
+            long thisEpoch = epoch.incrementAndGet(); 
+            transactionEpoch.set(thisEpoch) ;
+            IntegerState state = new IntegerState(value.get());
+            transactionValue.set(state);
+            transactionMode.set(TxnType.initial(txnType));
+            transactionType.set(txnType);
         }
     }
 
     @Override
+    public boolean promote() {
+        checkTxn();
+        if ( transactionMode.get() == ReadWrite.WRITE )
+            return true;
+        TxnType txnType = transactionType.get();
+        if ( txnType == TxnType.READ )
+            throw new JenaTransactionException("Attempt to promote a READ transsction");
+        if ( txnType == TxnType.READ_COMMITTED_PROMOTE ) {
+            acquireWriterLock(true);
+            transactionMode.set(ReadWrite.WRITE);
+            IntegerState state = new IntegerState(value.get());
+            transactionValue.set(state);
+            return true;
+        }
+        // READ no committed.
+        acquireWriterLock(true);
+        synchronized(txnLifecycleLock) {
+            long nowEpoch = epoch.get();
+            if ( transactionEpoch.get() != nowEpoch ) {
+                // Can't.
+                releaseWriterLock();
+                return false;
+            }
+            // Can.
+            transactionMode.set(ReadWrite.WRITE);
+        }
+        return true;
+    }
+
+    
+    @Override
     public void commit() {
         checkTxn(); 
         if ( isWriteTxn() ) {
             // Set global.
-            value.set(getDataState().txnValue) ;
-            txnValue.set(null); 
+            value.set(getDataState().txnValue);
+            transactionValue.set(null);
             releaseWriterLock();
         }
-        endOnce() ;
+        endOnce();
     }
 
     @Override
     public void abort() {
         checkTxn(); 
         if ( isWriteTxn() ) {
-            txnValue.set(null); 
+            transactionValue.set(null); 
             releaseWriterLock();
         }
-        endOnce() ;
+        endOnce();
     }
 
     @Override
     public boolean isInTransaction() {
-        ReadWrite mode = txnMode.get() ;
-        if ( mode == null )
-            // Remove it - avoid holding the memory.
-            txnMode.remove();
-        return mode != null ;
+        return Lib.readThreadLocal(transactionMode) != null;
+    }
+
+    @Override
+    public ReadWrite transactionMode() {
+        return Lib.readThreadLocal(transactionMode);
+    }
+    
+    @Override
+    public TxnType transactionType() {
+        return Lib.readThreadLocal(transactionType);
     }
 
     @Override
     public void end() {
         if ( ! isInTransaction() ) 
-            return ;
-        if ( isWriteTxn() && txnValue.get() != null )
-            throw new JenaTransactionException("No commit or abort before end for a write transaction") ;
-        endOnce() ;
+            return;
+        if ( isWriteTxn() && transactionValue.get() != null )
+            throw new JenaTransactionException("No commit or abort before end for a write transaction");
+        endOnce();
     }
 
     private void endOnce() {
         if ( isActiveTxn() ) {
             synchronized(txnLifecycleLock) {
-                txnValue.remove();
-                txnMode.remove();
+                transactionValue.remove();
+                transactionType.remove();
+                transactionMode.remove();
+                transactionEpoch.remove();
             }
         }
     }
 
     /** Increment the value inside a write transaction */ 
     public void inc() {
-        checkWriteTxn() ;
-        IntegerState ts = getDataState() ;
-        ts.txnValue++ ;
+        checkWriteTxn();
+        IntegerState ts = getDataState();
+        ts.txnValue++;
     }
     
     /** Decrement the value inside a write transaction */ 
     public void dec() {
-        checkWriteTxn() ;
-        IntegerState ts = getDataState() ;
-        ts.txnValue-- ;
+        checkWriteTxn();
+        IntegerState ts = getDataState();
+        ts.txnValue--;
     }
 
     /** Set the value inside a write transaction, return the old value*/ 
     public long set(long x) {
-        checkWriteTxn() ;
-        IntegerState ts = getDataState() ;
-        long v = ts.txnValue ;
-        ts.txnValue = x ;
-        return v ;
+        checkWriteTxn();
+        IntegerState ts = getDataState();
+        long v = ts.txnValue;
+        ts.txnValue = x;
+        return v;
     }
     
 
@@ -186,7 +235,7 @@ public class TxnCounter implements Transactional {
      */
     public long read() {
         checkTxn();
-        return getDataState().txnValue ;
+        return getDataState().txnValue;
     }
 
     /** Return the current value.
@@ -197,14 +246,14 @@ public class TxnCounter implements Transactional {
      */
     public long get() {
         if ( isActiveTxn() )
-            return getDataState().txnValue ;
+            return getDataState().txnValue;
         else
-            return value.get() ;
+            return value.get();
     }
 
     /** Read the current global state (that is, the last committed value) outside a transaction. */
     public long value() {
-        return value.get() ;
+        return value.get();
     }
 
     // These two operations not clear the thread local if we are not in a transaction.
@@ -213,28 +262,28 @@ public class TxnCounter implements Transactional {
 
     /** Is this a write transaction? Should be called inside a transaction. */
     private boolean isWriteTxn() {
-        ReadWrite rw = txnMode.get() ;
+        ReadWrite rw = transactionMode.get();
         if ( rw == null )
-            throw new JenaTransactionException(Lib.classShortName(this.getClass())+".isWriteTxn called outside a transaction") ;
-        return txnMode.get() == ReadWrite.WRITE ;
+            throw new JenaTransactionException(Lib.classShortName(this.getClass())+".isWriteTxn called outside a transaction");
+        return transactionMode.get() == ReadWrite.WRITE;
     }
 
     private boolean isActiveTxn() {
-        ReadWrite rw = txnMode.get() ;
-        return rw != null ;
+        ReadWrite rw = transactionMode.get();
+        return rw != null;
     }
 
     private IntegerState getDataState() {
-        return txnValue.get() ;
+        return transactionValue.get();
     }
 
     private void checkWriteTxn() {
         if ( ! isWriteTxn() )
-            throw new JenaTransactionException("Not in a write transaction") ;
+            throw new JenaTransactionException("Not in a write transaction");
     }
 
     private void checkTxn() {
         if ( ! isActiveTxn() )
-            throw new JenaTransactionException("Not in a transaction") ;
+            throw new JenaTransactionException("Not in a transaction");
     }
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/edab900a/jena-arq/src/test/java/org/apache/jena/sparql/core/TS_Core.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/core/TS_Core.java b/jena-arq/src/test/java/org/apache/jena/sparql/core/TS_Core.java
index c851ee8..800e428 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/core/TS_Core.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/core/TS_Core.java
@@ -34,7 +34,6 @@ import org.junit.runners.Suite ;
     , TestDatasetGraphViewGraphs.class
     , TestGraphView.class
     , TestDatasetMonitor.class
-    , TestDatasetGraphWithLock.class
     
     , TestDatasetGraphBaseFind_General.class
     , TestDatasetGraphBaseFind_General.class

http://git-wip-us.apache.org/repos/asf/jena/blob/edab900a/jena-arq/src/test/java/org/apache/jena/sparql/core/TestDatasetGraphWithLock.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/core/TestDatasetGraphWithLock.java b/jena-arq/src/test/java/org/apache/jena/sparql/core/TestDatasetGraphWithLock.java
deleted file mode 100644
index 0a3b0a6..0000000
--- a/jena-arq/src/test/java/org/apache/jena/sparql/core/TestDatasetGraphWithLock.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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.query.Dataset ;
-import org.apache.jena.query.DatasetFactory ;
-
-/** Non-transaction part of DatasetGraphWithLock testing */
-public class TestDatasetGraphWithLock extends AbstractTestDataset {
-    @Override
-    protected Dataset createDataset() {
-        return DatasetFactory.wrap(new DatasetGraphWithLock(DatasetGraphFactory.create()));
-    }
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/edab900a/jena-arq/src/test/java/org/apache/jena/sparql/core/mem/TestDatasetGraphInMemoryLock.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/core/mem/TestDatasetGraphInMemoryLock.java b/jena-arq/src/test/java/org/apache/jena/sparql/core/mem/TestDatasetGraphInMemoryLock.java
index 27daed4..721dbb9 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/core/mem/TestDatasetGraphInMemoryLock.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/core/mem/TestDatasetGraphInMemoryLock.java
@@ -20,9 +20,9 @@ package org.apache.jena.sparql.core.mem;
 
 import org.apache.jena.query.Dataset ;
 import org.apache.jena.query.DatasetFactory ;
-import org.apache.jena.sparql.core.TestDatasetGraphWithLock ;
+import org.apache.jena.sparql.core.AbstractTestDataset;
 
-public class TestDatasetGraphInMemoryLock extends TestDatasetGraphWithLock {
+public class TestDatasetGraphInMemoryLock extends AbstractTestDataset {
 	@Override
 	protected Dataset createDataset() {
 		return DatasetFactory.createTxnMem();

http://git-wip-us.apache.org/repos/asf/jena/blob/edab900a/jena-arq/src/test/java/org/apache/jena/sparql/transaction/AbstractTestTransPromote.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/transaction/AbstractTestTransPromote.java b/jena-arq/src/test/java/org/apache/jena/sparql/transaction/AbstractTestTransPromote.java
index 3378a87..9134668 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/transaction/AbstractTestTransPromote.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/transaction/AbstractTestTransPromote.java
@@ -18,7 +18,7 @@
 
 package org.apache.jena.sparql.transaction ;
 
-import static org.junit.Assert.assertEquals ;
+import static org.junit.Assert.* ;
 import static org.junit.Assert.fail ;
 
 import java.util.concurrent.* ;
@@ -172,7 +172,7 @@ public abstract class AbstractTestTransPromote {
         assertCount(0, dsg) ;
     }
 
-    // XXX LOCK UP
+    // LOCK UP
     //@Test public void promote_snapshot_06()         { run_06(TxnMode.READ_PROMOTE) ; }
     //@Test public void promote_readCommitted_06()    { run_06(TxnMode.READ_COMMITTED_PROMOTE) ; }
     
@@ -387,4 +387,51 @@ public abstract class AbstractTestTransPromote {
                 throw e ;
         } catch (InterruptedException | ExecutionException e1) { throw new RuntimeException(e1) ; }
     }
+    
+    // This would locks up because of a WRITE-WRITE deadly embrace.
+    //  @Test(expected=JenaTransactionException.class)
+    //  public void promote11() { test2(TxnMode.WRITE); }
+      
+    @Test
+    public void promote_thread_writer_1() { test_thread_writer(TxnType.READ_COMMITTED_PROMOTE); }
+      
+    @Test(expected=JenaTransactionException.class)
+    public void promote_thread_writer_2() { test_thread_writer(TxnType.READ_PROMOTE); }
+    
+    private void test_thread_writer(TxnType txnType) {
+        DatasetGraph dsg = create();
+        ThreadAction a = ThreadTxn.threadTxnWrite(dsg, ()->dsg.add(q1));
+        dsg.begin(txnType);
+        assertEquals(txnType, dsg.transactionType());
+        a.run();
+        dsg.add(q2);
+        // TDB1 does not keep the the original TxnType.
+        assertEquals(txnType, dsg.transactionType());
+        assertEquals(ReadWrite.WRITE, dsg.transactionMode());
+        dsg.commit();
+        dsg.end();
+    }
+    
+    // With explicit "promote()"
+    private void test_promote_thread_writer(TxnType txnType) {
+        DatasetGraph dsg = create();
+        ThreadAction a = ThreadTxn.threadTxnWrite(dsg, ()->dsg.add(q1));
+        dsg.begin(txnType);
+        assertEquals(txnType, dsg.transactionType());
+        a.run();
+        
+        boolean b = dsg.promote();
+        
+        if ( txnType == TxnType.READ_PROMOTE )
+            assertFalse(b);
+        if ( txnType == TxnType.READ_COMMITTED_PROMOTE )
+            assertTrue(b);
+
+        dsg.add(q2);
+        assertEquals(txnType, dsg.transactionType());
+        assertEquals(ReadWrite.WRITE, dsg.transactionMode());
+        dsg.commit();
+        dsg.end();
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/edab900a/jena-arq/src/test/java/org/apache/jena/sparql/transaction/AbstractTestTransactionLifecycle.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/transaction/AbstractTestTransactionLifecycle.java b/jena-arq/src/test/java/org/apache/jena/sparql/transaction/AbstractTestTransactionLifecycle.java
index 32b8ca3..c1043b8 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/transaction/AbstractTestTransactionLifecycle.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/transaction/AbstractTestTransactionLifecycle.java
@@ -18,8 +18,9 @@
 
 package org.apache.jena.sparql.transaction;
 
-import static org.apache.jena.query.ReadWrite.READ ;
-import static org.apache.jena.query.ReadWrite.WRITE ;
+import static org.apache.jena.query.TxnType.READ ;
+import static org.apache.jena.query.TxnType.WRITE ;
+import static org.junit.Assume.assumeTrue;
 
 import java.util.ArrayList ;
 import java.util.List ;
@@ -30,15 +31,20 @@ import org.apache.jena.atlas.junit.BaseTest ;
 import org.apache.jena.atlas.lib.Lib ;
 import org.apache.jena.query.Dataset ;
 import org.apache.jena.query.ReadWrite ;
+import org.apache.jena.query.TxnType;
+import org.apache.jena.shared.JenaException;
 import org.apache.jena.sparql.JenaTransactionException ;
-import static org.junit.Assume.* ;
 import org.junit.Test ;
 
+/**
+ * Dataset transaction lifecycle. 
+ */
 public abstract class AbstractTestTransactionLifecycle extends BaseTest
 {
     protected abstract Dataset create() ;
     
-    protected boolean supportsAbort() { return true ; } 
+    protected boolean supportsAbort()   { return true ; } 
+    protected boolean supportsPromote() { return true ; }
 
     @Test
     public void transaction_00() {
@@ -49,7 +55,7 @@ public abstract class AbstractTestTransactionLifecycle extends BaseTest
     @Test
     public void transaction_r01() {
         Dataset ds = create() ;
-        ds.begin(ReadWrite.READ) ;
+        ds.begin(TxnType.READ) ;
         assertTrue(ds.isInTransaction()) ;
         ds.end() ;
         assertFalse(ds.isInTransaction()) ;
@@ -58,7 +64,7 @@ public abstract class AbstractTestTransactionLifecycle extends BaseTest
     @Test
     public void transaction_r02() {
         Dataset ds = create() ;
-        ds.begin(ReadWrite.READ) ;
+        ds.begin(TxnType.READ) ;
         assertTrue(ds.isInTransaction()) ;
         ds.commit() ;
         assertFalse(ds.isInTransaction()) ;
@@ -69,7 +75,7 @@ public abstract class AbstractTestTransactionLifecycle extends BaseTest
     @Test
     public void transaction_r03() {
         Dataset ds = create() ;
-        ds.begin(ReadWrite.READ) ;
+        ds.begin(TxnType.READ) ;
         assertTrue(ds.isInTransaction()) ;
         ds.abort() ;
         assertFalse(ds.isInTransaction()) ;
@@ -78,8 +84,26 @@ public abstract class AbstractTestTransactionLifecycle extends BaseTest
     }
 
     @Test
+    public void transaction_r04() {
+        Dataset ds = create() ;
+        ds.begin(ReadWrite.READ) ;
+        assertTrue(ds.isInTransaction()) ;
+        ds.end() ;
+        assertFalse(ds.isInTransaction()) ;
+    }
+
+    @Test
     public void transaction_w01() {
         Dataset ds = create() ;
+        ds.begin(TxnType.WRITE) ;
+        assertTrue(ds.isInTransaction()) ;
+        ds.commit() ;
+        assertFalse(ds.isInTransaction()) ;
+    }
+    
+    @Test
+    public void transaction_w02() {
+        Dataset ds = create() ;
         ds.begin(ReadWrite.WRITE) ;
         assertTrue(ds.isInTransaction()) ;
         ds.commit() ;
@@ -87,19 +111,19 @@ public abstract class AbstractTestTransactionLifecycle extends BaseTest
     }
 
     @Test
-    public void transaction_w02() {
+    public void transaction_w03() {
         assumeTrue(supportsAbort()) ;
         Dataset ds = create() ;
-        ds.begin(ReadWrite.WRITE) ;
+        ds.begin(TxnType.WRITE) ;
         assertTrue(ds.isInTransaction()) ;
         ds.abort() ;
         assertFalse(ds.isInTransaction()) ;
     }
 
     @Test
-    public void transaction_w03() {
+    public void transaction_w04() {
         Dataset ds = create() ;
-        ds.begin(ReadWrite.WRITE) ;
+        ds.begin(TxnType.WRITE) ;
         assertTrue(ds.isInTransaction()) ;
         ds.commit() ;
         assertFalse(ds.isInTransaction()) ;
@@ -108,10 +132,10 @@ public abstract class AbstractTestTransactionLifecycle extends BaseTest
     }
 
     @Test
-    public void transaction_w04() {
+    public void transaction_w05() {
         assumeTrue(supportsAbort()) ;
         Dataset ds = create() ;
-        ds.begin(ReadWrite.WRITE) ;
+        ds.begin(TxnType.WRITE) ;
         assertTrue(ds.isInTransaction()) ;
         ds.abort() ;
         assertFalse(ds.isInTransaction()) ;
@@ -120,21 +144,110 @@ public abstract class AbstractTestTransactionLifecycle extends BaseTest
     }
 
     @Test
-    public void transaction_w05() {
+    public void transaction_w06() {
         assumeTrue(supportsAbort()) ;
         // .end is not necessary
         Dataset ds = create() ;
-        ds.begin(ReadWrite.WRITE) ;
+        ds.begin(TxnType.WRITE) ;
         assertTrue(ds.isInTransaction()) ;
         ds.abort() ;
         assertFalse(ds.isInTransaction()) ;
 
-        ds.begin(ReadWrite.WRITE) ;
+        ds.begin(TxnType.WRITE) ;
         assertTrue(ds.isInTransaction()) ;
         ds.abort() ;
         assertFalse(ds.isInTransaction()) ;
     }
 
+//    TxnType.READ_PROMOTE
+//    TxnType.READ_COMMITTED_PROMOTE
+    
+    @Test
+    public void transaction_p01() {
+        assumeTrue(supportsPromote()) ;
+        Dataset ds = create() ;
+        ds.begin(TxnType.READ_PROMOTE) ;
+        assertEquals(TxnType.READ_PROMOTE, ds.transactionType());
+        assertTrue(ds.isInTransaction()) ;
+        assertEquals(ReadWrite.READ, ds.transactionMode());
+        ds.promote();
+        assertEquals(ReadWrite.WRITE, ds.transactionMode());
+        ds.commit();
+        ds.end();
+    }
+    
+    @Test
+    public void transaction_p02() {
+        assumeTrue(supportsPromote()) ;
+        Dataset ds = create() ;
+        ds.begin(TxnType.READ_COMMITTED_PROMOTE) ;
+        assertEquals(TxnType.READ_COMMITTED_PROMOTE, ds.transactionType());
+        assertTrue(ds.isInTransaction()) ;
+        assertEquals(ReadWrite.READ, ds.transactionMode());
+        boolean b = ds.promote();
+        assertTrue(b) ;
+        assertEquals(ReadWrite.WRITE, ds.transactionMode());
+        ds.commit();
+        ds.end();
+    }
+    
+    @Test
+    public void transaction_p03() {
+        assumeTrue(supportsPromote()) ;
+        Dataset ds = create() ;
+        ds.begin(TxnType.READ_PROMOTE) ;
+        assertTrue(ds.isInTransaction()) ;
+        assertEquals(ReadWrite.READ, ds.transactionMode());
+        boolean b = ds.promote();
+        assertTrue(b) ;
+        assertEquals(ReadWrite.WRITE, ds.transactionMode());
+        ds.abort();
+        ds.end();
+        assertFalse(ds.isInTransaction()) ;
+    }
+
+    @Test
+    public void transaction_p04() {
+        assumeTrue(supportsPromote()) ;
+        Dataset ds = create() ;
+        ds.begin(TxnType.READ_COMMITTED_PROMOTE) ;
+        assertTrue(ds.isInTransaction()) ;
+        assertEquals(ReadWrite.READ, ds.transactionMode());
+        boolean b = ds.promote();
+        assertTrue(b) ;
+        assertEquals(ReadWrite.WRITE, ds.transactionMode());
+        ds.abort();
+        ds.end();
+        assertFalse(ds.isInTransaction()) ;
+    }
+
+    @Test
+    public void transaction_p05() {
+        assumeTrue(supportsPromote()) ;
+        Dataset ds = create() ;
+        ds.begin(TxnType.READ_COMMITTED_PROMOTE) ;
+        assertTrue(ds.isInTransaction()) ;
+        boolean b1 = ds.promote();
+        assertTrue(b1) ;
+        boolean b2 = ds.promote();
+        assertTrue(b2) ;
+        ds.commit();
+        ds.end();
+    }
+    
+    @Test(expected=JenaException.class)
+    public void transaction_err_read_promote() {
+        assumeTrue(supportsPromote()) ;
+        Dataset ds = create() ;
+        ds.begin(TxnType.READ) ;
+        boolean b = ds.promote();   // Illgeal.
+        assertFalse(b) ;
+        ds.commit();
+        ds.end();
+    }
+    
+    // XXX READ_PROMOTE -> update -> fail promote/boolean. 
+
     // Patterns.
     @Test
     public void transaction_pattern_01() {
@@ -253,7 +366,7 @@ public abstract class AbstractTestTransactionLifecycle extends BaseTest
     public void transaction_err_12()    { testAbortCommit(WRITE) ; }
 
     private void read1(Dataset ds) {
-        ds.begin(ReadWrite.READ) ;
+        ds.begin(TxnType.READ) ;
         assertTrue(ds.isInTransaction()) ;
         ds.commit() ;
         assertFalse(ds.isInTransaction()) ;
@@ -261,14 +374,14 @@ public abstract class AbstractTestTransactionLifecycle extends BaseTest
     }
 
     private void read2(Dataset ds) {
-        ds.begin(ReadWrite.READ) ;
+        ds.begin(TxnType.READ) ;
         assertTrue(ds.isInTransaction()) ;
         ds.end() ;
         assertFalse(ds.isInTransaction()) ;
     }
 
     private void write(Dataset ds) {
-        ds.begin(ReadWrite.WRITE) ;
+        ds.begin(TxnType.WRITE) ;
         assertTrue(ds.isInTransaction()) ;
         ds.commit() ;
         assertFalse(ds.isInTransaction()) ;
@@ -281,67 +394,67 @@ public abstract class AbstractTestTransactionLifecycle extends BaseTest
     
     // Error conditions that should be detected.
 
-    private void testBeginBegin(ReadWrite mode1, ReadWrite mode2) {
+    private void testBeginBegin(TxnType txnType1, TxnType txnType2) {
         Dataset ds = create() ;
-        ds.begin(mode1) ;
+        ds.begin(txnType1) ;
         try {
-            ds.begin(mode2) ;
-            fail("Expected transaction exception - begin-begin (" + mode1 + ", " + mode2 + ")") ;
+            ds.begin(txnType2) ;
+            fail("Expected transaction exception - begin-begin (" + txnType1 + ", " + txnType2 + ")") ;
         }
         catch (JenaTransactionException ex) {
             safeEnd(ds) ;
         }
     }
     
-    private void testCommitCommit(ReadWrite mode) {
+    private void testCommitCommit(TxnType txnType) {
         Dataset ds = create() ;
-        ds.begin(mode) ;
+        ds.begin(txnType) ;
         ds.commit() ;
         try {
             ds.commit() ;
-            fail("Expected transaction exception - commit-commit(" + mode + ")") ;
+            fail("Expected transaction exception - commit-commit(" + txnType + ")") ;
         }
         catch (JenaTransactionException ex) {
             safeEnd(ds) ;
         }
     }
 
-    private void testCommitAbort(ReadWrite mode) {
+    private void testCommitAbort(TxnType txnType) {
         assumeTrue(supportsAbort()) ;
         Dataset ds = create() ;
-        ds.begin(mode) ;
+        ds.begin(txnType) ;
         ds.commit() ;
         try {
             ds.abort() ;
-            fail("Expected transaction exception - commit-abort(" + mode + ")") ;
+            fail("Expected transaction exception - commit-abort(" + txnType + ")") ;
         }
         catch (JenaTransactionException ex) {
             safeEnd(ds) ;
         }
     }
 
-    private void testAbortAbort(ReadWrite mode) {
+    private void testAbortAbort(TxnType txnType) {
         assumeTrue(supportsAbort()) ;
         Dataset ds = create() ;
-        ds.begin(mode) ;
+        ds.begin(txnType) ;
         ds.abort() ;
         try {
             ds.abort() ;
-            fail("Expected transaction exception - abort-abort(" + mode + ")") ;
+            fail("Expected transaction exception - abort-abort(" + txnType + ")") ;
         }
         catch (JenaTransactionException ex) {
             ds.end() ;
         }
     }
 
-    private void testAbortCommit(ReadWrite mode) {
+    private void testAbortCommit(TxnType txnType) {
         assumeTrue(supportsAbort()) ;
         Dataset ds = create() ;
-        ds.begin(mode) ;
+        ds.begin(txnType) ;
         ds.abort() ;
         try {
             ds.commit() ;
-            fail("Expected transaction exception - abort-commit(" + mode + ")") ;
+            fail("Expected transaction exception - abort-commit(" + txnType + ")") ;
         }
         catch (JenaTransactionException ex) {
             safeEnd(ds) ;
@@ -360,7 +473,7 @@ public abstract class AbstractTestTransactionLifecycle extends BaseTest
 
                 @Override
                 public Boolean call() {
-                    ds.begin(ReadWrite.WRITE);
+                    ds.begin(TxnType.WRITE);
                     long x = counter.incrementAndGet() ;
                     // Hold the lock for a short while.
                     // The W threads will take the sleep serially.
@@ -394,7 +507,7 @@ public abstract class AbstractTestTransactionLifecycle extends BaseTest
             Callable<Boolean> callable = new Callable<Boolean>() {
                 @Override
                 public Boolean call() {
-                    ds.begin(ReadWrite.READ);
+                    ds.begin(TxnType.READ);
                     long x = counter.incrementAndGet() ;
                     // Hold the lock for a few seconds - these should be in parallel.
                     Lib.sleep(1000) ;

http://git-wip-us.apache.org/repos/asf/jena/blob/edab900a/jena-arq/src/test/java/org/apache/jena/sparql/transaction/TS_Transaction.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/transaction/TS_Transaction.java b/jena-arq/src/test/java/org/apache/jena/sparql/transaction/TS_Transaction.java
index 7fbbe62..7cc1879 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/transaction/TS_Transaction.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/transaction/TS_Transaction.java
@@ -25,8 +25,6 @@ import org.junit.runners.Suite.SuiteClasses ;
 @RunWith(Suite.class)
 @SuiteClasses( {
     TestTransactionSupport.class
-    , TestTransactionDSGWithLockNoAbort.class
-    , TestTransactionDSGWithLockWithAbort.class
 })
 
 public class TS_Transaction

http://git-wip-us.apache.org/repos/asf/jena/blob/edab900a/jena-arq/src/test/java/org/apache/jena/sparql/transaction/TestTransactionDSGWithLockNoAbort.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/transaction/TestTransactionDSGWithLockNoAbort.java b/jena-arq/src/test/java/org/apache/jena/sparql/transaction/TestTransactionDSGWithLockNoAbort.java
deleted file mode 100644
index 98617d0..0000000
--- a/jena-arq/src/test/java/org/apache/jena/sparql/transaction/TestTransactionDSGWithLockNoAbort.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/**
- * 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.transaction;
-
-import org.apache.jena.query.Dataset ;
-import org.apache.jena.query.DatasetFactory ;
-import org.apache.jena.sparql.core.DatasetGraph ;
-import org.apache.jena.sparql.core.DatasetGraphFactory ;
-import org.apache.jena.sparql.core.DatasetGraphWithLock ;
-
-/** Version with abortImplemented == false (the default). */
-public class TestTransactionDSGWithLockNoAbort extends AbstractTestTransactionLifecycle
-{
-    @Override
-    protected boolean supportsAbort() { return false ; }
-
-    @Override
-    protected Dataset create()
-    { 
-        DatasetGraph dsg = DatasetGraphFactory.create() ;
-        DatasetGraphWithLock dsgl = new  DatasetGraphWithLock(dsg, false) ;
-        return DatasetFactory.wrap(dsgl) ;
-    }
- }
-

http://git-wip-us.apache.org/repos/asf/jena/blob/edab900a/jena-arq/src/test/java/org/apache/jena/sparql/transaction/TestTransactionDSGWithLockWithAbort.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/transaction/TestTransactionDSGWithLockWithAbort.java b/jena-arq/src/test/java/org/apache/jena/sparql/transaction/TestTransactionDSGWithLockWithAbort.java
deleted file mode 100644
index 9e0fbd6..0000000
--- a/jena-arq/src/test/java/org/apache/jena/sparql/transaction/TestTransactionDSGWithLockWithAbort.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/**
- * 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.transaction;
-
-import org.apache.jena.query.Dataset ;
-import org.apache.jena.query.DatasetFactory ;
-import org.apache.jena.sparql.core.DatasetGraph ;
-import org.apache.jena.sparql.core.DatasetGraphFactory ;
-import org.apache.jena.sparql.core.DatasetGraphWithLock ;
-
-/** Version with abortImplemented == true */
-public class TestTransactionDSGWithLockWithAbort extends AbstractTestTransactionLifecycle
-{
-    @Override
-    protected Dataset create() { 
-        // The tests don't actually add/delete data.
-        DatasetGraph dsg = DatasetGraphFactory.createTxnMem() ;
-        DatasetGraphWithLock dsgl = new  DatasetGraphWithLock(dsg, true) ;
-        return DatasetFactory.wrap(dsgl) ;
-    }
- }
-

http://git-wip-us.apache.org/repos/asf/jena/blob/edab900a/jena-arq/src/test/java/org/apache/jena/sparql/transaction/TestTransactionSupport.java
----------------------------------------------------------------------
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 4eb1cac..46c955a 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 java.util.List ;
 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.DatasetGraphZero;
 import org.apache.jena.sparql.graph.GraphFactory ;
 import org.junit.Assert ;
 import org.junit.Test ;
@@ -50,7 +51,10 @@ public class TestTransactionSupport {
         x.add(new Object[] {"createOneGraph" ,
             (Creator<DatasetGraph>)()->
                 DatasetGraphFactory.createOneGraph(GraphFactory.createDefaultGraph()),
-            true, false}) ;
+            true, true}) ;
+        x.add(new Object[] {"createZeroGraph" ,
+            (Creator<DatasetGraph>)()->new DatasetGraphZero(),
+            true, true}) ;
         x.add(new Object[] {"create(Graph)",
             (Creator<DatasetGraph>)()->
                 DatasetGraphFactory.create(GraphFactory.createDefaultGraph()),

http://git-wip-us.apache.org/repos/asf/jena/blob/edab900a/jena-base/src/main/java/org/apache/jena/atlas/lib/Lib.java
----------------------------------------------------------------------
diff --git a/jena-base/src/main/java/org/apache/jena/atlas/lib/Lib.java b/jena-base/src/main/java/org/apache/jena/atlas/lib/Lib.java
index 27db959..19b6ac0 100644
--- a/jena-base/src/main/java/org/apache/jena/atlas/lib/Lib.java
+++ b/jena-base/src/main/java/org/apache/jena/atlas/lib/Lib.java
@@ -101,6 +101,17 @@ public class Lib
     }
     
     /** 
+     * Read thread local, assuming that "null" means it does not exist for this thread.
+     * If null is read, the thread local is removed.
+     */
+    public static <X> X readThreadLocal(ThreadLocal<X> threadLocal) {
+        X x = threadLocal.get();
+        if ( x == null )
+            threadLocal.remove();
+        return x ;
+    }
+    
+    /** 
      * @see CRC32
      */
     public static long crc32(byte[] bytes)

http://git-wip-us.apache.org/repos/asf/jena/blob/edab900a/jena-db/jena-dboe-transaction/src/main/java/org/apache/jena/dboe/transaction/txn/Transaction.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-dboe-transaction/src/main/java/org/apache/jena/dboe/transaction/txn/Transaction.java b/jena-db/jena-dboe-transaction/src/main/java/org/apache/jena/dboe/transaction/txn/Transaction.java
index d523e74..6bac45c 100644
--- a/jena-db/jena-dboe-transaction/src/main/java/org/apache/jena/dboe/transaction/txn/Transaction.java
+++ b/jena-db/jena-dboe-transaction/src/main/java/org/apache/jena/dboe/transaction/txn/Transaction.java
@@ -181,10 +181,11 @@ public class Transaction implements TransactionInfo {
     public void end() {
         txnMgr.notifyEndStart(this) ;
         if ( isWriteTxn() && getState() == ACTIVE ) {
-            throw new TransactionException("Write transaction with no commit or abort") ;
             //Log.warn(this, "Write transaction with no commit() or abort() before end()");
             // Just the abort process.
-            //abort$() ;
+            abort$() ;
+            endInternal() ;
+            throw new TransactionException("Write transaction with no commit() or abort() before end() - forced abort") ;
         }
         endInternal() ;
         txnMgr.notifyEndFinish(this) ;