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 2017/09/28 11:08:17 UTC

[01/50] [abbrv] jena git commit: Version 0.3.0-SNAPSHOT

Repository: jena
Updated Branches:
  refs/heads/jena-tdb2 3fbbfccf0 -> d9da3592f


Version 0.3.0-SNAPSHOT


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

Branch: refs/heads/jena-tdb2
Commit: 46862c3d01043880f900405d061f46206023c654
Parents: cd88ede
Author: Andy Seaborne <an...@seaborne.org>
Authored: Sun Aug 20 22:01:48 2017 +0100
Committer: Andy Seaborne <an...@seaborne.org>
Committed: Sun Aug 20 22:14:03 2017 +0100

----------------------------------------------------------------------
 dboe-base/pom.xml        |  2 +-
 dboe-index-test/pom.xml  |  4 ++--
 dboe-index/pom.xml       |  4 ++--
 dboe-trans-data/pom.xml  | 10 +++++-----
 dboe-transaction/pom.xml |  4 ++--
 fuseki-tdb2/pom.xml      |  4 ++--
 pom.xml                  |  2 +-
 tdb2-cmds/pom.xml        |  4 ++--
 tdb2/pom.xml             |  4 ++--
 9 files changed, 19 insertions(+), 19 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/46862c3d/dboe-base/pom.xml
----------------------------------------------------------------------
diff --git a/dboe-base/pom.xml b/dboe-base/pom.xml
index be06a45..1843969 100644
--- a/dboe-base/pom.xml
+++ b/dboe-base/pom.xml
@@ -26,7 +26,7 @@
   <parent>
     <groupId>org.seaborne.mantis</groupId>
     <artifactId>mantis</artifactId>
-    <version>0.2.1-SNAPSHOT</version>
+    <version>0.3.0-SNAPSHOT</version>
   </parent> 
 
   <dependencies>

http://git-wip-us.apache.org/repos/asf/jena/blob/46862c3d/dboe-index-test/pom.xml
----------------------------------------------------------------------
diff --git a/dboe-index-test/pom.xml b/dboe-index-test/pom.xml
index 63bb38f..658d621 100644
--- a/dboe-index-test/pom.xml
+++ b/dboe-index-test/pom.xml
@@ -26,7 +26,7 @@
   <parent>
     <groupId>org.seaborne.mantis</groupId>
     <artifactId>mantis</artifactId>
-    <version>0.2.1-SNAPSHOT</version>
+    <version>0.3.0-SNAPSHOT</version>
   </parent> 
 
   <dependencies>
@@ -34,7 +34,7 @@
     <dependency>
       <groupId>org.seaborne.mantis</groupId>
       <artifactId>dboe-index</artifactId>
-      <version>0.2.1-SNAPSHOT</version>
+      <version>0.3.0-SNAPSHOT</version>
     </dependency>
 
     <dependency>

http://git-wip-us.apache.org/repos/asf/jena/blob/46862c3d/dboe-index/pom.xml
----------------------------------------------------------------------
diff --git a/dboe-index/pom.xml b/dboe-index/pom.xml
index 5b7eaae..2b21bbe 100644
--- a/dboe-index/pom.xml
+++ b/dboe-index/pom.xml
@@ -26,7 +26,7 @@
   <parent>
     <groupId>org.seaborne.mantis</groupId>
     <artifactId>mantis</artifactId>
-    <version>0.2.1-SNAPSHOT</version>
+    <version>0.3.0-SNAPSHOT</version>
   </parent> 
 
   <dependencies>
@@ -34,7 +34,7 @@
     <dependency>
       <groupId>org.seaborne.mantis</groupId>
       <artifactId>dboe-base</artifactId>
-      <version>0.2.1-SNAPSHOT</version>
+      <version>0.3.0-SNAPSHOT</version>
     </dependency>
 
   </dependencies>

http://git-wip-us.apache.org/repos/asf/jena/blob/46862c3d/dboe-trans-data/pom.xml
----------------------------------------------------------------------
diff --git a/dboe-trans-data/pom.xml b/dboe-trans-data/pom.xml
index 97ae961..08bd822 100644
--- a/dboe-trans-data/pom.xml
+++ b/dboe-trans-data/pom.xml
@@ -25,7 +25,7 @@
   <parent>
     <groupId>org.seaborne.mantis</groupId>
     <artifactId>mantis</artifactId>
-    <version>0.2.1-SNAPSHOT</version>
+    <version>0.3.0-SNAPSHOT</version>
   </parent> 
 
   <dependencies>
@@ -33,26 +33,26 @@
     <dependency>
       <groupId>org.seaborne.mantis</groupId>
       <artifactId>dboe-transaction</artifactId>
-      <version>0.2.1-SNAPSHOT</version>
+      <version>0.3.0-SNAPSHOT</version>
     </dependency>
 
     <dependency>
       <groupId>org.seaborne.mantis</groupId>
       <artifactId>dboe-index</artifactId>
-      <version>0.2.1-SNAPSHOT</version>
+      <version>0.3.0-SNAPSHOT</version>
     </dependency>
 
     <dependency>
       <groupId>org.seaborne.mantis</groupId>
       <artifactId>dboe-index-test</artifactId>
-      <version>0.2.1-SNAPSHOT</version>
+      <version>0.3.0-SNAPSHOT</version>
       <scope>test</scope>
     </dependency>
 
     <dependency>
       <groupId>org.seaborne.mantis</groupId>
       <artifactId>dboe-base</artifactId>
-      <version>0.2.1-SNAPSHOT</version>
+      <version>0.3.0-SNAPSHOT</version>
       <classifier>tests</classifier>
       <scope>test</scope>
     </dependency>

http://git-wip-us.apache.org/repos/asf/jena/blob/46862c3d/dboe-transaction/pom.xml
----------------------------------------------------------------------
diff --git a/dboe-transaction/pom.xml b/dboe-transaction/pom.xml
index eed36d7..76b7902 100644
--- a/dboe-transaction/pom.xml
+++ b/dboe-transaction/pom.xml
@@ -25,7 +25,7 @@
   <parent>
     <groupId>org.seaborne.mantis</groupId>
     <artifactId>mantis</artifactId>
-    <version>0.2.1-SNAPSHOT</version>
+    <version>0.3.0-SNAPSHOT</version>
   </parent> 
 
   <dependencies>
@@ -33,7 +33,7 @@
     <dependency>
       <groupId>org.seaborne.mantis</groupId>
       <artifactId>dboe-base</artifactId>
-      <version>0.2.1-SNAPSHOT</version>
+      <version>0.3.0-SNAPSHOT</version>
     </dependency>
 
   </dependencies>

http://git-wip-us.apache.org/repos/asf/jena/blob/46862c3d/fuseki-tdb2/pom.xml
----------------------------------------------------------------------
diff --git a/fuseki-tdb2/pom.xml b/fuseki-tdb2/pom.xml
index 4c1d368..248881c 100644
--- a/fuseki-tdb2/pom.xml
+++ b/fuseki-tdb2/pom.xml
@@ -25,7 +25,7 @@
   <parent>
     <groupId>org.seaborne.mantis</groupId>
     <artifactId>mantis</artifactId>
-    <version>0.2.1-SNAPSHOT</version>
+    <version>0.3.0-SNAPSHOT</version>
   </parent> 
 
   <packaging>jar</packaging>
@@ -36,7 +36,7 @@
     <dependency>
       <groupId>org.seaborne.mantis</groupId>
       <artifactId>tdb2</artifactId>
-      <version>0.2.1-SNAPSHOT</version>
+      <version>0.3.0-SNAPSHOT</version>
     </dependency>
 
     <dependency>

http://git-wip-us.apache.org/repos/asf/jena/blob/46862c3d/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 5c67470..77a2033 100644
--- a/pom.xml
+++ b/pom.xml
@@ -23,7 +23,7 @@
   <artifactId>mantis</artifactId>
   <packaging>pom</packaging>
   <url>https://github.com/afs/mantis</url>
-  <version>0.2.1-SNAPSHOT</version>
+  <version>0.3.0-SNAPSHOT</version>
   <name>Mantis :: Database Operation Environment</name>
   <description>Mantis :: Database Operation Environment</description>
 

http://git-wip-us.apache.org/repos/asf/jena/blob/46862c3d/tdb2-cmds/pom.xml
----------------------------------------------------------------------
diff --git a/tdb2-cmds/pom.xml b/tdb2-cmds/pom.xml
index 7ab19bd..9cd77bf 100644
--- a/tdb2-cmds/pom.xml
+++ b/tdb2-cmds/pom.xml
@@ -27,7 +27,7 @@
   <parent>
     <groupId>org.seaborne.mantis</groupId>
     <artifactId>mantis</artifactId>
-    <version>0.2.1-SNAPSHOT</version>
+    <version>0.3.0-SNAPSHOT</version>
   </parent> 
 
   <dependencies>
@@ -35,7 +35,7 @@
     <dependency>
       <groupId>org.seaborne.mantis</groupId>
       <artifactId>tdb2</artifactId>
-      <version>0.2.1-SNAPSHOT</version>
+      <version>0.3.0-SNAPSHOT</version>
     </dependency>
 
     <dependency>

http://git-wip-us.apache.org/repos/asf/jena/blob/46862c3d/tdb2/pom.xml
----------------------------------------------------------------------
diff --git a/tdb2/pom.xml b/tdb2/pom.xml
index e3d08b9..0dd7f62 100644
--- a/tdb2/pom.xml
+++ b/tdb2/pom.xml
@@ -27,7 +27,7 @@
   <parent>
     <groupId>org.seaborne.mantis</groupId>
     <artifactId>mantis</artifactId>
-    <version>0.2.1-SNAPSHOT</version>
+    <version>0.3.0-SNAPSHOT</version>
   </parent> 
 
   <dependencies>
@@ -43,7 +43,7 @@
     <dependency>
       <groupId>org.seaborne.mantis</groupId>
       <artifactId>dboe-trans-data</artifactId>
-      <version>0.2.1-SNAPSHOT</version>
+      <version>0.3.0-SNAPSHOT</version>
     </dependency>
 
     <dependency>


[30/50] [abbrv] jena git commit: JENA-1396: Merge //github/afs/mantis as subdirectory jena-db/

Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableOps.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableOps.java
index 0000000,0000000..378bdfc
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableOps.java
@@@ -1,0 -1,0 +1,50 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.nodetable;
++
++import java.util.ArrayList ;
++import java.util.List ;
++
++import org.apache.jena.graph.Node ;
++import org.seaborne.tdb2.store.NodeId ;
++
++public class NodeTableOps {
++    
++    /** Convert a bulk operation into a loop */  
++    public static List<NodeId> bulkNodeToNodeIdImpl(NodeTable nt, List<Node> nodes, boolean withAllocation) {
++        List<NodeId> nodeIds = new ArrayList<>(nodes.size()) ;
++        for ( Node node : nodes ) {
++            NodeId nid = withAllocation ? nt.getAllocateNodeId(node) : nt.getNodeIdForNode(node) ;
++            nodeIds.add(nid) ;
++        }
++        return nodeIds ;
++    }
++    
++    /** Convert a bulk operation into a loop */  
++    public static List<Node> bulkNodeIdToNodeImpl(NodeTable nt, List<NodeId> nodeIds) {
++        List<Node> nodes = new ArrayList<>(nodeIds.size()) ;
++        for ( NodeId nodeId : nodeIds ) {
++            Node n = nt.getNodeForNodeId(nodeId) ;
++            nodes.add(n) ;
++        }
++        return nodes ;
++    }
++    
++
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableTRDF.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableTRDF.java
index 0000000,0000000..44f71d4
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableTRDF.java
@@@ -1,0 -1,0 +1,108 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.nodetable ;
++
++import org.apache.jena.atlas.logging.Log ;
++import org.apache.jena.graph.Node ;
++import org.apache.jena.riot.thrift.RiotThriftException ;
++import org.apache.jena.riot.thrift.TRDF ;
++import org.apache.jena.riot.thrift.ThriftConvert ;
++import org.apache.jena.riot.thrift.wire.RDF_Term ;
++import org.apache.thrift.TException ;
++import org.apache.thrift.protocol.TProtocol ;
++import org.seaborne.dboe.base.file.BinaryDataFile ;
++import org.seaborne.dboe.index.Index ;
++import org.seaborne.tdb2.TDBException ;
++import org.seaborne.tdb2.store.NodeId ;
++import org.seaborne.tdb2.store.NodeIdFactory;
++
++/** NodeTable using Thrift for the I/O and storage. */
++
++public class NodeTableTRDF extends NodeTableNative {
++    // Write buffering is done in the underlying BinaryDataFile
++    BinaryDataFile diskFile ;
++    private TReadAppendFileTransport transport ;
++    private final TProtocol protocol ;
++
++    public NodeTableTRDF(Index nodeToId, BinaryDataFile objectFile) {
++        super(nodeToId) ;
++        try {
++            this.diskFile = objectFile ;
++            transport = new TReadAppendFileTransport(diskFile) ;
++            if ( ! transport.isOpen() )
++                transport.open(); 
++            this.protocol = TRDF.protocol(transport) ;
++        }
++        catch (Exception ex) {
++            throw new TDBException("NodeTableTRDF", ex) ;
++        }
++    }
++
++    @Override
++    protected NodeId writeNodeToTable(Node node) {
++        RDF_Term term = ThriftConvert.convert(node, true) ;
++        try {
++            long x = diskFile.length() ;
++            // Paired : [*]
++            NodeId nid = NodeIdFactory.createPtr(x);
++            term.write(protocol) ;
++            //transport.flush() ;
++            return nid ;
++        }
++        catch (Exception ex) {
++            throw new TDBException("NodeTableThrift/Write", ex) ;
++        }
++    }
++
++    @Override
++    protected Node readNodeFromTable(NodeId id) {
++        try {
++            // Paired : [*]
++            long x = id.getPtrLocation();
++            transport.readPosition(x) ;
++            RDF_Term term = new RDF_Term() ;
++            term.read(protocol) ;
++            Node n = ThriftConvert.convert(term) ;
++            return n ;
++        }
++        catch (TException ex) {
++            throw new TDBException("NodeTableTRDF/Read", ex) ;
++        }
++        catch (RiotThriftException ex) {
++            Log.error(this, "Bad encoding: NodeId = "+id) ;
++            throw ex ;
++        }
++    }
++
++    @Override
++    protected void syncSub() {
++        try { transport.flush(); }
++        catch (Exception ex) { throw new TDBException("NodeTableTRDF", ex) ; }
++    }
++
++    @Override
++    protected void closeSub() {
++        if ( transport.isOpen() ) {
++            try { transport.close() ; }
++            catch (Exception ex) { throw new TDBException("NodeTableTRDF", ex) ; }
++        }
++    }
++
++    public Index getIndex()             { return nodeHashToId ; }
++    public BinaryDataFile getData()     { return diskFile ; }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableWrapper.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableWrapper.java
index 0000000,0000000..f18203d
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableWrapper.java
@@@ -1,0 -1,0 +1,93 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.nodetable ;
++
++import java.util.Iterator ;
++import java.util.List ;
++
++import org.apache.jena.atlas.lib.Pair ;
++import org.apache.jena.graph.Node ;
++import org.seaborne.tdb2.store.NodeId ;
++
++public class NodeTableWrapper implements NodeTable {
++    protected final NodeTable nodeTable ;
++
++    @Override
++    public final NodeTable wrapped() {
++        return nodeTable ;
++    }
++
++    protected NodeTableWrapper(NodeTable nodeTable) {
++        this.nodeTable = nodeTable ;
++    }
++
++    @Override
++    public NodeId getAllocateNodeId(Node node) {
++        return nodeTable.getAllocateNodeId(node) ;
++    }
++
++    @Override
++    public NodeId getNodeIdForNode(Node node) {
++        return nodeTable.getNodeIdForNode(node) ;
++    }
++
++    @Override
++    public Node getNodeForNodeId(NodeId id) {
++        return nodeTable.getNodeForNodeId(id) ;
++    }
++
++    @Override
++    public boolean containsNode(Node node) {
++        return nodeTable.containsNode(node) ;
++    }
++
++    @Override
++    public boolean containsNodeId(NodeId nodeId) {
++        return nodeTable.containsNodeId(nodeId) ;
++    }
++
++    @Override
++    public List<NodeId> bulkNodeToNodeId(List<Node> nodes, boolean withAllocation) {
++        return nodeTable.bulkNodeToNodeId(nodes, withAllocation) ;
++    }
++
++    @Override
++    public List<Node> bulkNodeIdToNode(List<NodeId> nodeIds) {
++        return nodeTable.bulkNodeIdToNode(nodeIds) ;
++    }
++
++    @Override
++    public Iterator<Pair<NodeId, Node>> all() {
++        return nodeTable.all() ;
++    }
++
++    @Override
++    public boolean isEmpty() {
++        return nodeTable.isEmpty() ;
++    }
++
++    @Override
++    public void sync() {
++        nodeTable.sync() ;
++    }
++
++    @Override
++    public void close() {
++        nodeTable.close() ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/TReadAppendFileTransport.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/TReadAppendFileTransport.java
index 0000000,0000000..34eebfb
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/TReadAppendFileTransport.java
@@@ -1,0 -1,0 +1,85 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.nodetable;
++
++import java.util.Objects ;
++
++import org.apache.thrift.transport.TTransport ;
++import org.seaborne.dboe.base.file.BinaryDataFile ;
++
++/** A file transport that supports random access read and
++ *  buffered append write.
++ *  <p>
++ *  Adapter TTransport -&gt; BinaryDataFile
++ */
++public class TReadAppendFileTransport extends TTransport {
++    private BinaryDataFile file ;
++    private long readPosn = -1 ;
++    
++    public TReadAppendFileTransport(BinaryDataFile file) {
++        Objects.requireNonNull(file) ;
++        this.file = file ;
++    }
++    
++    @Override
++    public boolean isOpen() {
++        return file.isOpen() ;
++    }
++
++    @Override
++    public void open() {
++        file.open() ; 
++    }
++
++    @Override
++    public void close() {
++        file.close() ; 
++    }
++    
++    public void truncate(long posn) {
++        file.truncate(posn); 
++    }
++
++    public BinaryDataFile getBinaryDataFile() { return file ; }
++
++    public long readPosition() {
++        return readPosn ;
++    }
++
++    public void readPosition(long posn) {
++        readPosn = posn ;
++    }
++    
++    @Override
++    public int read(byte[] buf, int off, int len) {
++        int x = file.read(readPosn, buf, off, len) ;
++        readPosn += x ;
++        return x ;
++    }
++
++    @Override
++    public void write(byte[] buf, int off, int len) {
++        file.write(buf, off, len) ;
++    }
++    
++    @Override
++    public void flush()  {
++        file.sync(); 
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTable.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTable.java
index 0000000,0000000..4d48189
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTable.java
@@@ -1,0 -1,0 +1,71 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.nodetupletable ;
++
++import java.util.Iterator ;
++
++import org.apache.jena.atlas.lib.Closeable ;
++import org.apache.jena.atlas.lib.Sync ;
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.graph.Node ;
++import org.seaborne.tdb2.store.NodeId ;
++import org.seaborne.tdb2.store.nodetable.NodeTable ;
++import org.seaborne.tdb2.store.tupletable.TupleTable ;
++
++public interface NodeTupleTable extends Sync, Closeable
++{
++    public void addRow(Node... nodes) ;
++
++    public void deleteRow(Node... nodes) ;
++
++    /** Find by node. */
++    public Iterator<Tuple<Node>> find(Node... nodes) ;
++
++    /** Find by node - return an iterator of NodeIds. Can return "null" for not found as well as NullIterator */
++    public Iterator<Tuple<NodeId>> findAsNodeIds(Node... nodes) ;
++
++    /** Find by NodeId. */
++    public Iterator<Tuple<NodeId>> find(NodeId... ids) ;
++    
++    /** Find by NodeId. */
++    public Iterator<Tuple<NodeId>> find(Tuple<NodeId> ids) ;
++
++    /** Find all tuples */ 
++    public Iterator<Tuple<NodeId>> findAll() ;
++
++    /** Return the undelying tuple table - used with great care by tools
++     * that directly manipulate internal structures. 
++     */
++    public TupleTable getTupleTable() ;
++
++    /** Return the node table */
++    public NodeTable getNodeTable() ;
++
++    public boolean isEmpty() ;
++    
++    /** Clear the tuple table.  After this operation, find* will find  nothing.
++     * This does not mean all data has been removed - for example, it does not mean
++     * that any node table has been emptied.
++     */
++    public void clear() ;
++
++    // No clear operation - need to manage the tuple table 
++    // and node tables separately.
++    
++    public long size() ;
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableConcrete.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableConcrete.java
index 0000000,0000000..e2fc5a9
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableConcrete.java
@@@ -1,0 -1,0 +1,240 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.nodetupletable ;
++
++import static java.lang.String.format ;
++
++import java.util.Iterator ;
++
++import org.apache.jena.atlas.iterator.Iter ;
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.atlas.lib.tuple.TupleFactory ;
++import org.apache.jena.graph.Node ;
++import org.seaborne.tdb2.TDBException ;
++import org.seaborne.tdb2.lib.TupleLib ;
++import org.seaborne.tdb2.store.NodeId ;
++import org.seaborne.tdb2.store.nodetable.NodeTable ;
++import org.seaborne.tdb2.store.tupletable.TupleIndex ;
++import org.seaborne.tdb2.store.tupletable.TupleTable ;
++
++/** Group a tuple table and node table together to provide a real NodeTupleTable */
++public class NodeTupleTableConcrete implements NodeTupleTable
++{
++    protected final NodeTable  nodeTable ;
++    protected final TupleTable tupleTable ;
++
++    /*
++     * Concurrency checking: Everything goes through one of 
++     * addRow, deleteRow or find*
++     */
++
++    public NodeTupleTableConcrete(int N, TupleIndex[] indexes, NodeTable nodeTable)
++    {
++        if (indexes.length == 0 || indexes[0] == null) throw new TDBException("A primary index is required") ;
++        for (TupleIndex index : indexes)
++        {
++            if (N != index.getTupleLength())
++                throw new TDBException(format("Inconsistent: TupleTable width is %d but index %s is %d",
++                                              N, index.getMappingStr(), index.getTupleLength())) ;
++        }
++
++        this.tupleTable = new TupleTable(N, indexes) ;
++        this.nodeTable = nodeTable ;
++    }
++
++    private void startWrite()   { }
++
++    private void finishWrite()  { }
++
++    private void startRead()    { }
++
++    private void finishRead()   { }
++
++    @Override
++    public void addRow(Node... nodes)
++    {
++        try  {
++            startWrite() ;
++            NodeId n[] = new NodeId[nodes.length] ;
++            for (int i = 0; i < nodes.length; i++)
++                n[i] = nodeTable.getAllocateNodeId(nodes[i]) ;
++
++            Tuple<NodeId> t = TupleFactory.create(n) ;
++            tupleTable.add(t) ;
++        } finally 
++        {
++            finishWrite() ;
++        }
++    }
++
++    @Override
++    public void deleteRow(Node... nodes)
++    {
++        try
++        {
++            startWrite() ;
++            NodeId n[] = new NodeId[nodes.length] ;
++            for (int i = 0; i < nodes.length; i++)
++            {
++                NodeId id = idForNode(nodes[i]) ;
++                if (NodeId.isDoesNotExist(id)) return ;
++                n[i] = id ;
++            }
++
++            Tuple<NodeId> t = TupleFactory.create(n) ;
++            tupleTable.delete(t) ;
++        } finally
++        {
++            finishWrite() ;
++        }
++    }
++
++    /** Find by node. */
++    @Override
++    public Iterator<Tuple<Node>> find(Node... nodes)
++    {
++        try {
++            startRead() ;
++            Iterator<Tuple<NodeId>> iter1 = findAsNodeIds(nodes) ; // **public call
++            if (iter1 == null) 
++                return Iter.nullIterator() ;
++            Iterator<Tuple<Node>> iter2 = TupleLib.convertToNodes(nodeTable, iter1) ;
++            return iteratorControl(iter2) ;
++        } finally { finishRead() ; }
++    }
++
++    /**
++     * Find by node - return an iterator of NodeIds. 
++     * Can return "null" (when a node is known to be unknown)
++     * for not found as well as NullIterator (when
++     * no tuples are found (unknown unknown).
++     */
++    @Override
++    public Iterator<Tuple<NodeId>> findAsNodeIds(Node... nodes)
++    {
++        NodeId n[] = new NodeId[nodes.length] ;
++        try {
++            startRead() ;
++            for (int i = 0; i < nodes.length; i++)
++            {
++                NodeId id = idForNode(nodes[i]) ;
++                if (NodeId.isDoesNotExist(id)) 
++                    return Iter.nullIterator() ;
++                n[i] = id ;
++            }
++            return find(n) ; // **public call
++        } finally { finishRead() ; }
++    }
++
++    /** Find by NodeId. */
++    @Override
++    public Iterator<Tuple<NodeId>> find(NodeId... ids)
++    {
++        Tuple<NodeId> tuple = TupleFactory.create(ids) ;
++        return find(tuple) ;
++    }
++
++    /** Find by NodeId. */
++    @Override
++    public Iterator<Tuple<NodeId>> find(Tuple<NodeId> tuple)
++    {
++        // All find/*, except findAll, comes through this operation so startRead/finishRead/checkIterator only needs to happen here.
++        try {
++            startRead() ;
++            // find worker - need also protect iterators that access the node table.
++            Iterator<Tuple<NodeId>> iter = tupleTable.find(tuple) ;
++            return iteratorControl(iter) ;
++        } finally { finishRead() ; }
++    }
++
++    @Override
++    public Iterator<Tuple<NodeId>> findAll()
++    {
++        try {
++            startRead() ;
++            return iteratorControl(tupleTable.getIndex(0).all()) ;
++        } finally { finishRead() ; }
++    }
++
++    // ==== Node
++
++    protected final NodeId idForNode(Node node) {
++        if ( node == null || node == Node.ANY )
++            return NodeId.NodeIdAny ;
++        if ( node.isVariable() )
++            throw new TDBException("Can't pass variables to NodeTupleTable.find*") ;
++        return nodeTable.getNodeIdForNode(node) ;
++    }
++
++    // ==== Accessors
++
++    /**
++     * Return the undelying tuple table - used with great care by tools that directly
++     * manipulate internal structures.
++     */
++    @Override
++    public final TupleTable getTupleTable() {
++        return tupleTable ;
++    }
++
++    /** Return the node table */
++    @Override
++    public final NodeTable getNodeTable() {
++        return nodeTable ;
++    }
++
++    @Override
++    public boolean isEmpty() {
++        return tupleTable.isEmpty() ;
++    }
++
++    /** Clear the tuple table - does not clear the node table */
++    @Override
++    public void clear() {
++        try {
++            startWrite() ;
++            tupleTable.clear() ;
++        } finally { finishWrite() ; }
++    }
++
++    @Override
++    public long size() {
++        return tupleTable.size() ;
++    }
++
++    @Override
++    public final void close() {
++        try {
++            startWrite() ;
++            tupleTable.close() ;
++            nodeTable.close() ;
++        } finally { finishWrite() ; }
++    }
++
++    @Override
++    public final void sync() {
++        try {
++            startWrite() ;
++            tupleTable.sync() ;
++            nodeTable.sync() ;
++        } finally { finishWrite() ; }
++    }
++
++    // Add any iterator checking.
++    private <T> Iterator<T> iteratorControl(Iterator<T> iter) { return iter; }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableView.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableView.java
index 0000000,0000000..ea24558
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableView.java
@@@ -1,0 -1,0 +1,112 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.nodetupletable;
++
++import java.util.Iterator ;
++
++import org.apache.jena.atlas.lib.ArrayUtils ;
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.graph.Node ;
++import org.seaborne.tdb2.store.NodeId ;
++import org.seaborne.tdb2.store.tupletable.TupleTable ;
++
++/** (Read-only?) projection of another NodeTupleTable. 
++ * This will not reduce a N-wide tuple to N-1 when find*() used. 
++ */ 
++public class NodeTupleTableView extends NodeTupleTableWrapper
++{
++    private Node prefix ;
++    private NodeId prefixId ;
++    //private boolean readOnly = false ;
++
++    public NodeTupleTableView(NodeTupleTable ntt, Node prefix)
++    {
++        super(ntt) ;
++        this.prefix = prefix ;
++        this.prefixId = ntt.getNodeTable().getNodeIdForNode(prefix) ;
++    }
++    
++    @Override
++    public void addRow(Node... nodes)
++    { 
++        nodes = push(Node.class, prefix, nodes) ;
++        super.addRow(nodes) ;
++    }
++
++    @Override
++    public void deleteRow(Node... nodes)
++    {
++        nodes = push(Node.class, prefix, nodes) ;
++        super.deleteRow(nodes) ;
++    }
++    
++    @Override
++    public Iterator<Tuple<Node>> find(Node... nodes)
++    { 
++        nodes = push(Node.class, prefix, nodes) ;
++        return nodeTupleTable.find(nodes) ;
++    }
++    
++    private static <T> T[] push(Class<T> cls, T x,  T[] array)
++    {
++        T[] array2 = ArrayUtils.alloc(cls, array.length+1) ;
++        System.arraycopy(array, 0, array2, 1, array.length) ;
++        array2[0] = x ;
++        return array2 ;
++    }
++
++    private static <T> T[] push(Class<T> cls, T x,  Tuple<T> tuple)
++    {
++        T[] array2 = ArrayUtils.alloc(cls, tuple.len()+1) ;
++        tuple.copyInto(array2, 1);
++        array2[0] = x ;
++        return array2 ;
++    }
++
++    @Override
++    public Iterator<Tuple<NodeId>> find(NodeId... ids)
++    {
++        ids = push(NodeId.class, prefixId, ids) ;
++        return nodeTupleTable.find(ids) ;
++    }
++    
++    @Override
++    public Iterator<Tuple<NodeId>> find(Tuple<NodeId> ids)
++    {
++        NodeId[] ids2 = push(NodeId.class, prefixId, ids) ;
++        return nodeTupleTable.find(ids2) ;
++    }
++
++    @Override
++    public Iterator<Tuple<NodeId>> findAsNodeIds(Node... nodes)
++    {
++        nodes = push(Node.class, prefix, nodes) ;
++        return nodeTupleTable.findAsNodeIds(nodes) ;
++    }
++
++//    @Override
++//    public boolean isReadOnly() { return readOnly ; }
++//
++//    @Override
++//    public void setReadOnly(boolean mode)   { readOnly = mode ; }
++
++    @Override
++    public TupleTable getTupleTable()
++    // Need a projection of this?
++    { return super.getTupleTable() ; }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableWrapper.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableWrapper.java
index 0000000,0000000..952dda9
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableWrapper.java
@@@ -1,0 -1,0 +1,97 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.nodetupletable;
++
++import java.util.Iterator ;
++
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.graph.Node ;
++import org.seaborne.tdb2.store.NodeId ;
++import org.seaborne.tdb2.store.nodetable.NodeTable ;
++import org.seaborne.tdb2.store.tupletable.TupleTable ;
++
++public class NodeTupleTableWrapper implements NodeTupleTable
++{
++    protected NodeTupleTable nodeTupleTable ;
++
++    public NodeTupleTableWrapper(NodeTupleTable ntt) { 
++        setNodeTupleTable(ntt) ;
++    }
++    
++    protected NodeTupleTable setNodeTupleTable(NodeTupleTable ntt) {
++        NodeTupleTable old = nodeTupleTable ;
++        nodeTupleTable = ntt ;
++        return old ;
++    }
++     
++    @Override
++    public void addRow(Node... nodes)
++    { nodeTupleTable.addRow(nodes) ; }
++
++    @Override
++    public void deleteRow(Node... nodes)
++    { nodeTupleTable.deleteRow(nodes) ; }
++
++    @Override
++    public Iterator<Tuple<Node>> find(Node... nodes)
++    { return nodeTupleTable.find(nodes) ; }
++    
++    @Override
++    public Iterator<Tuple<NodeId>> find(NodeId... ids)
++    { return nodeTupleTable.find(ids) ; }
++    
++    @Override
++    public Iterator<Tuple<NodeId>> find(Tuple<NodeId> tuple)
++    { return nodeTupleTable.find(tuple) ; }
++    
++    @Override
++    public Iterator<Tuple<NodeId>> findAsNodeIds(Node... nodes)
++    { return nodeTupleTable.findAsNodeIds(nodes) ; }
++
++    @Override
++    public Iterator<Tuple<NodeId>> findAll()
++    { return nodeTupleTable.findAll() ; }
++
++    @Override
++    public NodeTable getNodeTable()
++    { return nodeTupleTable.getNodeTable() ; }
++
++    @Override
++    public TupleTable getTupleTable()
++    { return nodeTupleTable.getTupleTable() ; }
++
++    @Override
++    public boolean isEmpty()
++    { return nodeTupleTable.isEmpty() ; }
++    
++    @Override
++    public void clear()
++    { nodeTupleTable.clear(); }
++
++    @Override
++    public long size()
++    { return nodeTupleTable.size() ; }
++
++    @Override
++    public void sync()
++    { nodeTupleTable.sync() ; }
++
++    @Override
++    public void close()
++    { nodeTupleTable.close() ; }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleIndex.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleIndex.java
index 0000000,0000000..e3c8b47
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleIndex.java
@@@ -1,0 -1,0 +1,87 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.tupletable;
++
++import java.util.Collection ;
++import java.util.Iterator ;
++
++import org.apache.jena.atlas.lib.Closeable ;
++import org.apache.jena.atlas.lib.Sync ;
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.atlas.lib.tuple.TupleMap ;
++import org.seaborne.tdb2.store.NodeId ;
++
++public interface TupleIndex extends Sync, Closeable
++{
++    /** Insert a tuple */
++    public void add(Tuple<NodeId> tuple) ;
++
++    /** Delete a tuple */
++    public void delete(Tuple<NodeId> tuple) ; 
++
++    /** Insert tuples */
++    public void addAll(Collection<Tuple<NodeId>> tuples) ;
++    
++    /** Delete tuples */
++    public void deleteAll(Collection<Tuple<NodeId>> tuples) ;
++    
++    /** Get a convenient display string for the index - do not rely on the format */ 
++    public String getName() ;
++    
++    /** Get a convenient display string based on the details of the column map - do not rely on the format */ 
++    public String getMappingStr() ;
++    
++    /** Get the mapping of tuples used by this index */  
++    public TupleMap getMapping() ;
++    
++    /** Find all matching tuples - a slot of NodeId.NodeIdAny (or null) means match any.
++     *  Input pattern in natural order, not index order.
++     */
++    public Iterator<Tuple<NodeId>> find(Tuple<NodeId> pattern) ;
++    
++    /** return an iterator of everything */
++    public Iterator<Tuple<NodeId>> all() ;
++    
++    /** Weight a pattern - specified in normal order (not index order).
++     * Large numbers means better match. */
++    public int weight(Tuple<NodeId> pattern) ;
++
++    /** Length of tuple supported */
++    public int getTupleLength() ;
++
++    /** Size of index (number of slots). May be an estimate and not exact. -1 for unknown.  */
++    public long size() ;
++
++    /** Answer whether empty or not */
++    public boolean isEmpty() ;
++    
++    /** Clear the index */
++    public void clear() ;
++    
++    /** Return a TupleIndex if this instance wraps another, else return null */  
++    public TupleIndex wrapped() ; 
++    
++    default public TupleIndex baseTupleIndex() {
++        TupleIndex index = this ;
++        TupleIndex index2 = null ;
++        while( (index2 = index.wrapped()) != null ) {
++            index = index2 ;
++        }
++        return index ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleIndexBase.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleIndexBase.java
index 0000000,0000000..911950b
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleIndexBase.java
@@@ -1,0 -1,0 +1,123 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.tupletable;
++
++import java.util.Iterator ;
++
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.atlas.lib.tuple.TupleMap ;
++import org.seaborne.tdb2.TDBException ;
++import org.seaborne.tdb2.store.NodeId ;
++
++public abstract class TupleIndexBase implements TupleIndex
++{
++    private static final boolean Check = false ;
++
++    protected final TupleMap tupleMap ;
++    protected final int tupleLength ;
++
++    private final String name ;
++    
++    protected TupleIndexBase(int N, TupleMap indexMapping, String name)
++    {
++        this.tupleLength = N ;
++        this.tupleMap = indexMapping ;
++        this.name = name ;
++    }
++    
++    @Override
++    public TupleIndex wrapped() {
++        return null ;
++    }
++    
++    /** Add tuple worker: Tuple passed in unmapped (untouched) order */
++    protected abstract void performAdd(Tuple<NodeId> tuple) ;
++    
++    /** Delete tuple worker: Tuple passed in unmapped (untouched) order */
++    protected abstract void performDelete(Tuple<NodeId> tuple) ;
++    
++    /** Find tuples worker: Tuple passed in unmaped (untouched) order */
++    protected abstract Iterator<Tuple<NodeId>> performFind(Tuple<NodeId> tuple) ;
++
++    /** Insert a tuple */
++    @Override
++    public final void add(Tuple<NodeId> tuple) 
++    { 
++        if ( Check ) {
++            if ( tupleLength != tuple.len() )
++                throw new TDBException(String.format("Mismatch: tuple length %d / index for length %d", tuple.len(), tupleLength));
++        }
++        performAdd(tuple) ;
++    }
++    /** Delete a tuple */
++    @Override
++    public final void delete(Tuple<NodeId> tuple) 
++    { 
++        if ( Check ) {
++            if ( tupleLength != tuple.len() )
++                throw new TDBException(String.format("Mismatch: tuple length %d / index for length %d", tuple.len(), tupleLength));
++        }
++
++        performDelete(tuple) ;
++    }
++
++    /** Find all matching tuples - a slot of NodeId.NodeIdAny (or null) means match any.
++     *  Input pattern in natural order, not index order.
++     */
++    @Override
++    public final Iterator<Tuple<NodeId>> find(Tuple<NodeId> pattern)
++    {
++        if ( Check ) {
++            if ( tupleLength != pattern.len() )
++                throw new TDBException(String.format("Mismatch: tuple length %d / index for length %d", pattern.len(), tupleLength));
++        }
++        // null to NodeId.NodIdAny ??
++        return performFind(pattern) ;
++    }
++    
++    @Override
++    public final int weight(Tuple<NodeId> pattern)
++    {
++        for ( int i = 0 ; i < tupleLength ; i++ )
++        {
++            NodeId X = tupleMap.mapSlot(i, pattern) ;
++            if ( undef(X) )
++                // End of fixed terms
++                return i ;
++        }
++        return tupleLength ;
++    }
++    
++    @Override
++    public final String getMappingStr()     { return tupleMap.getLabel() ; }
++
++    @Override
++    public final String getName()           { return name ; }
++
++    @Override
++    public final int getTupleLength()       { return tupleLength ; }
++
++    @Override
++    public final TupleMap getMapping()      { return tupleMap ;  }
++    
++    protected final boolean undef(NodeId x)
++    { return NodeId.isAny(x) ; }
++    
++    @Override
++    public String toString() { return "index:"+getName() ; }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleIndexRecord.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleIndexRecord.java
index 0000000,0000000..c723472
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleIndexRecord.java
@@@ -1,0 -1,0 +1,237 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.tupletable;
++
++import static java.lang.String.format ;
++import static org.seaborne.tdb2.sys.SystemTDB.SizeOfNodeId ;
++
++import java.util.Collection ;
++import java.util.Iterator ;
++import java.util.function.Predicate ;
++
++import org.apache.jena.atlas.iterator.Iter ;
++import org.apache.jena.atlas.iterator.NullIterator ;
++import org.apache.jena.atlas.iterator.SingletonIterator ;
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.atlas.lib.tuple.TupleMap ;
++import org.seaborne.dboe.base.record.Record ;
++import org.seaborne.dboe.base.record.RecordFactory ;
++import org.seaborne.dboe.index.RangeIndex ;
++import org.seaborne.tdb2.TDBException ;
++import org.seaborne.tdb2.lib.TupleLib ;
++import org.seaborne.tdb2.store.NodeId ;
++import org.seaborne.tdb2.store.NodeIdFactory;
++
++public class TupleIndexRecord extends TupleIndexBase
++{
++    private static final boolean Check = false ;
++    private RangeIndex index ; 
++    private RecordFactory factory ;
++    
++    public TupleIndexRecord(int N,  TupleMap tupleMapping, String name, RecordFactory factory, RangeIndex index)
++    {
++        super(N, tupleMapping, name) ;
++        this.factory = factory ;
++        this.index = index ;
++        
++        if ( factory.keyLength() != N*SizeOfNodeId)
++            throw new TDBException(format("Mismatch: TupleIndex of length %d is not comparative with a factory for key length %d", N, factory.keyLength())) ;
++    }
++    
++    /** Insert a tuple */
++    @Override
++    protected void performAdd(Tuple<NodeId> tuple) { 
++        Record r = TupleLib.record(factory, tuple, tupleMap) ;
++        index.insert(r) ;
++    }
++    
++    /** Delete a tuple */
++    @Override
++    protected void performDelete(Tuple<NodeId> tuple) { 
++        Record r = TupleLib.record(factory, tuple, tupleMap) ;
++        index.delete(r) ;
++    }
++    
++    /** Insert tuples */
++    @Override
++    public void addAll(Collection<Tuple<NodeId>> tuples) {
++        for ( Tuple<NodeId> t : tuples ) 
++            add(t) ;
++    }
++    
++    /** Delete tuples */
++    @Override
++    public void deleteAll(Collection<Tuple<NodeId>> tuples) {
++        for ( Tuple<NodeId> t : tuples ) 
++            delete(t) ;
++    }
++    
++    /** Find all matching tuples - a slot of NodeId.NodeIdAny (or null) means match any.
++     *  Input pattern in natural order, not index order.
++     */
++    
++    @Override
++    protected Iterator<Tuple<NodeId>> performFind(Tuple<NodeId> pattern) {
++        return findOrScan(pattern) ;
++    }
++
++    // Package visibility for testing.
++    final Iterator<Tuple<NodeId>> findOrScan(Tuple<NodeId> pattern) {
++        return findWorker(pattern, true, true) ;
++    }
++    
++    final Iterator<Tuple<NodeId>> findOrPartialScan(Tuple<NodeId> pattern) {
++        return findWorker(pattern, true, false) ;
++    }
++
++    final Iterator<Tuple<NodeId>> findByIndex(Tuple<NodeId> pattern) {
++        return findWorker(pattern, false, false) ;
++    }
++    
++    private Iterator<Tuple<NodeId>> findWorker(Tuple<NodeId> patternNaturalOrder, boolean partialScanAllowed, boolean fullScanAllowed) {
++        if ( Check )
++        {
++            if ( tupleLength != patternNaturalOrder.len() )
++            throw new TDBException(String.format("Mismatch: tuple length %d / index for length %d", patternNaturalOrder.len(), tupleLength)) ;
++        } 
++        
++        // Convert to index order.
++        Tuple<NodeId> pattern = tupleMap.map(patternNaturalOrder) ;
++        
++        // Canonical form.
++        int numSlots = 0 ;
++        int leadingIdx = -2;    // Index of last leading pattern NodeId.  Start less than numSlots-1
++        boolean leading = true ;
++        
++        // Records.
++        Record minRec = factory.createKeyOnly() ;
++        Record maxRec = factory.createKeyOnly() ;
++        
++        // Set the prefixes.
++        for ( int i = 0 ; i < pattern.len() ; i++ ) {
++            NodeId X = pattern.get(i) ;
++            if ( NodeId.isAny(X) ) {
++                X = null ;
++                // No longer seting leading key slots.
++                leading = false ;
++                continue ;
++            }
++//            if ( NodeId.isDoesNotExist(X) )
++//                return Iter.nullIterator();
++
++            numSlots++ ;
++            if ( leading ) {
++                leadingIdx = i ;
++                NodeIdFactory.set(X, minRec.getKey(), i*SizeOfNodeId) ;
++                NodeIdFactory.set(X, maxRec.getKey(), i*SizeOfNodeId) ;
++            }
++        }
++        
++        // Is it a simple existence test?
++        if ( numSlots == pattern.len() ) {
++            if ( index.contains(minRec) )
++                return new SingletonIterator<>(pattern) ;
++            else
++                return new NullIterator<>() ;
++        }
++        
++        Iterator<Record> iter = null ;
++        
++        if ( leadingIdx < 0 ) {
++            if ( ! fullScanAllowed )
++                return null ;
++            //System.out.println("Full scan") ;
++            // Full scan necessary
++            iter = index.iterator() ;
++        } else {
++            // Adjust the maxRec.
++            NodeId X = pattern.get(leadingIdx) ;
++            // Set the max Record to the leading NodeIds, +1.
++            // Example, SP? inclusive to S(P+1)? exclusive where ? is zero. 
++            NodeIdFactory.setNext(X, maxRec.getKey(), leadingIdx*SizeOfNodeId) ;
++            iter = index.iterator(minRec, maxRec) ;
++        }
++        
++        Iterator<Tuple<NodeId>> tuples = Iter.map(iter, item -> TupleLib.tuple(item, tupleMap)) ;
++        
++        if ( leadingIdx < numSlots-1 ) {
++            if ( ! partialScanAllowed )
++                return null ;
++            // Didn't match all defined slots in request.  
++            // Partial or full scan needed.
++            //pattern.unmap(colMap) ;
++            tuples = scan(tuples, patternNaturalOrder) ;
++        }
++        
++        return tuples ;
++    }
++    
++    @Override
++    public Iterator<Tuple<NodeId>> all()
++    {
++        Iterator<Record> iter = index.iterator() ;
++        return Iter.map(iter, item -> TupleLib.tuple(item, tupleMap)) ;
++    }
++    
++    private Iterator<Tuple<NodeId>> scan(Iterator<Tuple<NodeId>> iter, Tuple<NodeId> pattern) {
++        Predicate<Tuple<NodeId>> filter = (item) -> {
++            // Check on pattern and item (both in natural order)
++            for ( int i = 0 ; i < tupleLength ; i++ ) {
++                NodeId n = pattern.get(i) ;
++                // The pattern must be null/Any or match the tuple being tested.
++                if ( ! NodeId.isAny(n) )
++                    if ( ! item.get(i).equals(n) ) 
++                        return false ;
++            }
++            return true ;
++        } ;
++        
++        return Iter.filter(iter, filter) ;
++    }
++    
++    @Override
++    public void close() {
++        index.close() ;
++    }
++
++    @Override
++    public void sync() {
++        index.sync() ;
++    }
++
++    public final RangeIndex getRangeIndex() {
++        return index ;
++    }
++
++    //protected final RecordFactory getRecordFactory()        { return factory ; }
++    
++    @Override
++    public boolean isEmpty() {
++        return index.isEmpty() ;
++    }
++
++    @Override
++    public void clear() {
++        index.clear() ;
++    }
++
++    @Override
++    public long size() {
++        return index.size() ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleIndexRecordAsyncBulkAdd.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleIndexRecordAsyncBulkAdd.java
index 0000000,0000000..242232e
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleIndexRecordAsyncBulkAdd.java
@@@ -1,0 -1,0 +1,262 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.tupletable;
++
++import static java.lang.String.format ;
++import static org.seaborne.tdb2.sys.SystemTDB.SizeOfNodeId ;
++
++import java.util.Collection ;
++import java.util.Iterator ;
++import java.util.function.Predicate ;
++
++import org.apache.jena.atlas.iterator.Iter ;
++import org.apache.jena.atlas.iterator.NullIterator ;
++import org.apache.jena.atlas.iterator.SingletonIterator ;
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.atlas.lib.tuple.TupleMap ;
++import org.seaborne.dboe.base.record.Record ;
++import org.seaborne.dboe.base.record.RecordFactory ;
++import org.seaborne.dboe.index.RangeIndex ;
++import org.seaborne.dboe.transaction.txn.Transaction ;
++import org.seaborne.tdb2.TDBException ;
++import org.seaborne.tdb2.lib.Async ;
++import org.seaborne.tdb2.lib.TupleLib ;
++import org.seaborne.tdb2.store.NodeId ;
++import org.seaborne.tdb2.store.NodeIdFactory;
++
++// Async addAll 
++public class TupleIndexRecordAsyncBulkAdd extends TupleIndexBase
++{
++    private static final boolean Check = false ;
++    private RangeIndex index ; 
++    private RecordFactory factory ;
++    
++    public TupleIndexRecordAsyncBulkAdd(int N,  TupleMap tupleMapping, String name, RecordFactory factory, RangeIndex index)
++    {
++        super(N, tupleMapping, name) ;
++        this.factory = factory ;
++        this.index = index ;
++        
++        if ( factory.keyLength() != N*SizeOfNodeId)
++            throw new TDBException(format("Mismatch: TupleIndex of length %d is not comparative with a factory for key length %d", N, factory.keyLength())) ;
++    }
++    
++    /** Insert a tuple */
++    @Override
++    protected void performAdd(Tuple<NodeId> tuple) {
++        switchToSync() ;
++        Record r = TupleLib.record(factory, tuple, tupleMap) ;
++        index.insert(r) ;
++    }
++    
++    /** Delete a tuple */
++    @Override
++    protected void performDelete(Tuple<NodeId> tuple) { 
++        switchToSync() ;
++        Record r = TupleLib.record(factory, tuple, tupleMap) ;
++        index.delete(r) ;
++    }
++    
++    private void switchToSync() {
++        async.completeAsyncOperations();
++    }
++
++    Async async = new Async(1,2) ;
++    Object lock = new Object() ;
++    
++    /** Insert tuples */
++    @Override
++    public void addAll(Collection<Tuple<NodeId>> tuples) {
++        Transaction txn = null ;
++        // pass into async block.
++        
++        async.execAsync(lock, () -> {
++            // Transaction?
++            
++            System.out.println(">>Async") ;
++            for ( Tuple<NodeId> t : tuples ) {
++                Record r = TupleLib.record(factory, t, tupleMap) ;
++                index.insert(r) ;
++            }
++            System.out.println("<<Async") ;
++        }) ;
++    }
++    
++    /** Delete tuples */
++    @Override
++    public void deleteAll(Collection<Tuple<NodeId>> tuples) {
++        tuples.stream().forEach(this::delete);
++    }
++    
++    /** Find all matching tuples - a slot of NodeId.NodeIdAny (or null) means match any.
++     *  Input pattern in natural order, not index order.
++     */
++    
++    @Override
++    protected Iterator<Tuple<NodeId>> performFind(Tuple<NodeId> pattern) {
++        switchToSync() ;
++        return findOrScan(pattern) ;
++    }
++
++    // Package visibility for testing.
++    final Iterator<Tuple<NodeId>> findOrScan(Tuple<NodeId> pattern) {
++        return findWorker(pattern, true, true) ;
++    }
++    
++    final Iterator<Tuple<NodeId>> findOrPartialScan(Tuple<NodeId> pattern) {
++        return findWorker(pattern, true, false) ;
++    }
++
++    final Iterator<Tuple<NodeId>> findByIndex(Tuple<NodeId> pattern) {
++        return findWorker(pattern, false, false) ;
++    }
++    
++    private Iterator<Tuple<NodeId>> findWorker(Tuple<NodeId> patternNaturalOrder, boolean partialScanAllowed, boolean fullScanAllowed) {
++        switchToSync();
++        if ( Check && tupleLength != patternNaturalOrder.len() )
++            throw new TDBException(String.format("Mismatch: tuple length %d / index for length %d", patternNaturalOrder.len(), tupleLength));
++
++        // Convert to index order.
++        Tuple<NodeId> pattern = tupleMap.map(patternNaturalOrder) ;
++        
++        // Canonical form.
++        int numSlots = 0 ;
++        int leadingIdx = -2;    // Index of last leading pattern NodeId.  Start less than numSlots-1
++        boolean leading = true ;
++        
++        // Records.
++        Record minRec = factory.createKeyOnly() ;
++        Record maxRec = factory.createKeyOnly() ;
++        
++        // Set the prefixes.
++        for ( int i = 0 ; i < pattern.len() ; i++ ) {
++            NodeId X = pattern.get(i) ;
++            if ( NodeId.isAny(X) ) {
++                X = null ;
++                // No longer setting leading key slots.
++                leading = false ;
++                continue ;
++            }
++
++            numSlots++ ;
++            if ( leading ) {
++                leadingIdx = i ;
++                NodeIdFactory.set(X, minRec.getKey(), i*SizeOfNodeId) ;
++                NodeIdFactory.set(X, maxRec.getKey(), i*SizeOfNodeId) ;
++            }
++        }
++        
++        // Is it a simple existence test?
++        if ( numSlots == pattern.len() ) {
++            if ( index.contains(minRec) )
++                return new SingletonIterator<>(pattern) ;
++            else
++                return new NullIterator<>() ;
++        }
++        
++        Iterator<Record> iter = null ;
++        
++        if ( leadingIdx < 0 ) {
++            if ( ! fullScanAllowed )
++                return null ;
++            //System.out.println("Full scan") ;
++            // Full scan necessary
++            iter = index.iterator() ;
++        } else {
++            // Adjust the maxRec.
++            NodeId X = pattern.get(leadingIdx) ;
++            // Set the max Record to the leading NodeIds, +1.
++            // Example, SP? inclusive to S(P+1)? exclusive where ? is zero. 
++            NodeIdFactory.setNext(X, maxRec.getKey(), leadingIdx*SizeOfNodeId) ;
++            iter = index.iterator(minRec, maxRec) ;
++        }
++        
++        Iterator<Tuple<NodeId>> tuples = Iter.map(iter, item -> TupleLib.tuple(item, tupleMap)) ;
++        
++        if ( leadingIdx < numSlots-1 ) {
++            if ( ! partialScanAllowed )
++                return null ;
++            // Didn't match all defined slots in request.  
++            // Partial or full scan needed.
++            //pattern.unmap(colMap) ;
++            tuples = scan(tuples, patternNaturalOrder) ;
++        }
++        
++        return tuples ;
++    }
++    
++    @Override
++    public Iterator<Tuple<NodeId>> all()
++    {
++        switchToSync() ;
++        Iterator<Record> iter = index.iterator() ;
++        return Iter.map(iter, item -> TupleLib.tuple(item, tupleMap)) ;
++    }
++    
++    private Iterator<Tuple<NodeId>> scan(Iterator<Tuple<NodeId>> iter, Tuple<NodeId> pattern) {
++        Predicate<Tuple<NodeId>> filter = (item) -> {
++            // Check on pattern and item (both in natural order)
++            for ( int i = 0 ; i < tupleLength ; i++ ) {
++                NodeId n = pattern.get(i) ;
++                // The pattern must be null/Any or match the tuple being tested.
++                if ( ! NodeId.isAny(n) )
++                    if ( ! item.get(i).equals(n) ) 
++                        return false ;
++            }
++            return true ;
++        } ;
++        
++        return Iter.filter(iter, filter) ;
++    }
++    
++    @Override
++    public void close() {
++        switchToSync() ;
++        index.close() ;
++    }
++
++    @Override
++    public void sync() {
++        switchToSync() ;
++        index.sync() ;
++    }
++
++    public final RangeIndex getRangeIndex() {
++        return index ;
++    }
++
++    //protected final RecordFactory getRecordFactory()        { return factory ; }
++    
++    @Override
++    public boolean isEmpty() {
++        switchToSync() ;
++        return index.isEmpty() ;
++    }
++
++    @Override
++    public void clear() {
++        switchToSync() ;
++        index.clear() ;
++    }
++
++    @Override
++    public long size() {
++        switchToSync() ;
++        return index.size() ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleIndexWrapper.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleIndexWrapper.java
index 0000000,0000000..43f4b72
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleIndexWrapper.java
@@@ -1,0 -1,0 +1,117 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.tupletable;
++
++import java.util.Collection ;
++import java.util.Iterator ;
++
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.atlas.lib.tuple.TupleMap ;
++import org.seaborne.tdb2.store.NodeId ;
++
++public class TupleIndexWrapper implements TupleIndex
++{
++    protected final TupleIndex index ;
++
++    public TupleIndexWrapper(TupleIndex index) { this.index = index ; }
++    
++    @Override
++    public final TupleIndex wrapped() {
++        return index ;
++    }
++
++    @Override
++    public void add(Tuple<NodeId> tuple) {
++        index.add(tuple) ;
++    }
++
++    @Override
++    public void addAll(Collection<Tuple<NodeId>> tuples) {
++        index.addAll(tuples) ;
++    }
++
++    @Override
++    public void delete(Tuple<NodeId> tuple) {
++        index.delete(tuple) ;
++    }
++
++    @Override
++    public void deleteAll(Collection<Tuple<NodeId>> tuples) {
++        index.deleteAll(tuples);
++    }
++
++    @Override
++    public Iterator<Tuple<NodeId>> find(Tuple<NodeId> pattern) {
++        return index.find(pattern) ;
++    }
++
++    @Override
++    public Iterator<Tuple<NodeId>> all() {
++        return index.all() ;
++    }
++
++    @Override
++    public int getTupleLength() {
++        return index.getTupleLength() ;
++    }
++
++    @Override
++    public String getMappingStr() {
++        return index.getMappingStr() ;
++    }
++
++    @Override
++    public TupleMap getMapping() {
++        return index.getMapping() ;
++    }
++
++    @Override
++    public String getName() {
++        return index.getName() ;
++    }
++
++    @Override
++    public int weight(Tuple<NodeId> pattern) {
++        return index.weight(pattern) ;
++    }
++
++    @Override
++    public long size() {
++        return index.size() ;
++    }
++
++    @Override
++    public boolean isEmpty() {
++        return index.isEmpty() ;
++    }
++
++    @Override
++    public void clear() {
++        index.clear() ;
++    }
++
++    @Override
++    public void sync() {
++        index.sync() ;
++    }
++
++    @Override
++    public void close() {
++        index.close() ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleTable.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleTable.java
index 0000000,0000000..d02df70
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/tupletable/TupleTable.java
@@@ -1,0 -1,0 +1,218 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.tupletable;
++
++import static java.lang.String.format ;
++
++import java.util.Iterator ;
++import java.util.List ;
++
++import org.apache.jena.atlas.iterator.Iter;
++import org.apache.jena.atlas.lib.Closeable ;
++import org.apache.jena.atlas.lib.Sync ;
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.atlas.logging.Log ;
++import org.seaborne.tdb2.TDBException ;
++import org.seaborne.tdb2.store.NodeId ;
++import org.seaborne.tdb2.sys.SystemTDB ;
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++/** A TupleTable is a set of TupleIndexes.  The first TupleIndex is the "primary" index and must exist */
++public class TupleTable implements Sync, Closeable
++{
++    private static Logger log = LoggerFactory.getLogger(TupleTable.class) ;
++    
++    private final TupleIndex[] indexes ;
++    private final TupleIndex   scanAllIndex ;   // Use this index if a complete scan is needed.
++    private final int tupleLen ;
++    private boolean syncNeeded = false ;
++    
++    public TupleTable(int tupleLen, TupleIndex[] indexes)
++    {
++        this.tupleLen = tupleLen ;
++        this.indexes = indexes ;
++        if ( indexes[0] == null )
++            throw new TDBException("TupleTable: no primary index") ;
++        for ( TupleIndex index : indexes )
++        {
++            if ( index != null && index.getTupleLength() != tupleLen )
++                throw new TDBException("Incompatible index: "+index.getMappingStr()) ;
++        }
++        scanAllIndex = chooseScanAllIndex(tupleLen, indexes) ;
++    }
++    
++    /** Choose an index to scan in case we are asked for everything
++     * This needs to be ???G for the distinctAdjacent filter in union query to work.
++     */
++    private static TupleIndex chooseScanAllIndex(int tupleLen, TupleIndex[] indexes)
++    {
++        if ( tupleLen != 4 )
++            return indexes[0] ;
++        
++        for ( TupleIndex index : indexes )
++        {
++            // First look for SPOG
++            if ( index.getName().equals("SPOG") )
++                return index ;
++        }
++        
++        for ( TupleIndex index : indexes )
++        {
++            // Then look for any ???G
++            if ( index.getName().endsWith("G") )
++                return index ;
++        }
++        
++        Log.warn(SystemTDB.errlog, "Did not find a ???G index for full scans") ;
++        return indexes[0] ;
++    }
++
++    /** Insert a tuple */
++    public void add(Tuple<NodeId> t) {
++        // A "contains test" could be used to avoid needing to hit all
++        // the indexes when the triple is already present.
++        if ( tupleLen != t.len() )
++            throw new TDBException(format("Mismatch: inserting tuple of length %d into a table of tuples of length %d", t.len(), tupleLen)) ;
++        for ( int i = 0 ; i < indexes.length ; i++ ) {
++            if ( indexes[i] == null ) continue ;
++            indexes[i].add(t) ;
++            syncNeeded = true ;
++        }
++    }
++
++    /** Insert tuples */
++    public void addAll(List<Tuple<NodeId>> t) {
++        // Parallel.
++        for ( int i = 0 ; i < indexes.length ; i++ ) {
++            if ( indexes[i] == null ) continue ;
++            indexes[i].addAll(t) ;
++            syncNeeded = true ;
++        }
++    }
++
++    /** Delete a tuple */
++    public void delete( Tuple<NodeId> t ) { 
++        if ( tupleLen != t.len() )
++            throw new TDBException(format("Mismatch: deleting tuple of length %d from a table of tuples of length %d", t.len(), tupleLen)) ;
++
++        for ( TupleIndex index : indexes ) {
++            if ( index == null )
++                continue;
++            index.delete( t );
++        }
++    }
++    
++    /** Delete tuples */
++    public void deleteAll(List<Tuple<NodeId>> t) {
++        // Parallel.
++        for ( int i = 0 ; i < indexes.length ; i++ ) {
++            if ( indexes[i] == null ) continue ;
++            indexes[i].deleteAll(t) ;
++            syncNeeded = true ;
++        }
++    }
++
++    /** Find all matching tuples - a slot of NodeId.NodeIdAny means match any */
++    public Iterator<Tuple<NodeId>> find(Tuple<NodeId> pattern) {
++        if ( tupleLen != pattern.len() )
++            throw new TDBException(format("Mismatch: finding tuple of length %d in a table of tuples of length %d", pattern.len(), tupleLen)) ;
++        
++        int numSlots = 0 ;
++        // Canonical form. 
++        for ( int i = 0 ; i < tupleLen ; i++ ) {
++            NodeId x = pattern.get(i) ;
++            if ( ! NodeId.isAny(x) )
++                numSlots++ ;
++            if ( NodeId.isDoesNotExist(x))
++                return Iter.nullIterator();
++        }
++
++        if ( numSlots == 0 )
++            return scanAllIndex.all() ;
++        
++        int indexNumSlots = 0 ;
++        TupleIndex index = null ;
++        for ( TupleIndex idx : indexes ) {
++            if ( idx != null ) {
++                int w = idx.weight( pattern );
++                if ( w > indexNumSlots ) {
++                    indexNumSlots = w;
++                    index = idx;
++                }
++            }
++        }
++        
++        if ( index == null )
++            // No index at all.  Scan.
++            index = indexes[0] ;
++        return index.find(pattern) ;
++    }
++    
++    @Override
++    final public void close() {
++        for ( TupleIndex idx : indexes ) {
++            if ( idx != null )
++                idx.close();
++        }
++    }
++    
++    @Override
++    public void sync() {
++        if ( syncNeeded ) {
++            for ( TupleIndex idx : indexes ) {
++                if ( idx != null )
++                    idx.sync() ;
++            }
++            syncNeeded = false ;
++        }
++    }
++
++    public boolean isEmpty()        { return indexes[0].isEmpty() ; }
++    
++    public void clear() {
++        for ( TupleIndex idx : indexes ) {
++            if ( idx != null )
++                idx.clear() ;
++        }
++        syncNeeded = true ;
++    }
++    
++    public long size() {
++        return indexes[0].size() ;
++    }
++    
++    /** Get i'th index */ 
++    public TupleIndex getIndex(int i)                   { return indexes[i] ; }
++    
++    /** Get all indexes - for code that maipulates internal structures directly - use with care */ 
++    public TupleIndex[] getIndexes()                    { return indexes ; }
++    
++    /** Get the width of tuples in indexes in this table */
++    public int getTupleLen()                            { return tupleLen ; }
++
++    /** Set index - for code that manipulates internal structures directly - use with care */ 
++    public void setTupleIndex(int i, TupleIndex index) {
++        if ( index != null && index.getTupleLength() != tupleLen )
++            throw new TDBException("Incompatible index: " + index.getMappingStr()) ;
++        indexes[i] = index ;
++    }
++
++    /** Number of indexes on this tuple table */
++    public int numIndexes()                             { return indexes.length ; }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/value/DateTimeNode.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/value/DateTimeNode.java
index 0000000,0000000..0853938
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/value/DateTimeNode.java
@@@ -1,0 -1,0 +1,253 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.value;
++
++import java.math.BigDecimal ;
++
++import javax.xml.datatype.DatatypeConfigurationException ;
++import javax.xml.datatype.DatatypeConstants ;
++import javax.xml.datatype.DatatypeFactory ;
++import javax.xml.datatype.XMLGregorianCalendar ;
++
++import org.apache.jena.atlas.lib.BitsInt ;
++import org.apache.jena.atlas.lib.BitsLong ;
++import org.apache.jena.atlas.lib.NumberUtils ;
++import org.seaborne.tdb2.TDBException ;
++
++public class DateTimeNode
++{
++    // ---- Layout
++    // Epoch base: 0000-01-01T00:00:00
++
++    // Layout:
++    // Bits 56-63 : type
++    
++    // Bits 49-55 (7 bits)  : timezone -- 15 min precision + special for Z and no timezone.
++    // Bits 27-48 (22 bits) : date, year is 13 bits = 8000 years  (0 to 7999)
++    // Bits 0-26  (27 bits) : time, to milliseconds 
++
++    // Layout:
++    // Hi: TZ YYYY MM DD HH MM SS.sss Lo:
++    // Looses the distinction between 00:00:00 vs 24:00:00 (of the day before).
++
++    // Const-ize
++    // 13 bits year, 4 bits month, 5 bits day => 22 bits
++    static final int       DATE_LEN        = 22;
++    // 5 bits hour + 6 bits minute + 16 bits seconds (to millisecond)
++    static final int       TIME_LEN        = 27;
++
++    static final int       MILLI           = 0;
++    static final int       MILLI_LEN       = 16;
++
++    static final int       MINUTES         = MILLI_LEN;
++    static final int       MINUTES_LEN     = 6;
++
++    static final int       HOUR            = MILLI_LEN + MINUTES_LEN;
++    static final int       HOUR_LEN        = 5;
++
++    static final int       DAY             = TIME_LEN;
++    static final int       DAY_LEN         = 5;
++
++    static final int       MONTH           = TIME_LEN + DAY_LEN;
++    static final int       MONTH_LEN       = 4;
++
++    static final int       YEAR            = TIME_LEN + MONTH_LEN + DAY_LEN;
++    static final int       YEAR_LEN        = 13;
++
++    static final int       TZ              = TIME_LEN + DATE_LEN;
++    static final int       TZ_LEN          = 7;
++    // Value for Z
++    static final int       TZ_Z            = 0x7F;
++    // Value for no timezone.
++    static final int       TZ_NONE         = 0x7E;
++    
++    static DatatypeFactory datatypeFactory = null;
++    static {
++        try {
++            datatypeFactory = DatatypeFactory.newInstance();
++        }
++        catch (DatatypeConfigurationException ex) {
++            throw new TDBException("DateTimeNode", ex);
++        }
++    }
++
++    // Packed in correct place.
++    static long time(long v, int hour, int mins, int millisec) {
++        // And bit offset for direct packing?
++        // HH:MM:SS.ssss => 5 bits H, 6 bits M, 16 bits S ==> 27 bits
++        v = BitsLong.pack(v, hour, HOUR, HOUR + HOUR_LEN);
++        v = BitsLong.pack(v, mins, MINUTES, MINUTES + MINUTES_LEN);
++        v = BitsLong.pack(v, millisec, MILLI, MILLI + MILLI_LEN);
++        return v;
++    }
++
++    // Packed in correct place.
++    static long date(long v, int year, int month, int day) {
++        // YYYY:MM:DD => 13 bits year, 4 bits month, 5 bits day => 22 bits
++        v = BitsLong.pack(v, year, YEAR, YEAR + YEAR_LEN);
++        v = BitsLong.pack(v, month, MONTH, MONTH + MONTH_LEN);
++        v = BitsLong.pack(v, day, DAY, DAY + DAY_LEN);
++        return v;
++    }
++
++    static long tz(long v, int tz_in_quarters) {
++        v = BitsLong.pack(v, tz_in_quarters, TZ, TZ + TZ_LEN);
++        return v;
++    }
++
++    // From string. Assumed legal. Retains all info this way.
++    // returns -1 for unpackable.
++    public static long packDate(String lex) {
++        return packDateTime(lex);
++    }
++
++    // From string. Assumed legal.
++    // Returns -1 for unpackable.
++
++    public static long packDateTime(String lex) {
++        try {
++            return packDateTime$(lex);
++        }
++        catch (Exception ex) {
++            return -1;
++        }
++    }
++
++    private static long packDateTime$(String lex) {
++        long v = 0;
++        // Whitespace facet processing.
++        lex = lex.trim();
++
++        boolean containsZ = (lex.indexOf('Z') > 0);
++
++        // Bug in Java 1.6 (build 5 at least)
++        // T24:00:00 not accepted.
++        // See also TestNodeId.nodeId_date_time_7
++
++        XMLGregorianCalendar xcal = datatypeFactory.newXMLGregorianCalendar(lex);
++
++        if ( xcal.getFractionalSecond() != null ) {
++            BigDecimal fs = xcal.getFractionalSecond();
++            // Were there sub-millisecond resolution fractional seconds?
++            // This isn't perfect but it needs a very long fractional part to break it,
++            // less than observable quantum of time.
++            if ( fs.doubleValue() != xcal.getMillisecond() / 1000.0 )
++                return -1;
++        }
++
++        int y = xcal.getYear();
++
++        if ( y < 0 || y >= 8000 )
++            return -1;
++
++        v = date(v, xcal.getYear(), xcal.getMonth(), xcal.getDay());
++        v = time(v, xcal.getHour(), xcal.getMinute(), xcal.getSecond() * 1000 + xcal.getMillisecond());
++
++        if ( containsZ )
++            return tz(v, TZ_Z);
++
++        int tz = xcal.getTimezone();
++        if ( tz == DatatypeConstants.FIELD_UNDEFINED )
++            return tz(v, TZ_NONE);
++
++        // Timezone is weird.
++        if ( tz % 15 != 0 )
++            return -1;
++
++        tz = tz / 15;
++        return tz(v, tz);
++    }
++
++    public static String unpackDateTime(long v) {
++        return unpack(v, true);
++    }
++
++    public static String unpackDate(long v) {
++        return unpack(v, false);
++    }
++
++    // Avoid calls to String.format
++    private static String unpack(long v, boolean isDateTime) {
++        // YYYY:MM:DD => 13 bits year, 4 bits month, 5 bits day => 22 bits
++        int years = (int)BitsLong.unpack(v, YEAR, YEAR + YEAR_LEN);
++        int months = (int)BitsLong.unpack(v, MONTH, MONTH + MONTH_LEN);
++        int days = (int)BitsLong.unpack(v, DAY, DAY + DAY_LEN);
++
++        // Hours: 5, mins 6, milli 16, TZ 7 => 34 bits
++        int hours = (int)BitsLong.unpack(v, HOUR, HOUR + HOUR_LEN);
++        int minutes = (int)BitsLong.unpack(v, MINUTES, MINUTES + MINUTES_LEN);
++        int milliSeconds = (int)BitsLong.unpack(v, MILLI, MILLI + MILLI_LEN);
++
++        int tz = (int)BitsLong.unpack(v, TZ, TZ + TZ_LEN);
++
++        int sec = milliSeconds / 1000;
++        int fractionSec = milliSeconds % 1000;
++
++        StringBuilder sb = new StringBuilder(50);
++        NumberUtils.formatInt(sb, years, 4);
++        sb.append('-');
++        NumberUtils.formatInt(sb, months, 2);
++        sb.append('-');
++        NumberUtils.formatInt(sb, days, 2);
++        if ( isDateTime ) {
++            sb.append('T');
++            NumberUtils.formatInt(sb, hours, 2);
++            sb.append(':');
++            NumberUtils.formatInt(sb, minutes, 2);
++            sb.append(':');
++            NumberUtils.formatInt(sb, sec, 2);
++
++            // Formatting needed : int->any
++            if ( fractionSec != 0 ) {
++                sb.append(".");
++                if ( fractionSec % 100 == 0 )
++                    NumberUtils.formatInt(sb, fractionSec / 100, 1);
++                else if ( fractionSec % 10 == 0 )
++                    NumberUtils.formatInt(sb, fractionSec / 10, 2);
++                else
++                    NumberUtils.formatInt(sb, fractionSec, 3);
++            }
++        }
++        // tz in 15min units
++        // Special values.
++        if ( tz == TZ_Z ) {
++            sb.append("Z");
++            return sb.toString();
++        }
++
++        if ( tz == TZ_NONE )
++            return sb.toString();
++
++        // Sign extend.
++        if ( BitsLong.isSet(v, TZ + TZ_LEN - 1) )
++            tz = BitsInt.set(tz, TZ_LEN, 32);
++
++        if ( tz < 0 ) {
++            tz = -tz;
++            sb.append('-');
++        } else
++            sb.append('+');
++
++        int tzH = tz / 4;
++        int tzM = (tz % 4) * 15;
++        NumberUtils.formatUnsignedInt(sb, tzH, 2);
++        sb.append(':');
++        NumberUtils.formatUnsignedInt(sb, tzM, 2);
++        return sb.toString();
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/value/DecimalNode.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/value/DecimalNode.java
index 0000000,0000000..2e03d96
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/value/DecimalNode.java
@@@ -1,0 -1,0 +1,22 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.value;
++
++/** Placeholder for a full length decimal , using int+long.*/
++public class DecimalNode {
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/value/DecimalNode56.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/value/DecimalNode56.java
index 0000000,0000000..c36e5c5
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/value/DecimalNode56.java
@@@ -1,0 -1,0 +1,140 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.value;
++
++import java.math.BigDecimal;
++import java.math.BigInteger;
++
++import org.apache.jena.atlas.lib.BitsLong ;
++
++
++// Decimal packed into 56 bits.
++public class DecimalNode56
++{
++    //private static Logger log = LoggerFactory.getLogger(DecimalNode.class) ;
++    
++    BigDecimal decimal = null ;
++    
++    // signed 8 bits of scale, signed 48 bits of value.
++    // Decimal precision is 47 bits (it's signed) or around 14 places.
++    // Not finance industry accuracy nor XSD (18 places minimum) but still useful.
++
++    static final int        SCALE_LEN = 8;
++    static final int        VALUE_LEN = 48;
++    static final int        ENC_LEN   = 48 + SCALE_LEN;
++
++    static final long       MAX_VALUE = (1L << (VALUE_LEN - 1)) - 1;
++    static final long       MIN_VALUE = -(1L << (VALUE_LEN - 1));
++
++    static final int        MAX_SCALE = (1 << (SCALE_LEN - 1)) - 1;
++    static final int        MIN_SCALE = -(1 << (SCALE_LEN - 1));
++
++    static final BigInteger MAX_I     = BigInteger.valueOf(MAX_VALUE);
++    static final BigInteger MIN_I     = BigInteger.valueOf(MIN_VALUE);
++
++    // Bits counts
++    static private int      SCALE_LO  = 56 - SCALE_LEN;
++    static private int      SCALE_HI  = 56;                               // Exclusive
++                                                                          // index
++
++    static private int      VALUE_LO  = 0;
++    static private int      VALUE_HI  = VALUE_LO + VALUE_LEN;
++
++    private int             scale;
++    private long            value;
++
++    public static DecimalNode56 valueOf(BigDecimal decimal) {
++        int scale = decimal.scale();
++        BigInteger bigInt = decimal.unscaledValue();
++        
++        //decimal.longValueExact(); // Throws exception
++        //new BigDecimal(long);
++
++        if ( bigInt.compareTo(MAX_I) > 0 || bigInt.compareTo(MIN_I) < 0 )
++            // This check makes sure that bigInt.longValue() is safe
++            return null;
++        return valueOf(bigInt.longValue(), scale);
++    }
++
++    public static DecimalNode56 valueOf(long binValue, int scale) {
++        if ( scale < MIN_SCALE || scale > MAX_SCALE ) {
++            // log.warn("Scale out of range: ("+binValue+","+scale+")") ;
++            return null;
++        }
++
++        if ( binValue < MIN_VALUE || binValue > MAX_VALUE ) {
++            // log.warn("Value out of range: ("+binValue+","+scale+")") ;
++            return null;
++        }
++
++        return new DecimalNode56(binValue, scale);
++    }
++
++    private DecimalNode56(long value, int scale) {
++        this.scale = scale;
++        this.value = value;
++    }
++
++    public long pack() {
++        return pack(value, scale);
++    }
++
++    /** Create the long value */
++    public static long pack(long value, int scale) {
++        // pack : scale, value
++        long v = 0;
++        v = BitsLong.pack(0L, scale, SCALE_LO, SCALE_HI);
++        v = BitsLong.pack(v, value, VALUE_LO, VALUE_HI);
++        // No need to do something about negative numbers
++        return v;
++    }
++
++    public static DecimalNode56 unpack(long v) {
++        int scale = (int)BitsLong.unpack(v, SCALE_LO, SCALE_HI);
++        long value = BitsLong.unpack(v, VALUE_LO, VALUE_HI);
++        return new DecimalNode56(value, scale);
++    }
++
++    public static BigDecimal unpackAsBigDecimal(long v) {
++        int scale = (int)BitsLong.unpack(v, SCALE_LO, SCALE_HI);
++        long value = BitsLong.unpack(v, VALUE_LO, VALUE_HI);
++        // Sign extend value.
++        if ( BitsLong.isSet(value, VALUE_HI - 1) )
++            value = value | -1L << (VALUE_HI);
++        return BigDecimal.valueOf(value, scale);
++    }
++
++    public BigDecimal get() {
++        if ( decimal == null )
++            decimal = BigDecimal.valueOf(value, scale);
++        return decimal;
++    }
++
++    @Override
++    public String toString() {
++        return get().toPlainString();
++    }
++
++    public int getScale() {
++        return scale;
++    }
++
++    public long getValue() {
++        return value;
++    }
++}


[09/50] [abbrv] jena git commit: Formatting

Posted by an...@apache.org.
Formatting


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

Branch: refs/heads/jena-tdb2
Commit: 10f2bb16994c0c8973205accda4a52500c065916
Parents: 7624f54
Author: Andy Seaborne <an...@seaborne.org>
Authored: Sun Sep 3 17:14:07 2017 +0100
Committer: Andy Seaborne <an...@seaborne.org>
Committed: Sun Sep 3 17:14:07 2017 +0100

----------------------------------------------------------------------
 .../seaborne/dboe/base/file/ChannelManager.java | 62 ++++++++------------
 .../transaction/txn/TransactionCoordinator.java |  1 -
 2 files changed, 26 insertions(+), 37 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/10f2bb16/dboe-base/src/main/java/org/seaborne/dboe/base/file/ChannelManager.java
----------------------------------------------------------------------
diff --git a/dboe-base/src/main/java/org/seaborne/dboe/base/file/ChannelManager.java b/dboe-base/src/main/java/org/seaborne/dboe/base/file/ChannelManager.java
index 8738dd5..7b1d041 100644
--- a/dboe-base/src/main/java/org/seaborne/dboe/base/file/ChannelManager.java
+++ b/dboe-base/src/main/java/org/seaborne/dboe/base/file/ChannelManager.java
@@ -33,42 +33,36 @@ public class ChannelManager
     // ChannelManager
     
     // FileBase ==> OpenFileRef, ChannelRef
-    
-    public static FileChannel acquire(String filename)
-    {
+
+    public static FileChannel acquire(String filename) {
         return acquire(filename, "rw") ;
     }
-    
-    public static FileChannel acquire(String filename, String mode)
-    {
+
+    public static FileChannel acquire(String filename, String mode) {
         return openref$(filename, mode) ;
     }
-    
+
     static private Map<String, FileChannel> name2channel = new HashMap<>() ;
     static private Map<FileChannel, String> channel2name = new HashMap<>() ;
-    
-    private static FileChannel openref$(String filename, String mode)
-    {
+
+    private static FileChannel openref$(String filename, String mode) {
         // Temp - for now, only journal files are tracked.
-        if ( ! filename.endsWith(".jrnl") )
-        {
+        if ( !filename.endsWith(".jrnl") ) {
             return open$(filename, mode) ;
         }
-        
+
         FileChannel chan = name2channel.get(filename) ;
-        if ( chan != null )
-        {
+        if ( chan != null ) {
             // Scream - it's currently open.
-            throw new FileException("Already open: "+filename) ;
+            throw new FileException("Already open: " + filename) ;
         }
         chan = open$(filename, mode) ;
         name2channel.put(filename, chan) ;
         channel2name.put(chan, filename) ;
         return chan ;
     }
-    
-    private static FileChannel open$(String filename, String mode)
-    {
+
+    private static FileChannel open$(String filename, String mode) {
         try {
             // "rwd" - Syncs only the file contents
             // "rws" - Syncs the file contents and metadata
@@ -81,36 +75,33 @@ public class ChannelManager
         } catch (IOException ex) { throw new FileException("Failed to open: "+filename+" (mode="+mode+")", ex) ; }
     }
     
-    public static void release(String filename)
-    {
+    public static void release(String filename) {
         FileChannel channel = name2channel.get(filename) ;
         if ( channel != null )
             release(channel) ;
     }
-    
-    public static void release(FileChannel chan)
-    {
+
+    public static void release(FileChannel chan) {
         // Always close even if not managed.
-        try { chan.close() ; } catch (Exception ex) {}
+        try {
+            chan.close() ;
+        }
+        catch (Exception ex) {}
         String name = channel2name.remove(chan) ;
         if ( name != null )
             name2channel.remove(name) ;
     }
-    
-    public static void reset()
-    {
+
+    public static void reset() {
         releaseAll(null) ;
     }
     
-    /** Shutdown all the files matching the prefix (typically a directory) */  
-    public static void releaseAll(String prefix)
-    {
+    /** Shutdown all the files matching the prefix (typically a directory) */
+    public static void releaseAll(String prefix) {
         // Use an iterator explicitly so we can remove from the map.
         List<FileChannel> x = new ArrayList<>() ;
-        for ( String fn : name2channel.keySet() )
-        {
-            if ( prefix == null || fn.startsWith(prefix) )
-            {
+        for ( String fn : name2channel.keySet() ) {
+            if ( prefix == null || fn.startsWith(prefix) ) {
                 x.add(name2channel.get(fn)) ;
                 // Don't call release here - potential CME problems.
                 // Could use an explicit iterator on teh keySet and .remove from that but
@@ -121,4 +112,3 @@ public class ChannelManager
             release(chan) ;
     }
 }
-

http://git-wip-us.apache.org/repos/asf/jena/blob/10f2bb16/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionCoordinator.java
----------------------------------------------------------------------
diff --git a/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionCoordinator.java b/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionCoordinator.java
index 56104bf..cede020 100644
--- a/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionCoordinator.java
+++ b/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionCoordinator.java
@@ -465,7 +465,6 @@ public class TransactionCoordinator {
                 throw new TransactionException("Writers currently being rejected");
             }
         }
-            
         
         if ( canBlock )
             exclusivitylock.readLock().lock() ;


[22/50] [abbrv] jena git commit: Remove SSE node table support in favour of binary node table.

Posted by an...@apache.org.
Remove SSE node table support in favour of binary node table.

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

Branch: refs/heads/jena-tdb2
Commit: 73e2ccedfc9b54aa3baad9e8fd64ef075b6a03a6
Parents: 67b69b5
Author: Andy Seaborne <an...@seaborne.org>
Authored: Wed Sep 13 14:49:00 2017 +0100
Committer: Andy Seaborne <an...@seaborne.org>
Committed: Wed Sep 13 14:49:00 2017 +0100

----------------------------------------------------------------------
 .../tdb2/store/nodetable/NodeTableSSE.java      | 304 -------------------
 1 file changed, 304 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/73e2cced/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableSSE.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableSSE.java b/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableSSE.java
deleted file mode 100644
index ed4577f..0000000
--- a/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableSSE.java
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- *  Licensed 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.
- *
- *  See the NOTICE file distributed with this work for additional
- *  information regarding copyright ownership.
- */
-
-package org.seaborne.tdb2.store.nodetable;
-
-import java.nio.ByteBuffer ;
-
-import org.apache.jena.atlas.io.BlockUTF8 ;
-import org.apache.jena.atlas.lib.StrUtils ;
-import org.apache.jena.graph.Node ;
-import org.apache.jena.graph.NodeFactory ;
-import org.apache.jena.riot.RiotException ;
-import org.apache.jena.riot.out.NodeFmtLib ;
-import org.apache.jena.riot.system.PrefixMap ;
-import org.apache.jena.riot.system.PrefixMapNull ;
-import org.apache.jena.riot.tokens.Token ;
-import org.apache.jena.riot.tokens.Tokenizer ;
-import org.apache.jena.riot.tokens.TokenizerFactory ;
-import org.apache.jena.riot.web.LangTag ;
-import org.apache.jena.shared.PrefixMapping ;
-import org.apache.jena.sparql.util.NodeUtils ;
-import org.seaborne.dboe.base.block.Block ;
-import org.seaborne.dboe.base.file.BufferChannel ;
-import org.seaborne.dboe.base.file.BufferChannelFile ;
-import org.seaborne.dboe.base.objectfile.ObjectFile ;
-import org.seaborne.dboe.base.objectfile.ObjectFileStorage ;
-import org.seaborne.dboe.index.Index ;
-import org.seaborne.tdb2.TDBException ;
-import org.seaborne.tdb2.store.NodeId ;
-import org.seaborne.tdb2.store.NodeIdFactory;
-
-// For reference only.
-public class NodeTableSSE extends NodeTableNative {
-
-    private long position ;
-    private ObjectFile objects ;
-    //private final Index nodeToId ;
-    
-    private /*public*/ NodeTableSSE(Index nodeToId, String filename)
-    {
-        super(nodeToId);
-        BufferChannel file = BufferChannelFile.create(filename) ;
-        this.objects = new ObjectFileStorage(file) ;
-    }
-    
-    @Override
-    protected final NodeId writeNodeToTable(Node node)
-    {
-        // Synchronized in accessIndex
-        long x = encodeStore(node, objects) ;
-        // Paired : [*]
-        return NodeIdFactory.createPtr(x);
-    }
-
-    @Override
-    protected final Node readNodeFromTable(NodeId id)
-    {
-        // Paired : [*]
-        long x = id.getPtrLocation();
-        if ( x >= objects.length() )
-            return null ;
-        return fetchDecode(x, objects) ;
-    }
-    
-    private static Nodec nodec = new NodecSSE() ;
-    
-    private static long encodeStore(Node node, ObjectFile file)
-    {
-        // Buffer pool?
-        
-        // Nodes can be writtern during reads.
-        // Make sure this operation is sync'ed. 
-        int maxSize = nodec.maxSize(node) ;
-        Block block = file.allocWrite(maxSize) ;
-        try {
-            int len = nodec.encode(node, block.getByteBuffer(), null) ;
-            file.completeWrite(block) ;
-            return block.getId() ;
-        } catch (TDBException ex)
-        {
-            file.abortWrite(block) ;
-            throw ex ;
-        }
-    }
-    
-    private static Node fetchDecode(long id, ObjectFile file)
-    {
-        ByteBuffer bb = file.read(id) ;
-        if ( bb == null )
-            return null ;
-        return decode(bb) ;
-    }
-    
-    /**
-     * Encode a node - it is better to use encodeStore which may avoid
-     * anadditional copy in getting the node into the ObjectFile
-     */
-    public static ByteBuffer encode(Node node)
-    {
-        int maxSize = nodec.maxSize(node) ;
-        ByteBuffer bb = ByteBuffer.allocate(maxSize) ;
-        int len = nodec.encode(node, bb, null) ;
-        bb.limit(len) ;
-        bb.position(0) ;
-        return bb ;
-    }
-    
-    /**
-     * Decode a node - it is better to use fetchDecode which may avoid an
-     * additional copy in getting the node from the ObjectFile.
-     */
-    public static Node decode(ByteBuffer bb)
-    {
-        bb.position(0) ;
-        Node n = nodec.decode(bb, null) ;
-        return n ;
-    }
-
-    @Override
-    protected void syncSub() { objects.sync(); }
-
-    @Override
-    protected void closeSub() { objects.close(); }
-
-    /** Encode/decode for Nodes into bytes */
-    public interface Nodec {
-        /**
-         * Calculate the maximum number of bytes needed for a Node. This needs to be
-         * an overestimate and is used to ensure there is space in the bytebuffer
-         * passed to encode.
-         */
-        public int maxSize(Node node) ;
-
-        /**
-         * Encode the node into the byte buffer, starting at the given offset. The
-         * ByteBuffer will have position/limit around the space used on return,
-         * <b>without a length code<b>.
-         * 
-         * @param node Node to encode.
-         * @param bb ByteBuffer
-         * @param pmap Optional prefix mapping. Can be null.
-         * @return Length of byte buffer used for the whole encoding.
-         */
-        public int encode(Node node, ByteBuffer bb, PrefixMapping pmap) ;
-
-        /**
-         * Decode the node from the byte buffer. The ByteBuffer position should be
-         * the start of the encoding (no binary length for example)
-         * 
-         * @param bb ByteBuffer
-         * @param pmap Optional prefix mapping. Can be null.
-         * @return the decoded Node.
-         */
-        public Node decode(ByteBuffer bb, PrefixMapping pmap) ;
-    }
-    
-    private static class NodecSSE implements Nodec
-    {
-        private static boolean SafeChars = false ;
-        // Characters in IRIs that are illegal and cause SSE problems, but we wish to keep.
-        final private static char MarkerChar = '_' ;
-        final private static char[] invalidIRIChars = { MarkerChar , ' ' } ; 
-        
-        public NodecSSE() {}
-        
-        @Override
-        public int maxSize(Node node)
-        {
-            return maxLength(node) ;
-        }
-
-        private static final PrefixMap pmap0 = PrefixMapNull.empty ;
-        private static final boolean onlySafeBNodeLabels = false ;
-        @Override
-        public int encode(Node node, ByteBuffer bb, PrefixMapping pmap)
-        {
-            String str = null ;
-
-            if ( node.isURI() ) 
-            {
-                // Pesky spaces etc
-                String x = StrUtils.encodeHex(node.getURI(), MarkerChar, invalidIRIChars) ;
-                if ( x != node.getURI() )
-                    node = NodeFactory.createURI(x) ; 
-            }
-            
-            if ( node.isLiteral() && NodeUtils.isLangString(node) )
-            {
-                // Check syntactically valid.
-                String lang = node.getLiteralLanguage() ;
-                if ( ! LangTag.check(lang) )
-                    throw new TDBException("bad language tag: "+node) ;
-            }
-            
-            if ( node.isBlank() && ! onlySafeBNodeLabels ) {
-                // Special case.
-                str = "_:"+node.getBlankNodeLabel() ;
-            }
-            
-            // Node->String
-            if ( str == null )
-                str = NodeFmtLib.str(node, (String)null, pmap0) ;
-            // String -> bytes ;
-            BlockUTF8.fromChars(str, bb) ;
-            bb.flip() ;
-            return bb.limit() ;
-        }
-
-        @Override
-        public Node decode(ByteBuffer bb, PrefixMapping pmap)
-        {
-            // Ideally, this would be straight from the byte buffer.
-            // But currently we go bytes -> string -> node 
-
-            // Byte -> String
-            String str = BlockUTF8.toString(bb) ;
-            //OLD  
-            //String str = Bytes.fromByteBuffer(bb) ;
-            // String -> Node
-            
-            // Easy cases.
-            if ( str.startsWith("_:") )   
-            {
-                // Must be done this way.
-                // In particular, bnode labels can contain ":" from Jena
-                // TokenizerText does not recognize these.
-                str = str.substring(2) ;
-                return NodeFactory.createBlankNode(str) ;
-            }
-
-            if ( str.startsWith("<") )
-            {
-                // Do directly.
-                // (is it quicker?)
-                str = str.substring(1,str.length()-1) ;
-                str = StrUtils.unescapeString(str) ;
-                str = StrUtils.decodeHex(str, MarkerChar) ;
-                return NodeFactory.createURI(str) ;
-            }
-
-            Tokenizer tokenizer = TokenizerFactory.makeTokenizerString(str) ;
-            if ( ! tokenizer.hasNext() )
-                throw new TDBException("Failed to tokenise: "+str) ;
-            Token t = tokenizer.next() ;
-
-            try {
-                Node n = t.asNode() ;
-                if ( n == null ) throw new TDBException("Not a node: "+str) ;
-                return n ;
-            } catch (RiotException ex)
-            {
-                throw new TDBException("Bad string for node: "+str) ;
-            }
-        }
-
-        // Over-estimate the length of the encoding.
-        private static int maxLength(Node node)
-        {
-            if ( node.isBlank() )
-                // "_:"
-                return 2+maxLength(node.getBlankNodeLabel()) ;    
-            if ( node.isURI() )
-                // "<>"
-                return 2+maxLength(node.getURI()) ;
-            if ( node.isLiteral() )
-            {
-                int len = 2+maxLength(node.getLiteralLexicalForm()) ;
-                if ( NodeUtils.isLangString(node) )
-                    // Space for @ (language tag is ASCII)
-                    len = len + 3 + node.getLiteralLanguage().length() ;
-                else if ( ! NodeUtils.isSimpleString(node) )
-                    // The quotes and also space for ^^<>
-                    len = len + 4 + maxLength(node.getLiteralDatatypeURI()) ;
-                return len ;
-            }
-            if ( node.isVariable() )
-                // "?"
-                return 1+maxLength(node.getName()) ;
-            throw new TDBException("Unrecognized node type: "+node) ;
-        }
-
-        private static int maxLength(String string)
-        {
-            // Very worse case for UTF-8 - and then some.
-            // Encoding every character as _XX or bad UTF-8 conversion (3 bytes)
-            // Max 3 bytes UTF-8 for up to 10FFFF (NB Java treats above 16bites as surrogate pairs only). 
-            return string.length()*3 ;
-        }
-    }
- }
\ No newline at end of file


[32/50] [abbrv] jena git commit: JENA-1396: Merge //github/afs/mantis as subdirectory jena-db/

Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/StageMatchTuple.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/StageMatchTuple.java
index 0000000,0000000..aa5103d
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/StageMatchTuple.java
@@@ -1,0 -1,0 +1,195 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.solver;
++
++import java.util.Iterator ;
++import java.util.List ;
++import java.util.function.Function ;
++import java.util.function.Predicate ;
++
++import org.apache.jena.atlas.iterator.Iter ;
++import org.apache.jena.atlas.iterator.RepeatApplyIterator ;
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.atlas.lib.tuple.TupleFactory ;
++import org.apache.jena.graph.Node ;
++import org.apache.jena.sparql.core.Var ;
++import org.apache.jena.sparql.engine.ExecutionContext ;
++import org.seaborne.tdb2.store.NodeId ;
++import org.seaborne.tdb2.store.nodetable.NodeTable ;
++import org.seaborne.tdb2.store.nodetupletable.NodeTupleTable ;
++
++public class StageMatchTuple extends RepeatApplyIterator<BindingNodeId>
++{
++    private final NodeTupleTable nodeTupleTable ;
++    private final Tuple<Node> patternTuple ;
++
++    private final ExecutionContext execCxt ;
++    private boolean anyGraphs ;
++    private Predicate<Tuple<NodeId>> filter ;
++
++    public StageMatchTuple(NodeTupleTable nodeTupleTable, Iterator<BindingNodeId> input, 
++                            Tuple<Node> tuple, boolean anyGraphs, 
++                            Predicate<Tuple<NodeId>> filter, 
++                            ExecutionContext execCxt)
++    {
++        super(input) ;
++        this.filter = filter ;
++        this.nodeTupleTable = nodeTupleTable ; 
++        this.patternTuple = tuple ;
++        this.execCxt = execCxt ;
++        this.anyGraphs = anyGraphs ; 
++    }
++
++    /** Prepare a pattern (tuple of nodes), and an existing binding of NodeId, into NodeIds and Variables. 
++     *  A variable in the pattern is replaced by its binding or null in the Nodeids.
++     *  A variable that is not bound by the binding is placed in the var array.
++     *  Return false if preparation detechs the pattern can not match. 
++     */
++    public static boolean prepare(NodeTable nodeTable, Tuple<Node> patternTuple, BindingNodeId input, NodeId ids[], Var[] var)
++    {
++        // Process the Node to NodeId conversion ourselves because
++        // we wish to abort if an unknown node is seen.
++        for ( int i = 0 ; i < patternTuple.len() ; i++ )
++        {
++            Node n = patternTuple.get(i) ;
++            // Substitution and turning into NodeIds
++            // Variables unsubstituted are null NodeIds
++            NodeId nId = idFor(nodeTable, input, n) ;
++            if ( NodeId.isDoesNotExist(nId) )
++                return false;
++            ids[i] = nId ;
++            if ( nId == null )
++                var[i] = asVar(n) ;
++        }
++        return true ;
++    }
++    
++    @Override
++    protected Iterator<BindingNodeId> makeNextStage(final BindingNodeId input)
++    {
++        // ---- Convert to NodeIds 
++        NodeId ids[] = new NodeId[patternTuple.len()] ;
++        // Variables for this tuple after subsitution
++        final Var[] var = new Var[patternTuple.len()] ;
++
++        boolean b = prepare(nodeTupleTable.getNodeTable(), patternTuple, input, ids, var) ;
++        if ( !b )
++            // Short cut - known unknown NodeId
++            return Iter.nullIterator(); 
++        
++        Iterator<Tuple<NodeId>> iterMatches = nodeTupleTable.find(TupleFactory.create(ids)) ;  
++        
++        // ** Allow a triple or quad filter here.
++        if ( filter != null )
++            iterMatches = Iter.filter(iterMatches, filter) ;
++        
++        // If we want to reduce to RDF semantics over quads,
++        // we need to reduce the quads to unique triples. 
++        // We do that by having the graph slot as "any", then running
++        // through a distinct-ifier. 
++        // Assumes quads are GSPO - zaps the first slot.
++        // Assumes that tuples are not shared.
++        if ( anyGraphs )
++        {
++            iterMatches = Iter.map(iterMatches, quadsToAnyTriples) ;
++            //Guaranteed 
++            //iterMatches = Iter.distinct(iterMatches) ;
++            
++            // This depends on the way indexes are chosen and
++            // the indexing pattern. It assumes that the index 
++            // chosen ends in G so same triples are adjacent 
++            // in a union query.
++            // 
++            // If any slot is defined, then the index will be X??G.
++            // If no slot is defined, then the index will be ???G.
++            // But the  TupleTable
++            //  See TupleTable.scanAllIndex that ensures the latter.
++            //  No G part way through.
++            iterMatches = Iter.distinctAdjacent(iterMatches) ;
++        }
++        
++        // Map Tuple<NodeId> to BindingNodeId
++        Function<Tuple<NodeId>, BindingNodeId> binder = tuple -> 
++            {
++                BindingNodeId output = new BindingNodeId(input) ;
++                for ( int i = 0 ; i < var.length ; i++ )
++                {
++                    Var v = var[i] ;
++                    if ( v == null )
++                        continue ;
++                    NodeId id = tuple.get(i) ;
++                    if ( reject(output, v, id) )
++                        return null ;
++                    output.put(v, id) ;
++                }
++                return output ;
++        } ;
++        
++        return Iter.iter(iterMatches).map(binder).removeNulls() ;
++    }
++    
++    private static Iterator<Tuple<NodeId>> print(Iterator<Tuple<NodeId>> iter)
++    {
++        if ( ! iter.hasNext() )
++            System.err.println("<empty>") ;
++        else
++        {
++            List<Tuple<NodeId>> r = Iter.toList(iter) ;
++            String str = Iter.asString(r, "\n") ;
++            System.err.println(str) ;
++            // Reset iter
++            iter = Iter.iter(r) ;
++        }
++        return iter ;
++    }
++    
++    private static boolean reject(BindingNodeId output , Var var, NodeId value)
++    {
++        if ( ! output.containsKey(var) )
++            return false ;
++        
++        if ( output.get(var).equals(value) )
++            return false ;
++
++        return true ;
++    }
++    
++    private static Var asVar(Node node)
++    {
++        if ( Var.isVar(node) )
++            return Var.alloc(node) ;
++        return null ;
++    }
++
++    /** Return null for variables, and for nodes, the node id or NodeDoesNotExist */
++    private static NodeId idFor(NodeTable nodeTable, BindingNodeId input, Node node)
++    {
++        if ( Var.isVar(node) )
++        {
++            NodeId n = input.get((Var.alloc(node))) ;
++            // Bound to NodeId or null. 
++            return n ;
++        } 
++        // May return NodeId.NodeDoesNotExist which must not be null. 
++        return nodeTable.getNodeIdForNode(node) ;
++    }
++    
++    private static Function<Tuple<NodeId>, Tuple<NodeId>> quadsToAnyTriples = item -> {
++        return TupleFactory.create4(NodeId.NodeIdAny, item.get(1), item.get(2), item.get(3) ) ;
++    } ;
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/stats/Stats.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/stats/Stats.java
index 0000000,0000000..c1b59b0
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/stats/Stats.java
@@@ -1,0 -1,0 +1,137 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.solver.stats;
++
++import static org.apache.jena.sparql.sse.Item.addPair ;
++import static org.apache.jena.sparql.sse.Item.createTagged ;
++
++import java.io.BufferedOutputStream ;
++import java.io.FileOutputStream ;
++import java.io.IOException ;
++import java.io.OutputStream ;
++import java.util.Iterator ;
++import java.util.Map ;
++import java.util.Map.Entry ;
++
++import org.apache.jena.atlas.lib.DateTimeUtils ;
++import org.apache.jena.atlas.logging.Log ;
++import org.apache.jena.graph.Graph ;
++import org.apache.jena.graph.Node ;
++import org.apache.jena.graph.Triple ;
++import org.apache.jena.sparql.engine.optimizer.StatsMatcher ;
++import org.apache.jena.sparql.graph.NodeConst ;
++import org.apache.jena.sparql.sse.Item ;
++import org.apache.jena.sparql.sse.ItemList ;
++import org.apache.jena.sparql.sse.ItemWriter ;
++import org.apache.jena.sparql.util.NodeFactoryExtra ;
++
++public class Stats
++{
++    static Item ZERO = Item.createNode(NodeFactoryExtra.intToNode(0)) ;
++
++    /** Write statistics */
++    static public void write(String filename, StatsResults stats)
++    {
++        write(filename, stats.getPredicates(), stats.getTypes(), stats.getCount()) ;
++    }
++    
++    /** Write statistics */
++    static public void write(OutputStream output, StatsResults stats)
++    {
++        write(output, stats.getPredicates(), stats.getTypes(), stats.getCount()) ;
++    }
++    
++    static private void write(String filename, Map<Node, Integer> predicateStats, Map<Node, Integer> typeStats, long statsTotal)
++    {
++        // Write out the stats
++        try (OutputStream statsOut = new BufferedOutputStream(new FileOutputStream(filename))) {
++            write(statsOut, predicateStats, typeStats, statsTotal) ;
++        } catch (IOException ex)
++        { Log.warn(Stats.class, "Problem when writing stats file", ex) ; }
++    }
++    
++    static private void write(OutputStream output, Map<Node, Integer> predicateStats, Map<Node, Integer> typeStats, long statsTotal)
++    {
++        Item item = format(predicateStats, typeStats, statsTotal) ;
++        ItemWriter.write(output, item) ;
++    }
++    
++
++    /** Gather statistics, any graph */
++    public static StatsCollector gather(Graph graph)
++    {
++        StatsCollector stats = new StatsCollector() ;
++    
++        Iterator<Triple> iter = graph.find(Node.ANY, Node.ANY, Node.ANY) ;
++        for ( ; iter.hasNext() ; )
++        {
++            Triple t = iter.next();
++            stats.record(null, t.getSubject(), t.getPredicate(), t.getObject()) ;
++        }
++        
++        return stats ;
++    }
++
++    public static Item format(StatsResults stats)
++    {
++        return format(stats.getPredicates(), stats.getTypes(), stats.getCount()) ;
++    }
++    
++    private static Item format(Map<Node, Integer> predicates, Map<Node, Integer> types, long count)
++    {
++        Item stats = Item.createList() ;
++        ItemList statsList = stats.getList() ;
++        statsList.add("stats") ;
++
++        Item meta = createTagged(StatsMatcher.META) ;
++        addPair(meta.getList(), "timestamp", NodeFactoryExtra.nowAsDateTime()) ;
++        addPair(meta.getList(), "run@",  DateTimeUtils.nowAsString()) ;
++        if ( count >= 0 )
++            addPair(meta.getList(), StatsMatcher.COUNT, NodeFactoryExtra.intToNode((int)count)) ;
++        statsList.add(meta) ;
++        
++        for ( Entry<Node, Integer> entry : types.entrySet() )
++        {
++            Node type = entry.getKey() ;
++            addTypeTriple(statsList, type, NodeFactoryExtra.intToNode(entry.getValue()) ) ;
++        }
++        
++        for ( Entry<Node, Integer> entry : predicates.entrySet() )
++        {
++            Node node = entry.getKey() ;
++            // Skip these - they just clog things up!
++            if ( node.getURI().startsWith("http://www.w3.org/1999/02/22-rdf-syntax-ns#_") )
++                continue ;
++            addPair(statsList, node, NodeFactoryExtra.intToNode(entry.getValue())) ;
++        }
++        
++        // Add a default rule.
++        addPair(statsList, StatsMatcher.OTHER, ZERO) ;
++        
++        return stats ;
++    }
++
++    private static void addTypeTriple(ItemList statsList, Node type, Node intCount)
++    {
++        ItemList triple = new ItemList() ;
++        triple.add("VAR") ;
++        triple.add(NodeConst.nodeRDFType) ;
++        triple.add(type) ;
++        addPair(statsList, Item.createList(triple), Item.createNode(intCount)) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/stats/StatsCollector.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/stats/StatsCollector.java
index 0000000,0000000..0766cc3
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/stats/StatsCollector.java
@@@ -1,0 -1,0 +1,35 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.solver.stats;
++
++import java.util.Map ;
++
++import org.apache.jena.graph.Node ;
++import org.apache.jena.sparql.graph.NodeConst ;
++
++/** Statistics collector, general purpose, uses Nodes */
++public class StatsCollector extends StatsCollectorBase<Node>
++{
++    public StatsCollector() { super(NodeConst.nodeRDFType) ; }
++
++    @Override
++    protected Map<Node, Integer> convert(Map<Node, Integer> map)
++    {
++        return map ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/stats/StatsCollectorBase.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/stats/StatsCollectorBase.java
index 0000000,0000000..9722e82
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/stats/StatsCollectorBase.java
@@@ -1,0 -1,0 +1,53 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.solver.stats;
++
++
++import java.util.HashMap ;
++import java.util.Map ;
++
++import org.apache.jena.graph.Node ;
++
++/** Statistics collector, general purpose */
++abstract class StatsCollectorBase<T>
++{
++    private long count = 0 ;
++    private Map<T, Integer> predicates = new HashMap<>(10000) ;
++    private Map<T, Integer> types = new HashMap<>(10000) ;
++    private T typeTrigger ;
++    
++    protected StatsCollectorBase(T typeTrigger)
++    {
++        this.typeTrigger = typeTrigger ;
++    }
++
++    public void record(T g, T s, T p, T o)
++    {
++        count++ ;
++		predicates.put(p, predicates.getOrDefault(p, 0) + 1);
++        if ( typeTrigger != null && typeTrigger.equals(p) )
++        		types.put(o, types.getOrDefault(o, 0) + 1);
++    }
++
++    protected abstract Map<Node, Integer> convert(Map<T, Integer> map) ;
++    
++    public StatsResults results()
++    {
++        return new StatsResults(convert(predicates), convert(types), count) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/stats/StatsCollectorNodeId.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/stats/StatsCollectorNodeId.java
index 0000000,0000000..6b8ee95
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/stats/StatsCollectorNodeId.java
@@@ -1,0 -1,0 +1,55 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.solver.stats;
++
++import java.util.HashMap ;
++import java.util.Map ;
++
++import org.apache.jena.graph.Node ;
++import org.apache.jena.sparql.graph.NodeConst ;
++import org.seaborne.tdb2.store.NodeId ;
++import org.seaborne.tdb2.store.nodetable.NodeTable ;
++
++/** Statistics collector, aggregates based on NodeId */
++public class StatsCollectorNodeId extends StatsCollectorBase<NodeId>
++{
++    private NodeTable nodeTable ;
++    
++    public StatsCollectorNodeId(NodeTable nodeTable)
++    {
++        super(findRDFType(nodeTable)) ;
++        this.nodeTable = nodeTable ;
++    }
++    
++    private static NodeId findRDFType(NodeTable nodeTable2)
++    {
++        return nodeTable2.getAllocateNodeId(NodeConst.nodeRDFType) ;
++    }
++
++    @Override
++    protected Map<Node, Integer> convert(Map<NodeId, Integer> stats)
++    {
++        Map<Node, Integer> statsNodes = new HashMap<>(1000) ;
++        for ( NodeId p : stats.keySet() )
++        {
++            Node n = nodeTable.getNodeForNodeId(p) ;
++            statsNodes.put(n, stats.get(p)) ;
++        }
++        return statsNodes ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/stats/StatsResults.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/stats/StatsResults.java
index 0000000,0000000..322d251
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/stats/StatsResults.java
@@@ -1,0 -1,0 +1,52 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.solver.stats;
++
++import java.util.Map ;
++
++import org.apache.jena.graph.Node ;
++
++/** Statistics collector */
++public class StatsResults 
++{
++    private final Map<Node, Integer> predicates ;
++    private final Map<Node, Integer> types ;
++    private final long count ;
++
++    StatsResults(Map<Node, Integer> predicates, Map<Node, Integer> types, long count)
++    {
++        this.count = count ;
++        this.predicates = predicates ;
++        this.types = types ;
++    }
++
++    public Map<Node, Integer> getPredicates()
++    {
++        return predicates ;
++    }
++
++    public Map<Node, Integer> getTypes()
++    {
++        return types ;
++    }
++
++    public long getCount()
++    {
++        return count ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetGraphSwitchable.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetGraphSwitchable.java
index 0000000,0000000..9469a1d
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetGraphSwitchable.java
@@@ -1,0 -1,0 +1,203 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store;
++
++import java.nio.file.Path;
++import java.util.Map;
++import java.util.concurrent.atomic.AtomicReference;
++
++import org.apache.jena.atlas.lib.Cache ;
++import org.apache.jena.atlas.lib.CacheFactory ;
++import org.apache.jena.graph.Graph;
++import org.apache.jena.graph.Node;
++import org.apache.jena.shared.PrefixMapping;
++import org.apache.jena.shared.impl.PrefixMappingImpl;
++import org.apache.jena.sparql.core.DatasetGraph ;
++import org.apache.jena.sparql.core.DatasetPrefixStorage ;
++import org.seaborne.dboe.base.file.Location ;
++
++final
++public class DatasetGraphSwitchable extends DatasetGraphWrapperTxn /* Until ARQ catches up with promote */ 
++{
++    // QueryEngineFactoryWrapper has a QueryEngineFactory that is always loaded that
++    // executes on the unwrapped DSG (recursively). Unwrapping is via getBase, calling
++    // getWrapped() which is implemented with get().
++    
++//    static { 
++//        // QueryEngineRegistry.addFactory(factory());
++//    }
++    
++    private final AtomicReference<DatasetGraph> dsgx = new AtomicReference<>();
++    // Null for in-memory datasets.
++    private final Path basePath;
++    private final Location location ;
++    
++    public DatasetGraphSwitchable(Path base, Location location, DatasetGraph dsg) {
++        // Don't use the slot in datasetGraphWrapper - use the AtomicReference
++        super(null) ;
++        dsgx.set(dsg);
++        this.basePath = base;
++        this.location = location; 
++    }
++
++    /** Is this {@code DatasetGraphSwitchable} just a holder for a {@code DatasetGraph}?
++     *  If so, it does not have a location on disk.
++     */
++    public boolean hasContainerPath() { return basePath != null; } 
++    
++    public Path getContainerPath() { return basePath; }
++    
++    public Location getLocation() { return location; }
++
++    /** The dataset to use for redirection - can be overridden.
++     *  It is also guaranteed that this is called only once per
++     *  delegated call.  Changes to the wrapped object can be
++     *  made based on that contract.
++     */
++    @Override
++    public DatasetGraph get() { return dsgx.get(); }
++
++    /** Set the base {@link DatasetGraph}.
++     * Returns the old value.
++     */ 
++    public DatasetGraph set(DatasetGraph dsg) { 
++        return dsgx.getAndSet(dsg);
++    }
++    
++    /** If and only if the current value is the given old value, set the base {@link DatasetGraph}  
++     * Returns true if a swap happened.
++     */ 
++    public boolean change(DatasetGraph oldDSG, DatasetGraph newDSG) { 
++        // No need to clear. ngCache.clear();
++        return dsgx.compareAndSet(oldDSG, newDSG);
++    }
++
++    private Graph dftGraph = GraphViewSwitchable.createDefaultGraph(this);
++    
++    @Override
++    public Graph getDefaultGraph() {
++        return dftGraph;
++    }
++    
++//    private Cache<Node, Graph> ngCache = CacheFactory.createCache(10);
++    private Cache<Node, Graph> ngCache = CacheFactory.createOneSlotCache();
++    
++    @Override
++    public Graph getGraph(Node gn) {
++        return ngCache.getOrFill(gn, ()->GraphViewSwitchable.createNamedGraph(this, gn));
++    }
++
++    // TDB2 specific.
++    // Does not cope with blank nodes.
++    // A PrefixMapping sending operations via the switchable.
++    private PrefixMapping prefixMapping(Node graphName) {
++        
++        String gn = (graphName == null) ? "" : graphName.getURI(); 
++        
++        return new PrefixMappingImpl() {
++            
++            DatasetPrefixStorage dps() {
++                return ((DatasetGraphTDB)dsgx.get()).getPrefixes();
++            }
++            
++            Graph graph() {
++                DatasetGraphTDB dsg = (DatasetGraphTDB)dsgx.get();
++                if ( gn == null )
++                    return dsg.getDefaultGraph();
++                else
++                    return dsg.getGraph(graphName);
++            }
++            
++            PrefixMapping prefixMapping() {
++                if ( gn == null )
++                    return dps().getPrefixMapping();
++                else
++                    return dps().getPrefixMapping(gn); 
++            }
++
++            @Override
++            protected void set(String prefix, String uri) {
++                dps().insertPrefix(gn, prefix, uri);
++                super.set(prefix, uri);
++            }
++
++            @Override
++            protected String get(String prefix) {
++                return dps().readPrefix(gn, prefix);
++            }
++
++            @Override
++            protected void remove(String prefix) {
++                dps().getPrefixMapping().removeNsPrefix(prefix);
++                super.remove(prefix);
++            }
++            
++            @Override
++            public Map<String, String> getNsPrefixMap() {
++                return prefixMapping().getNsPrefixMap();
++                //return graph().getPrefixMapping().getNsPrefixMap();
++            }
++        };
++    }
++    
++    //static { register() ; }
++    
++    
++//    static QueryEngineFactory factory() {
++//        return new QueryEngineFactory() {
++//            @Override
++//            public boolean accept(Op op, DatasetGraph dataset, Context context) {
++//                DatasetGraphSwitchable dsg = extract(dataset) ;
++//                if ( dsg == null ) return false;
++//                QueryEngineFactory f = QueryEngineRegistry.findFactory(op, dsg.get(), context);
++//                return f.accept(op, dataset, context);
++//            }
++//
++//            @Override
++//            public Plan create(Op op, DatasetGraph dataset, Binding inputBinding, Context context) {
++//                DatasetGraphSwitchable dsg = extract(dataset) ;
++//                if ( dsg == null ) return null;
++//                QueryEngineFactory f = QueryEngineRegistry.findFactory(op, dsg.get(), context);
++//                return f.create(op, dataset, inputBinding, context);
++//            }
++//
++//            private DatasetGraphSwitchable extract(DatasetGraph dataset) {
++//                if ( dataset instanceof DatasetGraphSwitchable )
++//                    return (DatasetGraphSwitchable)dataset;
++//                return null;
++//            }
++//
++//            @Override
++//            public boolean accept(Query query, DatasetGraph dataset, Context context) {
++//                DatasetGraphSwitchable dsg = extract(dataset) ;
++//                if ( dsg == null ) return false;
++//                QueryEngineFactory f = QueryEngineRegistry.findFactory(query, dsg.get(), context);
++//                return f.accept(query, dataset, context);
++//            }
++//
++//            @Override
++//            public Plan create(Query query, DatasetGraph dataset, Binding inputBinding, Context context) {
++//                DatasetGraphSwitchable dsg = extract(dataset) ;
++//                if ( dsg == null ) return null;
++//                QueryEngineFactory f = QueryEngineRegistry.findFactory(query, dsg.get(), context);
++//                return f.create(query, dataset, inputBinding, context);
++//            }
++//        };
++//    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetGraphTDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetGraphTDB.java
index 0000000,0000000..5eb3053
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetGraphTDB.java
@@@ -1,0 -1,0 +1,480 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store;
++
++import static org.apache.jena.sparql.util.graph.GraphUtils.triples2quads ;
++
++import java.util.Iterator ;
++
++import org.apache.jena.atlas.iterator.Iter ;
++import org.apache.jena.atlas.lib.Closeable ;
++import org.apache.jena.atlas.lib.InternalErrorException ;
++import org.apache.jena.atlas.lib.Sync ;
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.graph.Graph ;
++import org.apache.jena.graph.GraphUtil ;
++import org.apache.jena.graph.Node ;
++import org.apache.jena.graph.Triple ;
++import org.apache.jena.query.ReadWrite ;
++import org.apache.jena.sparql.core.* ;
++import org.apache.jena.sparql.engine.optimizer.reorder.ReorderTransformation ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.dboe.transaction.TransactionalMonitor ;
++import org.seaborne.dboe.transaction.txn.Transaction ;
++import org.seaborne.dboe.transaction.txn.TransactionException ;
++import org.seaborne.dboe.transaction.txn.TransactionalSystem ;
++import org.seaborne.dboe.transaction.txn.TxnId;
++import org.seaborne.tdb2.TDBException ;
++import org.seaborne.tdb2.lib.NodeLib ;
++import org.seaborne.tdb2.setup.StoreParams ;
++import org.seaborne.tdb2.store.nodetupletable.NodeTupleTable ;
++import org.seaborne.tdb2.sys.StoreConnection ;
++/** This is the class that provides creates a dataset over the storage via
++ *  TripleTable, QuadTable and prefixes.
++ *  This is the coordination of the storage.
++ *  @see DatasetGraphTxn for the Transaction form.
++ */
++final
++public class DatasetGraphTDB extends DatasetGraphTriplesQuads
++                             implements DatasetGraphTxn, Sync, Closeable
++{
++    private StorageTDB storage; 
++//    // SWITCHING.
++//    private TripleTable tripleTable ;
++//    private QuadTable quadTable ;
++//    private DatasetPrefixStorage prefixes ;
++//    private Location location ;
++//    private StoreParams storeParams ;
++//    // SWITCHING.
++    private TransactionalSystem txnSystem ;
++    private final ReorderTransformation transform ;
++    
++    private GraphTDB defaultGraphTDB ;
++    private final boolean checkForChange = false ;
++    private boolean closed = false ;
++
++    /** Application should not create a {@code DatasetGraphTDB} directly */
++    public DatasetGraphTDB(TransactionalSystem txnSystem, 
++                           TripleTable tripleTable, QuadTable quadTable, DatasetPrefixStorage prefixes,
++                           ReorderTransformation transform, Location location, StoreParams params) {
++        reset(txnSystem, tripleTable, quadTable, prefixes, location, params) ;
++        this.transform = transform ;
++        this.defaultGraphTDB = getDefaultGraphTDB() ;
++    }
++
++    public void reset(TransactionalSystem txnSystem,
++                      TripleTable tripleTable, QuadTable quadTable, DatasetPrefixStorage 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();
++    }
++    
++    public QuadTable getQuadTable()         { checkNotClosed(); return storage.quadTable; }
++    public TripleTable getTripleTable()     { checkNotClosed(); return storage.tripleTable; }
++
++    /** Low level manipulation. */
++    public StorageTDB getStorage()              { return storage; }
++    /** Low level manipulation. 
++     * <b>Do not use unless in exclusive mode.</b>
++     */
++    public void setStorage(StorageTDB storage)  { this.storage = storage; }
++    
++    @Override
++    protected Iterator<Quad> findInDftGraph(Node s, Node p, Node o) {
++        checkNotClosed() ;
++        return isolate(triples2quadsDftGraph(getTripleTable().find(s, p, o))) ;
++    }
++
++    @Override
++    protected Iterator<Quad> findInSpecificNamedGraph(Node g, Node s, Node p, Node o) {
++        checkNotClosed();
++        return isolate(getQuadTable().find(g, s, p, o));
++    }
++
++    @Override
++    protected Iterator<Quad> findInAnyNamedGraphs(Node s, Node p, Node o) {
++        checkNotClosed();
++        return isolate(getQuadTable().find(Node.ANY, s, p, o));
++    }
++
++    protected Iterator<Quad> triples2quadsDftGraph(Iterator<Triple> iter)
++    { return isolate(triples2quads(Quad.defaultGraphIRI, iter)); }
++ 
++    private static final boolean CHECK_TXN = true; 
++    
++    private <T> Iterator<T> isolate(Iterator<T> iterator) {
++        if ( txnSystem.isInTransaction() ) {
++            TxnId txnId = txnSystem.getThreadTransaction().getTxnId();
++            // Add transaction protection.
++            return new IteratorTxnTracker<>(iterator, txnSystem, txnId);
++        }
++        // Risk the hidden arraylist is copied on growth.
++        return Iter.iterator(iterator);
++    }
++
++    @Override
++    protected void addToDftGraph(Node s, Node p, Node o) { 
++        checkNotClosed() ;
++        requireWriteTxn() ;
++        notifyAdd(null, s, p, o) ;
++        getTripleTable().add(s,p,o) ;
++    }
++
++    @Override
++    protected void addToNamedGraph(Node g, Node s, Node p, Node o) {
++        checkNotClosed() ;
++        requireWriteTxn() ;
++        notifyAdd(g, s, p, o) ;
++        getQuadTable().add(g, s, p, o) ; 
++    }
++
++    @Override
++    protected void deleteFromDftGraph(Node s, Node p, Node o) {
++        checkNotClosed() ;
++        requireWriteTxn() ;
++        notifyDelete(null, s, p, o) ;
++        getTripleTable().delete(s, p, o) ;
++    }
++
++    @Override
++    protected void deleteFromNamedGraph(Node g, Node s, Node p, Node o) {
++        checkNotClosed() ;
++        requireWriteTxn() ;
++        notifyDelete(g, s, p, o) ;
++        getQuadTable().delete(g, s, p, o) ;
++    }
++
++    // Promotion
++    private void requireWriteTxn() {
++        Transaction txn = txnSystem.getThreadTransaction() ;
++        if ( txn == null )
++            throw new TransactionException("Not in a transaction") ;
++            
++        if ( txn.isWriteTxn() )
++            return ;
++        // Transaction.promoteOrException
++        boolean b = txn.promote() ;
++        if ( !b )
++            throw new TransactionException("Can't write") ;
++    }
++
++    // TODO ?? Optimize by integrating with add/delete operations.
++    private final void notifyAdd(Node g, Node s, Node p, Node o) {
++        if ( monitor == null )
++            return ;
++        QuadAction action = QuadAction.ADD ;
++        if ( checkForChange ) {
++            if ( contains(g,s,p,o) )
++                action = QuadAction.NO_ADD ;
++        }
++        monitor.change(action, g, s, p, o);
++    }
++
++    private final void notifyDelete(Node g, Node s, Node p, Node o) {
++        if ( monitor == null )
++            return ;
++        QuadAction action = QuadAction.DELETE ;
++        if ( checkForChange ) {
++            if ( ! contains(g,s,p,o) )
++                action = QuadAction.NO_DELETE ;
++        }
++        monitor.change(action, g, s, p, o);
++    }
++    
++    /** No-op. There is no need to close datasets.
++     *  Use {@link StoreConnection#release(Location)}.
++     *  (Datasets can not be reopened on MS Windows). 
++     */
++    @Override
++    public void close() {
++        if ( closed )
++            return ;
++        closed = true ;
++    }
++    
++    private void checkNotClosed() {
++        if ( closed )
++            throw new TDBException("dataset closed") ;
++    }
++    
++    /** Release resources.
++     *  Do not call directly - this is called from StoreConnection.
++     *  Use {@link StoreConnection#release(Location)}. 
++     */
++    public void shutdown() {
++        close();
++        storage.tripleTable.close() ;
++        storage.quadTable.close() ;
++        storage.prefixes.close();
++        txnSystem.getTxnMgr().shutdown(); 
++    }
++    
++    @Override
++    // Empty graphs don't "exist" 
++    public boolean containsGraph(Node graphNode) {
++        checkNotClosed() ; 
++        if ( Quad.isDefaultGraphExplicit(graphNode) || Quad.isUnionGraph(graphNode)  )
++            return true ;
++        return _containsGraph(graphNode) ; 
++    }
++
++    private boolean _containsGraph(Node graphNode) {
++        // Have to look explicitly, which is a bit of a nuisance.
++        // But does not normally happen for GRAPH <g> because that's rewritten to quads.
++        // Only pattern with complex paths go via GRAPH. 
++        Iterator<Tuple<NodeId>> x = storage.quadTable.getNodeTupleTable().findAsNodeIds(graphNode, null, null, null) ;
++        if ( x == null )
++            return false ; 
++        boolean result = x.hasNext() ;
++        return result ;
++    }
++    
++    @Override
++    public void addGraph(Node graphName, Graph graph) {
++        checkNotClosed() ; 
++        removeGraph(graphName) ;
++        GraphUtil.addInto(getGraph(graphName), graph) ;
++    }
++
++    @Override
++    public final void removeGraph(Node graphName) {
++        checkNotClosed() ; 
++        deleteAny(graphName, Node.ANY, Node.ANY, Node.ANY) ;
++    }
++
++    @Override
++    public Graph getDefaultGraph() {
++        checkNotClosed() ; 
++        return new GraphTDB(this, null) ; 
++    }
++
++    public GraphTDB getDefaultGraphTDB() {
++        checkNotClosed();
++        return (GraphTDB)getDefaultGraph();
++    }
++
++    @Override
++    public Graph getGraph(Node graphNode) {
++        checkNotClosed();
++        return new GraphTDB(this, graphNode);
++    }
++
++    public GraphTDB getGraphTDB(Node graphNode) {
++        checkNotClosed();
++        return (GraphTDB)getGraph(graphNode);
++    }
++
++    public StoreParams getStoreParams() {
++        checkNotClosed();
++        return storage.storeParams;
++    }
++
++    public ReorderTransformation getReorderTransform() {
++        checkNotClosed();
++        return transform;
++    }
++
++    public DatasetPrefixStorage getPrefixes() {
++        checkNotClosed();
++        return storage.prefixes;
++    }
++
++    @Override
++    public Iterator<Node> listGraphNodes() {
++        checkNotClosed();
++        Iterator<Tuple<NodeId>> x = storage.quadTable.getNodeTupleTable().findAll();
++        Iterator<NodeId> z = Iter.iter(x).map(t -> t.get(0)).distinct();
++        return NodeLib.nodes(storage.quadTable.getNodeTupleTable().getNodeTable(), z);
++    }
++
++    @Override
++    public long size() {
++        checkNotClosed();
++        return Iter.count(listGraphNodes());
++    }
++
++    @Override
++    public boolean isEmpty() {
++        checkNotClosed();
++        return getTripleTable().isEmpty() && getQuadTable().isEmpty();
++    }
++
++    @Override
++    public void clear() {
++        checkNotClosed() ; 
++        // Leave the node table alone.
++        getTripleTable().clearTriples() ;
++        getQuadTable().clearQuads() ;
++    }
++    
++    public NodeTupleTable chooseNodeTupleTable(Node graphNode) {
++        checkNotClosed() ; 
++
++        if ( graphNode == null || Quad.isDefaultGraph(graphNode) )
++            return getTripleTable().getNodeTupleTable() ;
++        else
++            // Includes Node.ANY and union graph
++            return getQuadTable().getNodeTupleTable() ;
++    }
++    
++    private static final int sliceSize = 1000 ;
++    
++    @Override
++    public void deleteAny(Node g, Node s, Node p, Node o) {
++        // Delete in batches.
++        // That way, there is no active iterator when a delete
++        // from the indexes happens.
++        checkNotClosed() ;
++        
++        if ( monitor != null ) {
++            // Need to do by nodes because we will log the deletes.
++            super.deleteAny(g, s, p, o); 
++            return ;
++        }
++
++        // Not logging - do by working as NodeIds.
++        NodeTupleTable t = chooseNodeTupleTable(g) ;
++        @SuppressWarnings("unchecked")
++        Tuple<NodeId>[] array = (Tuple<NodeId>[])new Tuple<?>[sliceSize] ;
++
++        while (true) { // Convert/cache s,p,o?
++            // The Node Cache will catch these so don't worry unduely.
++            Iterator<Tuple<NodeId>> iter = null ;
++            if ( g == null )
++                iter = t.findAsNodeIds(s, p, o) ;
++            else
++                iter = t.findAsNodeIds(g, s, p, o) ;
++
++            if ( iter == null || ! iter.hasNext() )
++                return ;
++
++            // Get a slice
++            int len = 0 ;
++            for (; len < sliceSize; len++) {
++                if ( !iter.hasNext() )
++                    break ;
++                array[len] = iter.next() ;
++            }
++            
++            // Delete the NodeId Tuples
++            for (int i = 0; i < len; i++) {
++                t.getTupleTable().delete(array[i]) ;
++                array[i] = null ;
++            }
++            // Finished?
++            if ( len < sliceSize )
++                break ;
++        }
++    }
++    
++    public Location getLocation()       { return storage.location ; }
++
++    @Override
++    public void sync() {
++        checkNotClosed();
++        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") ;
++    }
++
++    @Override
++    public boolean isInTransaction() {
++        return txnSystem.isInTransaction() ;
++    }
++
++    // txnSystem with monitor?
++    @Override
++    public void begin(ReadWrite readWrite) {
++        if ( txnMonitor != null ) txnMonitor.startBegin(readWrite); 
++        txnSystem.begin(readWrite) ;
++        if ( txnMonitor != null ) txnMonitor.finishBegin(readWrite); 
++    }
++
++    @Override
++    public boolean promote() {
++        if ( txnMonitor != null ) txnMonitor.startPromote();
++        try { 
++            return txnSystem.promote() ;
++        } finally { if ( txnMonitor != null ) txnMonitor.finishPromote(); }
++    }
++
++    @Override
++    public void commit() {
++        if ( txnMonitor != null ) txnMonitor.startCommit();
++        txnSystem.commit() ;
++        if ( txnMonitor != null ) txnMonitor.finishCommit();  
++    }
++
++    @Override
++    public void abort() {
++        if ( txnMonitor != null ) txnMonitor.startAbort() ; 
++        txnSystem.abort() ;
++        if ( txnMonitor != null ) txnMonitor.finishAbort() ;  
++    }
++
++    @Override
++    public void end() {
++        if ( txnMonitor != null ) txnMonitor.startEnd(); 
++        txnSystem.end() ;
++        if ( txnMonitor != null ) txnMonitor.finishEnd(); 
++    }
++
++    public TransactionalSystem getTxnSystem() {
++        return txnSystem ;
++    }
++
++    // Watching changes (add, delete, deleteAny) 
++    
++    private DatasetChanges monitor = null ;
++    public void setMonitor(DatasetChanges changes) {
++        monitor = changes ;
++    }
++
++    public void removeMonitor(DatasetChanges changes) {
++        if ( monitor != changes )
++            throw new InternalErrorException() ;
++        monitor = null ;
++    }
++    
++    // Watching Transactional
++    
++    private TransactionalMonitor txnMonitor = null ;
++    public void setTransactionalMonitor(TransactionalMonitor changes) {
++        txnMonitor = changes ;
++    }
++
++    public void removeTransactionalMonitor(TransactionalMonitor changes) {
++        if ( txnMonitor != changes )
++            throw new InternalErrorException() ;
++        txnMonitor = null ;
++    }
++    
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetGraphTxn.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetGraphTxn.java
index 0000000,0000000..0915ff0
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetGraphTxn.java
@@@ -1,0 -1,0 +1,34 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store;
++
++import org.apache.jena.sparql.core.DatasetGraph ;
++import org.seaborne.dboe.transaction.Transactional ;
++
++/** Transaction representation of a DatasetGraph */ 
++public interface DatasetGraphTxn extends DatasetGraph, Transactional {
++    // Combines org.seaborne.dboe.transaction.Transactional and org.apache.jena.sparql.core.Transactional.
++    //    o.s.dboe.Transactional add promote()
++    
++    @Override
++    public default boolean supportsTransactions()       { return true ; }
++    
++    @Override
++    public default boolean supportsTransactionAbort()   { return true ; }
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetGraphWrapperTxn.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetGraphWrapperTxn.java
index 0000000,0000000..e172c90
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetGraphWrapperTxn.java
@@@ -1,0 -1,0 +1,34 @@@
++/*
++ * 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.seaborne.tdb2.store;
++
++import org.apache.jena.sparql.core.DatasetGraphWrapper ;
++
++public class DatasetGraphWrapperTxn extends DatasetGraphWrapper implements DatasetGraphTxn {
++
++    public DatasetGraphWrapperTxn(DatasetGraphTxn dsg) {
++        super(dsg) ;
++    }
++
++    @Override
++    public boolean promote() {
++        return ((DatasetGraphTxn)get()).promote() ;
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetPrefixesTDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetPrefixesTDB.java
index 0000000,0000000..0b7f072
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetPrefixesTDB.java
@@@ -1,0 -1,0 +1,173 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store;
++
++import java.util.* ;
++
++import org.apache.jena.atlas.iterator.Iter ;
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.atlas.logging.Log ;
++import org.apache.jena.graph.Node ;
++import org.apache.jena.graph.NodeFactory ;
++import org.apache.jena.shared.PrefixMapping ;
++import org.apache.jena.sparql.core.DatasetPrefixStorage ;
++import org.seaborne.dboe.base.record.RecordFactory ;
++import org.seaborne.tdb2.store.nodetupletable.NodeTupleTable ;
++
++/**
++ * Dataset prefixes; a table of nodes with prefix-centric operations. The table
++ * is G-P-U where G is a graph name ("" is used for the default graph), P is a
++ * string (the prefix) and U is the IRI.
++ */
++public class DatasetPrefixesTDB implements DatasetPrefixStorage
++{
++    // Consider a cache - like PrefixMappingCache was - but needs to respect transactions.
++    // See getPrefixMapping.
++    
++    static final RecordFactory factory = new RecordFactory(3*NodeId.SIZE, 0) ;
++    static final String unamedGraphURI = "" ;
++    
++    private final NodeTupleTable nodeTupleTable ;
++    
++    public DatasetPrefixesTDB(NodeTupleTable nodeTupleTable) {
++        this.nodeTupleTable = nodeTupleTable ;
++    }
++    
++    @Override
++    public void loadPrefixMapping(String graphName, PrefixMapping pmap) {
++        Node g = NodeFactory.createURI(graphName) ; 
++        Iterator<Tuple<Node>> iter = nodeTupleTable.find(g, null, null) ;
++        iter.forEachRemaining(tuple->{
++            Node prefix = tuple.get(1)  ;
++            Node uri = tuple.get(2) ;
++            pmap.setNsPrefix(prefix.getLiteralLexicalForm(), uri.getURI()) ;
++        }) ;
++    }
++
++    @Override
++    public synchronized void insertPrefix(String graphName, String prefix, String uri) {
++        Node g = NodeFactory.createURI(graphName) ; 
++        Node p = NodeFactory.createLiteral(prefix) ; 
++        Node u = NodeFactory.createURI(uri) ;
++
++        nodeTupleTable.addRow(g,p,u) ;
++    }
++
++    @Override
++    public Set<String> graphNames() {
++        Iterator<Tuple<Node>> iter = nodeTupleTable.find((Node)null, null, null) ;
++        Set <String> x = new HashSet<>() ;
++        for ( ; iter.hasNext() ; )
++            x.add(iter.next().get(0).getURI()) ;
++        Iter.close(iter) ;
++        return x ;
++    }
++    
++    @Override
++    public synchronized String readPrefix(String graphName, String prefix) {
++        Node g = NodeFactory.createURI(graphName) ; 
++        Node p = NodeFactory.createLiteral(prefix) ; 
++        
++        Iterator<Tuple<Node>> iter = nodeTupleTable.find(g, p, null) ;
++        try {
++            if ( ! iter.hasNext() )
++                return null ;
++            Tuple<Node> t = iter.next() ;
++            Node uri = t.get(2) ;
++            return uri.getURI() ;
++        } finally { Iter.close(iter) ; } 
++    }
++
++    @Override
++    public synchronized String readByURI(String graphName, String uriStr) {
++        Node g = NodeFactory.createURI(graphName) ; 
++        Node u = NodeFactory.createURI(uriStr) ; 
++        Iterator<Tuple<Node>> iter = nodeTupleTable.find(g, null, u) ;
++        if ( ! iter.hasNext() )
++            return null ;
++        Node prefix = iter.next().get(1) ;
++        Iter.close(iter) ;
++        return prefix.getLiteralLexicalForm()  ;
++    }
++
++    @Override
++    public synchronized Map<String, String> readPrefixMap(String graphName) {
++        Map<String, String> map = new HashMap<>() ;
++        Node g = NodeFactory.createURI(graphName) ;
++        Iterator<Tuple<Node>> iter = nodeTupleTable.find(g, null, null) ;
++        for ( ; iter.hasNext() ; ) {
++            try {
++                Tuple<Node> t = iter.next() ;
++                String prefix = t.get(1).getLiteralLexicalForm() ;
++                String uri = t.get(2).getURI() ;
++                map.put(prefix, uri) ;
++            }
++            catch (Exception ex) {
++                Log.warn(this, "Mangled prefix map: graph name=" + graphName, ex) ;
++            }
++        }
++        Iter.close(iter) ;
++        return map ;
++    }
++    
++    @Override
++    public void removeFromPrefixMap(String graphName, String prefix) {
++        Node g = NodeFactory.createURI(graphName) ; 
++        Node p = NodeFactory.createLiteral(prefix) ;
++        removeAll(g, p, null) ;
++    }
++
++    @Override
++    public void removeAllFromPrefixMap(String graphName) {
++        Node g = NodeFactory.createURI(graphName) ; 
++        removeAll(g, null, null) ;
++    }
++
++    /** Remove by pattern */
++    private synchronized void removeAll(Node g, Node p, Node uri) {
++        Iterator<Tuple<Node>> iter = nodeTupleTable.find(g, p, uri) ;
++        List<Tuple<Node>> list = Iter.toList(iter) ;    // Materialize.
++        Iter.close(iter) ;
++        for ( Tuple<Node> tuple : list )
++            nodeTupleTable.deleteRow(tuple.get(0), tuple.get(1), tuple.get(2)) ; 
++    }
++    
++    public NodeTupleTable getNodeTupleTable()  { return nodeTupleTable ; }
++    
++    /** Return a PrefixMapping for the unamed graph */
++    @Override
++    public PrefixMapping getPrefixMapping()
++    { return getPrefixMapping(unamedGraphURI) ; }
++
++    /** Return a PrefixMapping for a named graph */
++    @Override
++    public PrefixMapping getPrefixMapping(String graphName) {
++        PrefixMapping pm = new GraphPrefixesProjectionTDB(graphName, this) ;
++        return pm ;
++    }
++    
++    @Override
++    public void close() {
++        nodeTupleTable.close() ;
++    }
++
++    @Override
++    public void sync() {
++        nodeTupleTable.sync() ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/GraphPrefixesProjectionTDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/GraphPrefixesProjectionTDB.java
index 0000000,0000000..2c50740
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/GraphPrefixesProjectionTDB.java
@@@ -1,0 -1,0 +1,163 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store;
++
++import java.util.Map ;
++import java.util.Map.Entry ;
++import java.util.Optional ;
++
++import org.apache.jena.rdf.model.impl.Util ;
++import org.apache.jena.shared.PrefixMapping ;
++import org.apache.jena.sparql.core.DatasetPrefixStorage ;
++
++public class GraphPrefixesProjectionTDB implements PrefixMapping {
++    // Despite the name "TDB" this is general replacement for
++    // PrefixMapping over DatasetPrefixStorage.
++    private final String graphName ;
++    private final DatasetPrefixStorage prefixes ; 
++
++    public GraphPrefixesProjectionTDB(String graphName, DatasetPrefixStorage prefixes)
++    { 
++        this.graphName = graphName ;
++        this.prefixes = prefixes ;
++    }
++
++    @Override
++    public PrefixMapping setNsPrefix(String prefix, String uri) {
++        prefixes.insertPrefix(graphName, prefix, uri); 
++        return this ;
++    }
++
++    @Override
++    public PrefixMapping removeNsPrefix(String prefix) {
++        prefixes.removeFromPrefixMap(graphName, prefix);
++        return this ;
++    }
++
++    @Override
++    public PrefixMapping clearNsPrefixMap() {
++        prefixes.removeAllFromPrefixMap(graphName);
++        return this ;
++    }
++
++    @Override
++    public PrefixMapping setNsPrefixes(PrefixMapping other) {
++        setNsPrefixes(other.getNsPrefixMap()) ;
++        return this ;
++    }
++
++    @Override
++    public PrefixMapping setNsPrefixes(Map<String, String> map) {
++        map.entrySet().forEach(entry->{
++            setNsPrefix(entry.getKey(), entry.getValue()) ;
++        });
++        return this ;
++    }
++
++    @Override
++    public PrefixMapping withDefaultMappings(PrefixMapping other) {
++        other.getNsPrefixMap().entrySet().forEach(entry->{
++            String prefix = entry.getKey() ;
++            String uri = entry.getValue();
++            if (getNsPrefixURI( prefix ) == null && getNsURIPrefix( uri ) == null)
++                setNsPrefix( prefix, uri );
++        }) ;
++        return this ;
++    }
++
++    @Override
++    public String getNsPrefixURI(String prefix) {
++        return prefixes.readPrefix(graphName, prefix) ;
++    }
++
++    @Override
++    public String getNsURIPrefix(String uri) {
++        return prefixes.readByURI(graphName, uri) ;
++    }
++
++    @Override
++    public Map<String, String> getNsPrefixMap() {
++        return prefixes.readPrefixMap(graphName) ;
++    }
++
++    // From PrefixMappingImpl
++    @Override
++    public String expandPrefix(String prefixed) {
++        {
++            int colon = prefixed.indexOf(':') ;
++            if ( colon < 0 )
++                return prefixed ;
++            else {
++                String prefix = prefixed.substring(0, colon) ;
++                String uri = prefixes.readPrefix(graphName, prefix) ;
++                return uri == null ? prefixed : uri + prefixed.substring(colon + 1) ;
++            }
++        }
++    }
++
++    @Override
++    public String qnameFor(String uri) {
++        int split = Util.splitNamespaceXML(uri) ;
++        String ns = uri.substring(0, split); 
++        String local = uri.substring(split) ;
++        if ( local.equals("") )
++            return null ;
++        String prefix = prefixes.readByURI(graphName, ns) ;
++        return prefix == null ? null : prefix + ":" + local ;
++    }
++    
++    @Override
++    public String shortForm(String uri) {
++        Optional<Entry<String, String>> e = findMapping(uri, true) ;
++        if ( ! e.isPresent() )
++            return uri ;
++        return e.get().getKey() + ":" + uri.substring((e.get().getValue()).length()) ;
++    }
++
++    // Do better?
++    
++    @Override
++    public boolean hasNoMappings() { return getNsPrefixMap().isEmpty() ; }
++    
++    @Override
++    public int numPrefixes() {
++        return getNsPrefixMap().size() ;
++    }
++
++    private Optional<Entry<String, String>> findMapping( String uri, boolean partial )
++    {
++        return getNsPrefixMap().entrySet().stream().sequential().filter(e->{
++            String ss = e.getValue();
++            if (uri.startsWith( ss ) && (partial || ss.length() == uri.length())) 
++                return true;
++            return false ;
++        }).findFirst() ;
++    }    
++    
++    @Override
++    public PrefixMapping lock() {
++        return this ;
++    }
++
++    @Override
++    public boolean samePrefixMappingAs(PrefixMapping other) {
++        return this.getNsPrefixMap().equals(other.getNsPrefixMap()) ;
++    }
++
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/GraphTDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/GraphTDB.java
index 0000000,0000000..a1e99ce
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/GraphTDB.java
@@@ -1,0 -1,0 +1,180 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store ;
++
++import java.util.Iterator ;
++import java.util.function.Function;
++
++import org.apache.jena.atlas.iterator.Iter ;
++import org.apache.jena.atlas.lib.Closeable ;
++import org.apache.jena.atlas.lib.Sync ;
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.atlas.lib.tuple.TupleFactory ;
++import org.apache.jena.graph.* ;
++import org.apache.jena.riot.other.GLib ;
++import org.apache.jena.shared.PrefixMapping ;
++import org.apache.jena.sparql.core.GraphView ;
++import org.apache.jena.sparql.core.Quad ;
++import org.apache.jena.util.iterator.ExtendedIterator ;
++import org.apache.jena.util.iterator.WrappedIterator ;
++import org.seaborne.tdb2.TDBException ;
++import org.seaborne.tdb2.graph.TransactionHandlerTDB ;
++import org.seaborne.tdb2.store.nodetupletable.NodeTupleTable ;
++
++/**
++ * General operations for TDB graphs (free-standing graph, default graph and
++ * named graphs)
++ */
++public class GraphTDB extends GraphView implements Closeable, Sync {
++    private final TransactionHandler transactionHandler = new TransactionHandlerTDB(this) ;
++
++    // Switch this to DatasetGraphTransaction
++    private final DatasetGraphTDB    dataset ;
++
++    public GraphTDB(DatasetGraphTDB dataset, Node graphName) {
++        super(dataset, graphName) ;
++        this.dataset = dataset ;
++    }
++
++    /** get the current TDB dataset graph - changes for transactions */
++    public DatasetGraphTDB getDSG() {
++        return dataset ;
++    }
++
++    /** The NodeTupleTable for this graph */
++    public NodeTupleTable getNodeTupleTable() {
++        return getDSG().chooseNodeTupleTable(getGraphName()) ;
++    }
++
++    @Override
++    protected PrefixMapping createPrefixMapping() {
++        if ( isDefaultGraph() )
++            return getDSG().getPrefixes().getPrefixMapping() ;
++        if ( isUnionGraph() )
++            return getDSG().getPrefixes().getPrefixMapping() ;
++        return getDSG().getPrefixes().getPrefixMapping(getGraphName().getURI()) ;
++    }
++
++    @Override
++    public final void sync() {
++        dataset.sync() ;
++    }
++
++    @Override
++    final public void close() {
++        sync() ;
++        // Don't close the GraphBase.
++        //super.close() ;
++    }
++
++    protected static ExtendedIterator<Triple> graphBaseFindDft(DatasetGraphTDB dataset, Triple triple) {
++        Iterator<Quad> iterQuads = dataset.find(Quad.defaultGraphIRI, triple.getSubject(), triple.getPredicate(), triple.getObject()) ;
++        if ( iterQuads == null )
++            return org.apache.jena.util.iterator.NullIterator.instance() ;
++        // Can't be duplicates - fixed graph node..
++        Iterator<Triple> iterTriples = projectQuadsToTriples(Quad.defaultGraphIRI, iterQuads) ;
++        return WrappedIterator.createNoRemove(iterTriples) ;
++    }
++
++    protected static ExtendedIterator<Triple> graphBaseFindNG(DatasetGraphTDB dataset, Node graphNode, Triple m) {
++        Node gn = graphNode ;
++        // Explicitly named union graph.
++        if ( isUnionGraph(gn) )
++            gn = Node.ANY ;
++
++        Iterator<Quad> iter = dataset.getQuadTable().find(gn, m.getMatchSubject(), m.getMatchPredicate(), m.getMatchObject()) ;
++        if ( iter == null )
++            return org.apache.jena.util.iterator.NullIterator.instance() ;
++
++        Iterator<Triple> iterTriples = projectQuadsToTriples((gn == Node.ANY ? null : gn), iter) ;
++
++        if ( gn == Node.ANY )
++            iterTriples = Iter.distinct(iterTriples) ;
++        return WrappedIterator.createNoRemove(iterTriples) ;
++    }
++
++    @Override
++    protected ExtendedIterator<Triple> graphUnionFind(Node s, Node p, Node o) {
++        Node g = Quad.unionGraph ;
++        Iterator<Quad> iterQuads = getDSG().find(g, s, p, o) ;
++        Iterator<Triple> iter = GLib.quads2triples(iterQuads) ;
++        // Suppress duplicates after projecting to triples.
++        // TDB guarantees that duplicates are adjacent.
++        // See SolverLib.
++        iter = Iter.distinctAdjacent(iter) ;
++        return WrappedIterator.createNoRemove(iter) ;
++    }
++
++    @Override
++    protected final int graphBaseSize() {
++        if ( isDefaultGraph() )
++            return (int)getNodeTupleTable().size() ;
++
++        Node gn = getGraphName() ;
++        boolean unionGraph = isUnionGraph(gn) ;
++        gn = unionGraph ? Node.ANY : gn ;
++        Iterator<Tuple<NodeId>> iter = getDSG().getQuadTable().getNodeTupleTable().findAsNodeIds(gn, null, null, null) ;
++        if ( unionGraph ) {
++            iter = Iter.map(iter, project4TupleTo3Tuple) ;
++            iter = Iter.distinctAdjacent(iter) ;
++        }
++        return (int)Iter.count(iter) ;
++    }
++
++	private static Function<Tuple<NodeId>, Tuple<NodeId>> project4TupleTo3Tuple = item -> {
++		if (item.len() != 4)
++			throw new TDBException("Expected a Tuple of 4, got: " + item);
++		return TupleFactory.tuple(item.get(1), item.get(2), item.get(3));
++	};
++	
++	private static Iterator<Triple> projectQuadsToTriples(Node graphNode, Iterator<Quad> iter) {
++	    // Checking.
++        Function<Quad, Triple> f = (q) -> {
++            if ( graphNode != null && !q.getGraph().equals(graphNode) )
++                throw new InternalError("projectQuadsToTriples: Quads from unexpected graph (expected=" + graphNode + ", got=" + q.getGraph() + ")");
++            return q.asTriple();
++        };
++        // Without.
++        //Function<Quad, Triple> f = (q) -> q.asTriple();
++	    return Iter.map(iter, f);
++	}
++
++    @Override
++    public TransactionHandler getTransactionHandler() {
++        return transactionHandler ;
++    }
++
++    @Override
++    public void clear() {
++        dataset.deleteAny(getGraphName(), Node.ANY, Node.ANY, Node.ANY) ;
++        getEventManager().notifyEvent(this, GraphEvents.removeAll) ;
++    }
++
++    @Override
++    public void remove(Node s, Node p, Node o) {
++        if ( getEventManager().listening() ) {
++            // Have to do it the hard way so that triple events happen.
++            super.remove(s, p, o) ;
++            return ;
++        }
++
++        dataset.deleteAny(getGraphName(), s, p, o) ;
++        // We know no one is listening ...
++        // getEventManager().notifyEvent(this, GraphEvents.remove(s, p, o) ) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/GraphViewSwitchable.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/GraphViewSwitchable.java
index 0000000,0000000..d52f7a0
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/GraphViewSwitchable.java
@@@ -1,0 -1,0 +1,139 @@@
++/*
++ * 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.seaborne.tdb2.store;
++
++import java.util.Map ;
++
++import org.apache.jena.graph.Graph ;
++import org.apache.jena.graph.Node ;
++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 ;
++
++/** A GraphView that is sensitive to {@link DatasetGraphSwitchable} switching.
++ */
++public class GraphViewSwitchable extends GraphView {
++    // Fixups for GraphView
++    //   Prefixes.
++    //   Transaction handler.
++    // Long term - ensure that GraphView uses get() always, inc prefixes, transaction handlers
++    
++    // Factory style.
++    public static GraphViewSwitchable createDefaultGraph(DatasetGraphSwitchable dsg)
++    { return new GraphViewSwitchable(dsg, Quad.defaultGraphNodeGenerated) ; }
++    
++    public static GraphView createNamedGraph(DatasetGraphSwitchable dsg, Node graphIRI)
++    { return new GraphViewSwitchable(dsg, graphIRI) ; }
++    
++    public static GraphViewSwitchable createUnionGraph(DatasetGraphSwitchable dsg)
++    { return new GraphViewSwitchable(dsg, Quad.unionGraph) ; }
++    
++    private final DatasetGraphSwitchable dsgx;
++    protected DatasetGraphSwitchable getx() { return dsgx; }
++    
++    protected GraphViewSwitchable(DatasetGraphSwitchable dsg, Node gn) {
++        super(dsg, gn) ;
++        this.dsgx = dsg;
++    }
++
++//    @Override
++//    public TransactionHandler getTransactionHandler() {
++//        // XXX Awiting for promote to be enabled.
++//        return super.getTransactionHandler();
++//    }
++    
++    @Override
++    protected PrefixMapping createPrefixMapping() {
++        Node gn = super.getGraphName();
++        if ( gn == Quad.defaultGraphNodeGenerated )
++            gn = null;
++        if ( Quad.isUnionGraph(gn) ) {
++            // Read-only wrapper would be better that a copy.
++            PrefixMapping pmap = new PrefixMappingImpl();
++            pmap.setNsPrefixes(prefixMapping(null));
++            return pmap; 
++        }
++        return prefixMapping(gn);
++    }
++    
++    /** Return the {@code DatasetGraphSwitchable} we are viewing. */
++    @Override
++    public DatasetGraphSwitchable getDataset() {
++        return getx() ;
++    }
++
++    // TDB2 specific.
++    // Does not cope with blank nodes.
++    // A PrefixMapping sending operations via the switchable.
++    // Long term, rework as PrefixMapping over PrefixMap over DatasetPrefixStorage
++    private PrefixMapping prefixMapping(Node graphName) {
++        
++        String gn = (graphName == null) ? "" : graphName.getURI(); 
++        
++        return new PrefixMappingImpl() {
++            
++            DatasetPrefixStorage dps() {
++                return ((DatasetGraphTDB)(getx().get())).getPrefixes();
++            }
++            
++            Graph graph() {
++                DatasetGraphTDB dsg = (DatasetGraphTDB)getx().get();
++                if ( gn == null )
++                    return dsg.getDefaultGraph();
++                else
++                    return dsg.getGraph(graphName);
++            }
++            
++            PrefixMapping prefixMapping() {
++                if ( gn == null )
++                    return dps().getPrefixMapping();
++                else
++                    return dps().getPrefixMapping(gn); 
++            }
++
++            @Override
++            protected void set(String prefix, String uri) {
++                dps().insertPrefix(gn, prefix, uri);
++                super.set(prefix, uri);
++            }
++
++            @Override
++            protected String get(String prefix) {
++                //Ignore. String x = super.get(prefix);
++                // (Ignore more?)
++                return dps().readPrefix(gn, prefix);
++            }
++
++            @Override
++            protected void remove(String prefix) {
++                dps().getPrefixMapping().removeNsPrefix(prefix);
++                super.remove(prefix);
++            }
++            
++            @Override
++            public Map<String, String> getNsPrefixMap() {
++                return prefixMapping().getNsPrefixMap();
++                //return graph().getPrefixMapping().getNsPrefixMap();
++            }
++        };
++    }
++    
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/Hash.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/Hash.java
index 0000000,0000000..6692325
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/Hash.java
@@@ -1,0 -1,0 +1,56 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store;
++
++import java.util.Arrays;
++
++import org.apache.jena.atlas.lib.Bytes ;
++
++
++/** Hash values. */ 
++public class Hash
++{
++    private byte [] bytes ;
++    public Hash(int len) { bytes = new byte[len] ; }
++    public int getLen() { return bytes.length ; }
++    public byte [] getBytes() { return bytes ; }
++    
++    @Override
++    public int hashCode()
++    { 
++        return Arrays.hashCode(bytes) ;
++    }
++    
++    @Override
++    public boolean equals(Object other)
++    {
++        if ( this == other ) return true ;
++        if ( ! (other instanceof Hash) )
++            return false ;
++        boolean b = Arrays.equals(bytes, ((Hash)other).bytes) ;
++        return b ;
++    }
++    
++    @Override
++    public String toString()
++    {
++        return "hash:"+Bytes.asHex(bytes) ;
++    }
++    
++    
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/IteratorCheckNotConcurrent.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/IteratorCheckNotConcurrent.java
index 0000000,0000000..0aa4182
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/IteratorCheckNotConcurrent.java
@@@ -1,0 -1,0 +1,93 @@@
++/*
++ * 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.seaborne.tdb2.store;
++
++import static java.lang.String.format ;
++
++import java.util.ConcurrentModificationException ;
++import java.util.Iterator ;
++import java.util.NoSuchElementException ;
++import java.util.concurrent.atomic.AtomicLong ;
++
++import org.apache.jena.atlas.iterator.Iter ;
++import org.apache.jena.atlas.lib.Closeable ;
++
++/** Wrapper to check an iterator has not been used across some write operation. */ 
++class IteratorCheckNotConcurrent<T> implements Iterator<T>, Closeable
++{
++    private Iterator<T> iter ;
++    private AtomicLong eCount ;  // Needs fixing to be per thera in some way - either from the caller or here. 
++    private boolean finished = false ;
++    private long startEpoch ; 
++
++    IteratorCheckNotConcurrent(Iterator<T> iter, AtomicLong eCount) {
++        // Assumes correct locking to set up, i.e. eCount not changing
++        // (writer on separate thread).
++        this.iter = iter ;
++        this.eCount = eCount ;
++        this.startEpoch = eCount.get() ;
++    }
++
++    private void checkConcurrentModification() {
++        if ( finished )
++            return ;
++
++        long now = eCount.get() ;
++        if ( now != startEpoch ) {
++            policyError(format("Iterator: started at %d, now %d", startEpoch, now)) ;
++        }
++    }
++    
++    private static void policyError(String message) {
++        throw new ConcurrentModificationException(message) ;
++    } 
++
++    @Override
++    public boolean hasNext() {
++        checkConcurrentModification() ;
++        boolean b = iter.hasNext() ;
++        if ( !b )
++            close() ;
++        return b ;
++    }
++
++    @Override
++    public T next() {
++        checkConcurrentModification() ;
++        try { return iter.next() ; }
++        catch (NoSuchElementException ex) {
++            close() ;
++            throw ex ;
++        }
++    }
++
++    @Override
++    public void remove() {
++        checkConcurrentModification() ;
++        iter.remove() ;
++        // Reset the epoch.
++        startEpoch = eCount.get() ;
++    }
++
++    @Override
++    public void close() {
++        finished = true ;
++        Iter.close(iter) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/IteratorTxnTracker.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/IteratorTxnTracker.java
index 0000000,0000000..d56fb0d
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/IteratorTxnTracker.java
@@@ -1,0 -1,0 +1,55 @@@
++/*
++ * 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.seaborne.tdb2.store;
++
++import java.util.Iterator;
++import java.util.Objects;
++
++import org.apache.jena.atlas.iterator.IteratorWrapper;
++import org.seaborne.dboe.transaction.txn.Transaction;
++import org.seaborne.dboe.transaction.txn.TransactionException;
++import org.seaborne.dboe.transaction.txn.TransactionalSystem;
++import org.seaborne.dboe.transaction.txn.TxnId;
++
++/** Wrapper to check that an iterator is used inside its originating transaction. */
++public class IteratorTxnTracker<T> extends IteratorWrapper<T> {
++    private final TransactionalSystem txnSystem;
++    private final TxnId txnId;
++
++    public IteratorTxnTracker(Iterator<T> iterator, TransactionalSystem txnSystem, TxnId txnId) {
++        super(iterator);
++        this.txnSystem = Objects.requireNonNull(txnSystem, "TransactionalSystem");
++        this.txnId = Objects.requireNonNull(txnId, "TxnId") ;
++    }
++
++    @Override public boolean hasNext()  { check() ; return super.hasNext() ; }
++
++    @Override public T next()           { check() ; return super.next() ; }
++    
++    @Override public void remove()      { check() ; super.remove() ; }
++
++    private void check() {
++        Transaction txn = txnSystem.getThreadTransaction();
++        if ( txn == null )
++            throw new TransactionException("Iterator used outside its original transaction");
++        if ( txn != null && txnId.equals(txn.getTxnId()) )
++            return ;
++        throw new TransactionException("Iterator used inside a different transaction");
++    }
++}


[29/50] [abbrv] jena git commit: JENA-1396: Merge //github/afs/mantis as subdirectory jena-db/

Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/value/DoubleNode62.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/value/DoubleNode62.java
index 0000000,0000000..b65d5eb
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/value/DoubleNode62.java
@@@ -1,0 -1,0 +1,241 @@@
++/*
++ * 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.seaborne.tdb2.store.value;
++
++import org.apache.jena.atlas.lib.BitsLong;
++
++/**
++ * Doubles packed into 62 bits.
++ * <p>
++ * Uses java's 64 bit long format (which is IEEE754 binary64) except that 2 bits are taken
++ * from the exponent. This keeps the precision but reduces the range.
++ * <p>
++ * <b>Java
++ * (<a href="https://en.wikipedia.org/wiki/Double-precision_floating-point_format">IEEE
++ * 754 binary64</a>)</b>
++ * 
++ * <pre>
++ * bit 63 : sign bit
++ * bits 52-62 : exponent, 11 bits, the power of 2, bias -1023.
++ * bits 0-51  : mantissa (significand) 52 bits (the leading one is not stored).
++ * 
++ * Exponents are 11 bits, with values -1022 to +1023 held as 1 to 2046 (11 bits, bias -1023)
++ *    0x000 is signed zero.
++ *    0x7FF is +/- infinity.
++ * </pre>
++ * 
++ * for a maximum value of 1.797693e+308 = (2-2^-52)*2^1023 and smallest denormlized of
++ * (1-2^-52)*2^-1022 = 2.225...e-308.
++ * <p>
++ * <b>DoubleNode62</b>
++ * <p>
++ * In a 62 bit double:
++ * 
++ * <pre>
++ * bit 63 : pointer bit.
++ * bit 62 : double type bit.
++ * bit 61 : sign bit 
++ * bits 52-60 : exponent, 9 bits, the power of 2, bias -255
++ * bits 0-51  : mantissa (significand) 52 bits (the leading one is not stored).
++ * 
++ * Exponents are 9 bits, with values -254 to 255, held as 1 to 512 (9 bits, bias -255)
++ *    0x000 is signed zero.
++ *    0x1FF is +/- infinity.
++ * </pre>
++ * 
++ * for a maximum value of (2-2^-52)*2^255 = 1.157921e+77 and smallest denormlized of
++ * (1-2^-52)*2^-254 = 3.4544674e-77
++ * <p>
++ * 0 is not a legal encoding because the high bit is the pointer/value bit.
++ * <p>
++ * "No encoding" is 0xFF00_0000_0000_0000L which would otherwise be the smallest (most negative) denormalized value: 
++ *  -3.5336941295567687E72
++ * <p>All unencodeable numbers wil endup in the node table in full lexical form.  
++ */  
++public class DoubleNode62 {
++    /**
++     * An encoded value that is not possible.
++     * (it has high bits set).
++     */ 
++    public static long NO_ENCODING = 0xFF00_0000_0000_0000L;
++
++    /** Encode as a 62 bit long.
++     * Return {@link #NO_ENCODING} for "not possible".
++     * The top two bits are zero if packing was possible.
++     */
++    public static long pack(double v) {
++        long x = Double.doubleToRawLongBits(v);
++        long sign = BitsLong.unpack(x, 63, 64);
++        long exp11 = BitsLong.unpack(x, 52, 63);
++        long exp9 = encode11to9(exp11);
++        if ( exp9 == -1 )
++            return NO_ENCODING;
++        long significand = BitsLong.unpack(x, 0, 52);
++        // Do not set the value bit or double bit.
++        // This is done when outting into bytes. 
++        long z = 0;
++        z = BitsLong.pack(z, sign, 61, 62);
++        z = BitsLong.pack(z, exp9, 52, 61);
++        z = BitsLong.pack(z, significand, 0, 52);
++        return z;
++    }
++
++    public static long insertType(long x) {
++        // bits 63 1 (pointer/value), bit 62 1(double)
++        return x | 0x3L<<62;
++    }
++
++    public static long removeType(long x) {
++        // bits 63 1 (pointer/value), bit 62 1(double)
++        return x & ~(0x3L<<62);
++    }
++
++    public static double unpack(long x) {
++        if ( x == NO_ENCODING )
++            throw new IllegalArgumentException("No encoding inline");
++        long sign = BitsLong.unpack(x, 61, 62);
++        long exp9 = BitsLong.unpack(x, 52, 61);
++        long significand = BitsLong.unpack(x, 0, 52);
++        long exp11 = decode9to11(exp9);
++        long z = 0;
++        z = BitsLong.pack(z, sign, 63, 64);
++        z = BitsLong.pack(z, exp11, 52, 63);
++        z = BitsLong.pack(z, significand, 0, 52);
++        double d = Double.longBitsToDouble(z);
++        return d;
++    }
++
++    // Exponent: returns -1 for out of inlien range.
++    private static long encode11to9(long exp11) {
++        if ( exp11 == 0 )
++            return 0L;
++        if ( exp11 == 0x7FF )
++            return 0x3FFL;
++        
++        // Remove bias.
++        long expRebase = exp11 - 1023;
++        if ( expRebase < -254 || expRebase > 255 )
++            // Out of range.
++            return -1;
++        long exp9 = expRebase + 255;
++        return exp9;
++    }
++
++    // Exponent
++    private static long decode9to11(long exp9) {
++        if ( exp9 == 0 )
++            return 0L;
++        else if ( exp9 == 0x1FF )
++            return 0x7FFL;
++        long expRebase = exp9 - 255;
++        long exp11 = expRebase + 1023;
++        return exp11;
++        
++    }
++
++    // ---- Constants without type bits (so bits 62 and 63 are zero).
++    /** 
++     * 0x1ff0000000000000L
++     * @see Double#POSITIVE_INFINITY
++     */
++    public static final long POSITIVE_INFINITY_BITS = pack(Double.POSITIVE_INFINITY);
++
++    /** 
++     * @see Double#POSITIVE_INFINITY
++     */
++    public static final double POSITIVE_INFINITY = unpack(POSITIVE_INFINITY_BITS);
++
++    /**
++     * 0x3ff0000000000000L
++     * @see Double#NEGATIVE_INFINITY
++     */
++    public static final long NEGATIVE_INFINITY_BITS = pack(Double.NEGATIVE_INFINITY);
++
++    /**
++     * @see Double#NEGATIVE_INFINITY
++     */
++    public static final double NEGATIVE_INFINITY = unpack(NEGATIVE_INFINITY_BITS);
++
++    /**
++     * 0x1ff8000000000000L
++     * @see Double#NaN
++     */
++    public static final long NaN_BITS = pack(Double.NaN);
++
++    /**
++     * @see Double#NaN
++     */
++    public static final double NaN = unpack(NaN_BITS);
++
++    /**
++     * 0x3fefffffffffffffL
++     * <br/>
++     * (2-2<sup>-52</sup>)&middot;2<sup>255</sup>.
++     * 
++     * @see Double#MAX_VALUE
++     */
++    public static final long MAX_VALUE_BITS = 0x3fefffffffffffffL;
++
++    /**
++     * 0x3fefffffffffffffL
++     * <br/>
++     * (2-2<sup>-52</sup>)&middot;2<sup>255</sup>.
++     * 
++     * @see Double#MAX_VALUE
++     */
++    public static final double MAX_VALUE = unpack(MAX_VALUE_BITS);
++
++    /**
++     * 0x0010000000000000L
++     * @see Double#MIN_NORMAL
++     */
++    public static final long MIN_NORMAL_BITS = 0x0010000000000000L;
++
++    /**
++     * @see Double#MIN_NORMAL
++     */
++    public static final double MIN_NORMAL = unpack(0x0010000000000000L);
++
++    /**
++     * 0x01L
++     * @see Double#MIN_VALUE
++     */
++    public static final long MIN_VALUE_BITS = 0x01L;
++
++    /**
++     * 0x01L
++     * @see Double#MIN_VALUE
++     */
++    public static final double MIN_VALUE = unpack(MIN_VALUE_BITS);
++
++    /**
++     * @see Double#MAX_EXPONENT
++     */
++    public static final int MAX_EXPONENT = 255;
++
++    /**
++     * @see Double#MIN_EXPONENT
++     */
++    public static final int MIN_EXPONENT = -254;
++
++    /**
++     * @see Double#SIZE
++     */
++    public static final int SIZE = 62;
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/value/FloatNode.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/value/FloatNode.java
index 0000000,0000000..4dc1c0a
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/value/FloatNode.java
@@@ -1,0 -1,0 +1,31 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.value;
++
++public class FloatNode {
++    // 32 bits of value; collapses NaNs to a single value.
++
++    public static long pack(float v) {
++        return Float.floatToIntBits(v);
++    }
++
++    public static float unpack(long v) {
++        return Float.intBitsToFloat((int)v);
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/value/IntegerNode.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/value/IntegerNode.java
index 0000000,0000000..9df7352
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/value/IntegerNode.java
@@@ -1,0 -1,0 +1,63 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.value;
++
++import org.apache.jena.atlas.lib.BitsLong;
++
++public class IntegerNode {
++    // 56 bits of value, including sign bit.
++    public static int  LEN   = 56;
++    public static int  LBITS = Long.SIZE;
++    public static long MAX   = (1L << (LEN - 1)) - 1;
++    public static long MIN   = -(1L << (LEN - 1));
++
++    public static long pack(long v) {
++        if ( v >= MIN && v <= MAX ) {
++            v = BitsLong.clear(v, LEN, LBITS);
++            return v;
++        } else
++            return -1;
++    }
++
++    public static long unpack(long v) {
++        long val = BitsLong.clear(v, LEN, LBITS);
++        // Sign extends to 64 bits.
++        if ( BitsLong.isSet(val, LEN - 1) )
++            val = BitsLong.set(v, LEN, LBITS);
++        return val;
++    }
++    
++    // Same - renamed.
++    
++    public static long pack56(long v) {
++        if ( v >= MIN && v <= MAX ) {
++            v = BitsLong.clear(v, LEN, LBITS);
++            return v;
++        } else
++            return -1;
++    }
++
++    public static long unpack56(long v) {
++        long val = BitsLong.clear(v, LEN, LBITS);
++        // Sign extends to 64 bits.
++        if ( BitsLong.isSet(val, LEN - 1) )
++            val = BitsLong.set(v, LEN, LBITS);
++        return val;
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/ConnectionTracker.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/ConnectionTracker.java
index 0000000,0000000..f2950eb
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/ConnectionTracker.java
@@@ -1,0 -1,0 +1,23 @@@
++/*
++ * 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.seaborne.tdb2.sys;
++
++abstract class ConnectionTracker<X> {
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/CopyDSG.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/CopyDSG.java
index 0000000,0000000..4e53ea3
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/CopyDSG.java
@@@ -1,0 -1,0 +1,53 @@@
++/*
++ * 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.seaborne.tdb2.sys;
++
++import java.util.Iterator;
++import java.util.List;
++
++import org.apache.jena.atlas.iterator.Iter;
++import org.apache.jena.graph.Graph;
++import org.apache.jena.graph.Node;
++import org.apache.jena.sparql.core.DatasetGraph;
++import org.apache.jena.sparql.core.Quad;
++import org.seaborne.dboe.jenax.Txn;
++
++/** Copy operations of any {@link DatasetGraph} */
++public class CopyDSG {
++
++    public static void copy(DatasetGraph dsgSrc, DatasetGraph dsgDst) {
++        Txn.executeRead(dsgSrc, ()->{
++            Txn.executeWrite(dsgDst, () -> {
++                Iterator<Quad> iter = dsgSrc.find();
++                iter.forEachRemaining(dsgDst::add);
++                copyPrefixes(dsgSrc, dsgDst);
++            });
++        });
++    }
++
++    public static void copyPrefixes(DatasetGraph dsgSrc, DatasetGraph dsgDst) {
++        List<Node> graphNames = Iter.toList(dsgSrc.listGraphNodes());
++        copyPrefixes(dsgSrc.getDefaultGraph(), dsgDst.getDefaultGraph());
++        graphNames.forEach((gn)->copyPrefixes(dsgSrc.getGraph(gn), dsgDst.getGraph(gn)));
++    }
++
++    public static void copyPrefixes(Graph srcGraph, Graph dstGraph) {
++        dstGraph.getPrefixMapping().setNsPrefixes(srcGraph.getPrefixMapping());
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseConnection.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseConnection.java
index 0000000,0000000..e9e6c27
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseConnection.java
@@@ -1,0 -1,0 +1,180 @@@
++/*
++ * 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.seaborne.tdb2.sys;
++
++import java.io.IOException ;
++import java.nio.file.Path ;
++import java.nio.file.Paths ;
++import java.util.HashSet ;
++import java.util.Map ;
++import java.util.Set ;
++import java.util.concurrent.ConcurrentHashMap ;
++
++import org.apache.jena.atlas.io.IO ;
++import org.apache.jena.atlas.lib.FileOps ;
++import org.apache.jena.sparql.core.DatasetGraph ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.dboe.base.file.ProcessFileLock ;
++import org.seaborne.dboe.sys.Names ;
++import org.seaborne.tdb2.TDBException ;
++import org.seaborne.tdb2.setup.StoreParams ;
++import org.seaborne.tdb2.store.DatasetGraphSwitchable ;
++
++// StoreConnection, DatabaseConnection < Connection<X> 
++
++public class DatabaseConnection {
++    // ConnectionTracker<X> 
++    
++    private static Map<Location, DatabaseConnection> cache = new ConcurrentHashMap<>() ;
++    
++    /** Get the {@code DatabaseConnection} to a location, 
++     *  creating the storage structures if it does not exist. */ 
++    public synchronized static DatabaseConnection connectCreate(Location location) {
++        return connectCreate(location, null) ;
++    }
++
++    /** Get the {@code DatabaseConnection} to a location, 
++     *  creating the storage structures if it does not exist.
++     *  Use the provided {@link StoreParams} - any persistent setting 
++     *  already at the location take precedence.
++     */ 
++    public synchronized static DatabaseConnection connectCreate(Location location, StoreParams params) {
++        return make(location, params) ;
++    }
++    
++    /**
++     * Return a {@code StoreConnection} for a particular location,
++     * creating it if it does not exist in storage.
++     */
++    private synchronized static DatabaseConnection make(Location location, StoreParams params) {
++        if ( location.isMemUnique() ) {
++            // Uncached, in-memory. 
++            DatasetGraph dsg = DatabaseOps.create(location);
++            DatabaseConnection dbConn = new DatabaseConnection(dsg, location, null);
++            return dbConn;
++        }
++        // Cached by Location. Named in-memory or on-disk.
++        DatabaseConnection dbConn = cache.computeIfAbsent(location, (loc)->buildForCache(loc, params));
++        return dbConn ;
++    }
++    
++    private static DatabaseConnection buildForCache(Location location, StoreParams params) {
++        if ( location.isMemUnique() ) {
++            throw new TDBException("Can't buildForCache a memory-unique location");
++        }
++        ProcessFileLock lock = null;
++        if (SystemTDB.DiskLocationMultiJvmUsagePrevention && ! location.isMem() ) {
++            lock = lockForLocation(location);
++            // Take the lock.  This is atomic.
++            lock.lockEx();
++        }
++        DatasetGraph dsg = DatabaseOps.create(location);
++        return new DatabaseConnection(dsg, location, lock) ;
++    }
++    
++//    private static DatasetGraph buildMem(Location location, StoreParams params) {
++//        return StoreConnection.connectCreate(location, params).getDatasetGraph(); 
++//    }
++//
++//    private static DatasetGraph buildDisk(Location location, StoreParams params) {
++//        return DatabaseOps.create(location);
++//    }
++
++    // DRY
++    /** Create or fetch a {@link ProcessFileLock} for a Location */
++    public static ProcessFileLock lockForLocation(Location location) {
++        FileOps.ensureDir(location.getDirectoryPath());
++        String lockFilename = location.getPath(Names.TDB_LOCK_FILE);
++        Path path = Paths.get(lockFilename);
++        try {
++            path.toFile().createNewFile();
++        } catch(IOException ex) { IO.exception(ex); return null; }
++        return ProcessFileLock.create(lockFilename);
++    }
++    
++    /** Use via {@link TDBInternal#expel} wherever possible.
++     * <p>
++     * Stop managing a location. Use with great care (testing only).<br/>
++     * Does not expel from {@link StoreConnection}.
++     */
++    public static synchronized void internalExpel(Location location, boolean force) {
++        // ** Check it is a container location 
++        DatabaseConnection dbConn = cache.get(location) ;
++        if ( dbConn == null )
++            return ;
++        dbConn.isValid = false;
++        //dbConn.datasetGraph = null;
++        //dbConn.datasetGraphSwitchable = null;
++        cache.remove(location) ;
++        // Release the lock after the cache is emptied.
++        if (SystemTDB.DiskLocationMultiJvmUsagePrevention && ! location.isMem() ) {
++            if ( ! dbConn.lock.isLockedHere() )
++                SystemTDB.errlog.warn("Location " + location.getDirectoryPath() + " was not locked by this process.");
++            dbConn.lock.unlock();
++            ProcessFileLock.release(dbConn.lock);
++        }
++    }
++    
++    /** 
++     * Stop managing all locations. 
++     * Use with extreme care.
++     * This is intended to support internal testing.
++     */
++    public static synchronized void internalReset() {
++        // Copy to avoid potential CME.
++        Set<Location> x = new HashSet<>(cache.keySet()) ;
++        for (Location loc : x)
++            internalExpel(loc, true) ;
++        if ( ! cache.isEmpty() )
++            System.err.println("DatabaseConnection: Cache not empty!");
++        cache.clear() ;
++    }
++    
++    // One of the other.
++    private final DatasetGraphSwitchable   datasetGraphSwitchable;
++    private final DatasetGraph             datasetGraph;
++    // This is the location of the TDB2 container directory. 
++    private final Location          location ;
++    private final ProcessFileLock   lock ;
++    private boolean                 isValid = true ;
++    
++    private DatabaseConnection(DatasetGraph dsg, Location location, ProcessFileLock fileLock)
++    {
++        this.datasetGraph = dsg;
++        this.datasetGraphSwitchable =  ( dsg instanceof DatasetGraphSwitchable ) ? (DatasetGraphSwitchable )dsg : null;
++        this.location = location ;
++        this.lock = fileLock;
++    }
++
++    public DatasetGraph getDatasetGraph() {
++        return datasetGraph;
++    }
++
++    public DatasetGraphSwitchable getDatasetGraphSwitchable() {
++        return datasetGraphSwitchable;
++    }
++    
++    public Location getLocation() {
++        return location ;
++    }
++    
++    public ProcessFileLock getLock() {
++        return lock ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseOps.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseOps.java
index 0000000,0000000..bb34ed3
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseOps.java
@@@ -1,0 -1,0 +1,264 @@@
++/*
++ * 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.seaborne.tdb2.sys;
++
++import java.io.BufferedOutputStream ;
++import java.io.IOException ;
++import java.io.OutputStream ;
++import java.nio.file.* ;
++import java.util.List;
++import java.util.zip.GZIPOutputStream ;
++
++import org.apache.jena.atlas.RuntimeIOException ;
++import org.apache.jena.atlas.lib.DateTimeUtils ;
++import org.apache.jena.atlas.lib.Pair ;
++import org.apache.jena.atlas.logging.Log;
++import org.apache.jena.riot.Lang ;
++import org.apache.jena.riot.RDFDataMgr ;
++import org.apache.jena.sparql.core.DatasetGraph;
++import org.seaborne.dboe.base.file.Location;
++import org.seaborne.dboe.jenax.Txn ;
++import org.seaborne.dboe.transaction.txn.TransactionCoordinator;
++import org.seaborne.dboe.transaction.txn.TransactionalSystem;
++import org.seaborne.tdb2.TDBException;
++import org.seaborne.tdb2.setup.StoreParams;
++import org.seaborne.tdb2.store.DatasetGraphSwitchable ;
++import org.seaborne.tdb2.store.DatasetGraphTDB;
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++/** Operations on and about TDB2 databases. 
++ * <p>
++ * TDB2 uses a hierarchical structure to manage on disk.
++ * <p>
++ * If in-memory (non-scalable, not-performant, perfect simulation for functionality using a RAM disk, for testing mainly),
++ * then the switchable layer is just a covenience. DatasetGrapOps such as {@link #compact}
++ * and {@link #backup} do not apply.
++ * <p>
++ * Directory and files on disk:
++ * <ul>
++ * <li> {@code Data-NNNN/} -- databases by version. Compacting creates a new directory, leaving 
++ * <li> store params, static (disk layout related - can not chnage once created) and dynamic (settings related to in-memory).
++ * <li> {@code Backups/} -- backups.
++ * <li> External indexes like {@code TextIndex/} -- the text index only applies to the current, latest database.
++ * </ul>
++ */
++public class DatabaseOps {
++    private static Logger LOG = LoggerFactory.getLogger(DatabaseOps.class); 
++    private static final String dbPrefix     = "Data";
++    private static final String SEP          = "-";
++    private static final String startCount   = "0001";
++
++    private static final String BACKUPS_DIR  = "Backups";
++    // Basename of the backup file. "backup_{DateTime}.nq.gz
++    private static final String BACKUPS_FN   = "backup";
++    
++    /** Create a fresh database - called by {@code DatabaseMgr}.
++     * It is important to go via {@code DatabaseConnection} to avoid
++     * duplicate {@code DatasetGraphSwitchable}s for the same location.
++     */
++    /*package*/ static DatasetGraph create(Location location) {
++        // Hide implementation class.
++        return createSwitchable(location);
++    }
++    
++    private static DatasetGraphSwitchable createSwitchable(Location location) {
++        if ( location.isMem() ) {
++            DatasetGraph dsg = StoreConnection.connectCreate(location).getDatasetGraph();
++            return new DatasetGraphSwitchable(null, location, dsg);
++        }
++        // Exists?
++       if ( ! location.exists() )
++           throw new TDBException("No such location: "+location);
++       Path path = IOX.asPath(location);
++       // Scan for DBs
++       Path db = findLocation(path, dbPrefix);
++       // XXX [StoreParams]
++       StoreParams params;
++       if ( db == null ) {
++           db = path.resolve(dbPrefix+SEP+startCount);
++           IOX.createDirectory(db);
++       }
++       Location loc2 = IOX.asLocation(db);
++       DatasetGraphTDB dsg = StoreConnection.connectCreate(loc2).getDatasetGraphTDB();
++       DatasetGraphSwitchable appDSG = new DatasetGraphSwitchable(path, location, dsg);
++       return appDSG;
++    }
++    
++    public static String backup(DatasetGraphSwitchable container) {
++        checkSupportsAdmin(container);
++        Path dbPath = container.getContainerPath();
++        Path backupDir = dbPath.resolve(BACKUPS_DIR);
++        if ( ! Files.exists(backupDir) )
++            IOX.createDirectory(backupDir);
++      
++        DatasetGraph dsg = container;
++        
++//  // Per backup source lock. 
++//  synchronized(activeBackups) {
++//      // Atomically check-and-set
++//      if ( activeBackups.contains(dsg) )
++//          Log.warn(Fuseki.serverLog, "Backup already in progress") ;
++//      activeBackups.add(dsg) ;
++//  }
++
++        Pair<OutputStream, Path> x = openUniqueFileForWriting(backupDir, BACKUPS_FN, "nq.gz");
++        try (OutputStream out2 = x.getLeft();
++             OutputStream out1 = new GZIPOutputStream(out2, 8 * 1024) ;
++             OutputStream out = new BufferedOutputStream(out1)) {
++            Txn.executeRead(dsg, ()->RDFDataMgr.write(out, dsg, Lang.NQUADS));
++        } catch (IOException e) {
++            throw IOX.exception(e) ;
++        }
++        return x.getRight().toString();
++    }
++    
++    private static void checkSupportsAdmin(DatasetGraphSwitchable container) {
++        if ( ! container.hasContainerPath() )
++            throw new TDBException("Dataset does not support admin operations");
++    }
++
++    //private static void logWarn(String msg) {}
++    
++    
++    // --> IOX
++    private static Pair<OutputStream, Path> openUniqueFileForWriting(Path dirPath, String basename, String ext) {
++        if ( ! Files.isDirectory(dirPath) )
++            throw new IllegalArgumentException("Not a directory: "+dirPath);
++        if ( basename.contains("/") || basename.contains("\\") )
++            throw new IllegalArgumentException("Basename must not contain a file path separator (\"/\" or \"\\\")");
++        
++        String timestamp = DateTimeUtils.nowAsString("yyyy-MM-dd_HH:mm:ss") ;
++        String filename = basename + "_" + timestamp ;
++        Path p = dirPath.resolve(filename+"."+ext);
++        int x = 0 ;
++        for(;;) {
++            try {
++                OutputStream out = Files.newOutputStream(p, StandardOpenOption.CREATE_NEW); 
++                return Pair.create(out, p);
++            } catch (AccessDeniedException ex)  { 
++                throw IOX.exception("Access denied", ex);
++            } catch (FileAlreadyExistsException ex) {
++                // Drop through and try again.
++            } catch (IOException ex) {
++                throw IOX.exception(ex) ;
++            }
++            // Try again.
++            x++;
++            if ( x >= 5 )
++                throw new RuntimeIOException("Can't create the unique name: number of attempts exceeded");
++            p = dirPath.resolve(filename+"_"+x+"."+ext);
++        }
++    }
++
++    
++    // JVM-wide :-(
++    private static Object compactionLock = new Object();
++    
++    public static void compact(DatasetGraphSwitchable container) {
++        checkSupportsAdmin(container);
++        synchronized(compactionLock) {
++            Path base = container.getContainerPath();
++            Path db1 = findLocation(base, dbPrefix);
++            Location loc1 = IOX.asLocation(db1);
++
++            // -- Checks
++            Location loc1a = ((DatasetGraphTDB)container.get()).getLocation();
++            if ( loc1a.isMem() ) {}
++            if ( ! loc1a.exists() )
++                throw new TDBException("No such location: "+loc1a);
++            
++            // Is this the same database location?
++            if ( ! loc1.equals(loc1a) )
++                throw new TDBException("Inconsistent (not latested?) : "+loc1a+" : "+loc1);
++            // -- Checks
++            
++            // Version
++            int v = IOX.extractIndex(db1.getFileName().toString(), dbPrefix, SEP);
++            String next = FilenameUtils.filename(dbPrefix, SEP, v+1);
++            
++            Path db2 = db1.getParent().resolve(next);
++            IOX.createDirectory(db2);
++            Location loc2 = IOX.asLocation(db2);
++            LOG.debug(String.format("Compact %s -> %s\n", db1.getFileName(), db2.getFileName()));
++            
++            compact(container, loc1, loc2);
++        }
++    }
++    
++    // XXX Later - switch in a recording dataset, not block writers, and reply after
++    // switch over before releasing the new dataset to the container.
++    // Maybe copy indexes and switch the DSG over (drop switchable).
++    
++    /** Copy the latest version from one location to another. */
++    private static void compact(DatasetGraphSwitchable container, Location loc1, Location loc2) {
++        if ( loc1.isMem() || loc2.isMem() )
++            throw new TDBException("Compact involves a memory location: "+loc1+" : "+loc2);
++        StoreConnection srcConn = StoreConnection.connectExisting(loc1);
++        if ( srcConn == null ) {
++            throw new TDBException("No database at location : "+loc1); 
++        }
++        
++        DatasetGraphTDB dsgCurrent = (DatasetGraphTDB)container.get();
++        if ( ! dsgCurrent.getLocation().equals(loc1) )
++            throw new TDBException("Inconsistent locations for base : "+dsgCurrent.getLocation()+" , "+dsgCurrent.getLocation());
++        
++        DatasetGraphTDB dsgBase = srcConn.getDatasetGraphTDB();
++        if ( dsgBase != dsgCurrent )
++            throw new TDBException("Inconsistent datasets : "+dsgCurrent.getLocation()+" , "+dsgBase.getLocation());
++        
++        TransactionalSystem txnSystem = dsgBase.getTxnSystem();
++        TransactionCoordinator txnMgr = dsgBase.getTxnSystem().getTxnMgr();
++        
++        // Stop update.  On exit there are no writers and none will start until switched over.
++        txnMgr.tryBlockWriters();
++        // txnMgr.begin(WRITE, false) will now bounce.
++        
++        // Copy the latest generation.
++        DatasetGraphTDB dsgCompact = StoreConnection.connectCreate(loc2).getDatasetGraphTDB();
++        CopyDSG.copy(dsgBase, dsgCompact);   
++
++        TransactionCoordinator txnMgr2 = dsgCompact.getTxnSystem().getTxnMgr();
++        txnMgr2.startExclusiveMode();
++
++        txnMgr.startExclusiveMode();
++
++        // No transactions on either database.
++        // Switch.
++        if ( ! container.change(dsgCurrent, dsgCompact) ) {
++            Log.warn(DatabaseOps.class, "Inconistent: old datasetgraph not as expected");
++            container.set(dsgCompact);
++        }
++        txnMgr2.finishExclusiveMode();
++        // New database running.
++
++        // Clean-up.
++        // txnMgr.finishExclusiveMode();
++        // Don't call : txnMgr.startWriters();
++        StoreConnection.release(dsgBase.getLocation());
++    }
++    
++    private static Path findLocation(Path directory, String namebase) {
++        if ( ! Files.exists(directory) )
++            return null;
++        // In-order, low to high.
++        List<Path> maybe = IOX.scanForDirByPattern(directory, namebase, SEP);
++        return Util.getLastOrNull(maybe);
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/EnvTDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/EnvTDB.java
index 0000000,0000000..449853e
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/EnvTDB.java
@@@ -1,0 -1,0 +1,61 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.sys;
++
++import java.util.Properties ;
++import java.util.Set ;
++
++import org.apache.jena.sparql.util.Context ;
++import org.apache.jena.sparql.util.Symbol ;
++import org.seaborne.tdb2.TDB2 ;
++
++public class EnvTDB
++{
++    public static void processGlobalSystemProperties()
++    {
++        Context context = processProperties(System.getProperties()) ;
++        TDB2.getContext().putAll(context) ;
++    }
++    
++    static final String prefix = SystemTDB.tdbSymbolPrefix+":" ;
++    public static Context processProperties(Properties properties)
++    {
++        Context context = new Context() ;
++        Set<Object> keys = properties.keySet() ;
++        for ( Object key : keys )
++        {
++            if ( key instanceof String )
++            {
++                String keyStr = (String)key ;
++                if ( keyStr.startsWith(prefix) )
++                    keyStr = SystemTDB.symbolNamespace+keyStr.substring(prefix.length()) ;
++                
++                
++                if ( ! keyStr.startsWith(SystemTDB.symbolNamespace) )
++                    continue ;
++                
++                Object value = properties.get(key) ;
++                
++                Symbol symbol = Symbol.create(keyStr) ;
++                
++                context.set(symbol, value) ;
++            }
++        }
++        return context ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/FilenameUtils.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/FilenameUtils.java
index 0000000,0000000..623ac99
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/FilenameUtils.java
@@@ -1,0 -1,0 +1,102 @@@
++/*
++ * 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.seaborne.tdb2.sys;
++
++import java.io.IOException;
++import java.nio.file.DirectoryStream;
++import java.nio.file.Files;
++import java.nio.file.Path;
++import java.util.ArrayList;
++import java.util.Comparator;
++import java.util.List;
++import java.util.regex.Pattern;
++
++import org.apache.jena.atlas.logging.FmtLog;
++import org.seaborne.dboe.DBOpEnvException;
++import org.slf4j.Logger;
++import org.slf4j.LoggerFactory;
++
++public class FilenameUtils {
++
++    static Logger LOG = LoggerFactory.getLogger(FilenameUtils.class);
++
++    /** Find the files in this directory that have namebase as a prefix and 
++     *  are then numbered.
++     *  <p>
++     *  Returns a sorted list from, low to high index.
++     */
++    public static List<Path> scanForDirByPattern(Path directory, String namebase, String nameSep) {
++        Pattern pattern = Pattern.compile(Pattern.quote(namebase)+
++                                          Pattern.quote(nameSep)+
++                                          "[\\d]+");
++        List<Path> paths = new ArrayList<>();
++        try (DirectoryStream<Path> stream = Files.newDirectoryStream(directory, namebase + nameSep + "*")) {
++            for ( Path entry : stream ) {
++                if ( !pattern.matcher(entry.getFileName().toString()).matches() ) {
++                    throw new DBOpEnvException("Invalid filename for matching: "+entry.getFileName());
++                    // Alternative: Skip bad trailing parts but more likely there is a naming problem.  
++                    //   LOG.warn("Invalid filename for matching: {} skipped", entry.getFileName());
++                    //   continue;
++                }
++                // Follows symbolic links.
++                if ( !Files.isDirectory(entry) )
++                    throw new DBOpEnvException("Not a directory: "+entry);
++                paths.add(entry);
++            }
++        }
++        catch (IOException ex) {
++            FmtLog.warn(LOG, "Can't inspect directory: (%s, %s)", directory, namebase);
++            throw new DBOpEnvException(ex);
++        }
++        Comparator<Path> comp = (f1, f2) -> {
++            int num1 = extractIndex(f1.getFileName().toString(), namebase, nameSep);
++            int num2 = extractIndex(f2.getFileName().toString(), namebase, nameSep);
++            return Integer.compare(num1, num2);
++        };
++        paths.sort(comp);
++        //indexes.sort(Long::compareTo);
++        return paths;
++    }
++
++    /**
++     * Extract the inde from a version-ed filename. (Base-NNNN format). 
++     * @param name
++     * @param namebase
++     * @param nameSep
++     */
++    public static int extractIndex(String name, String namebase, String nameSep) {
++        int i = namebase.length()+nameSep.length();
++        String numStr = name.substring(i);
++        int num = Integer.parseInt(numStr);
++        return num;
++    }
++    
++    /** Construct a filename */
++    public static String filename(String prefix, String sep, int N) {
++        return String.format("%s%s%04d", prefix, sep, N);
++    }
++    
++    /** Construct a filename */
++    public static String filename(String prefix, String sep, String index) {
++        return String.format("%s%s%s", prefix, sep, index);
++    }
++    
++
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/IOX.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/IOX.java
index 0000000,0000000..ed78f88
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/IOX.java
@@@ -1,0 -1,0 +1,292 @@@
++/*
++ * 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.seaborne.tdb2.sys;
++
++import java.io.BufferedOutputStream;
++import java.io.File;
++import java.io.IOException;
++import java.io.OutputStream;
++import java.nio.file.*;
++import java.nio.file.attribute.FileAttribute;
++import java.util.ArrayList;
++import java.util.Comparator;
++import java.util.List;
++import java.util.function.Function ;
++import java.util.regex.Pattern;
++
++import org.apache.jena.atlas.RuntimeIOException;
++import org.apache.jena.atlas.logging.FmtLog;
++import org.seaborne.dboe.DBOpEnvException;
++import org.seaborne.dboe.base.file.Location;
++import org.slf4j.Logger;
++import org.slf4j.LoggerFactory;
++
++/**
++ *  PathX
++ */
++public class IOX {
++    
++    public static Path currentDirectory = Paths.get(".");
++    
++    /** A Consumer that can throw {@link IOException}. */
++    public interface IOConsumer<X> {
++        void actionEx(X arg) throws IOException;
++    }
++
++    /** Convert an {@link IOException} into a {@link RuntimeIOException}.
++     * <p>
++     * Idiom:
++     * <pre>
++     *     catch(IOException ex) { throw new exception(ex); } 
++     * </pre>
++     * @param ioException
++     * @return RuntimeIOException
++     */
++    public static RuntimeIOException exception(IOException ioException) {
++        return new RuntimeIOException(ioException);
++    }
++    
++    /** Convert an {@link IOException} into a {@link RuntimeIOException}.
++     * <p>
++     * Idiom:
++     * <pre>
++     *     catch(IOException ex) { throw new exception("Oh dear", ex); } 
++     * </pre>
++     * @param message
++     * @param ioException
++     * @return RuntimeIOException
++     */
++    public static RuntimeIOException exception(String message, IOException ioException) {
++        return new RuntimeIOException(message, ioException);
++    }
++    
++    /** Write a file safely - the change happens (the function returns true) or
++     * somthing went wrong (the function throws a runtime exception) and the file is not changed.
++     * Note that the tempfile must be in the same direct as the actual file so an OS-atomic rename can be done.  
++     */
++    public static boolean safeWrite(Path file, IOConsumer<OutputStream> writerAction) {
++        Path tmp = createTempFile(file.getParent(), file.getFileName().toString(), ".tmp");
++        return safeWrite(file, tmp, writerAction);
++    }
++
++    /** Write a file safely - the change happens (the function returns true) or
++     * somthing went wrong (the function throws a runtime exception) and the file is not changed.
++     * Note that the tempfile must be in the same direct as the actual file so an OS-atomic rename can be done.  
++     */
++    public static boolean safeWrite(Path file, Path tmpFile, IOConsumer<OutputStream> writerAction) {
++        try {
++            try(OutputStream out = new BufferedOutputStream(Files.newOutputStream(tmpFile)) ) {
++                writerAction.actionEx(out);
++            }
++            move(tmpFile, file);
++            return true;
++        } catch(IOException ex) { throw IOX.exception(ex); }
++    }
++
++    /** Delete a file. */
++    public static void delete(Path path) {
++        try { Files.delete(path); }
++        catch (IOException ex) {
++            FmtLog.error(IOX.class, ex, "IOException deleting %s", path);
++            throw IOX.exception(ex);
++        }
++    }
++    
++    /** Atomically move a file. */
++    public static void move(Path src, Path dst) {
++        try { Files.move(src, dst, StandardCopyOption.ATOMIC_MOVE) ; }
++        catch (IOException ex) {
++            FmtLog.error(IOX.class, ex, "IOException moving %s to %s", src, dst);
++            throw IOX.exception(ex);
++        }
++    }
++    
++    /** Copy a file, not atomic. *
++     * Can copy to a directory or over an existing file.
++     * @param srcFilename
++     * @param dstFilename
++     */
++    public static void copy(String srcFilename, String dstFilename) {
++        Path src = Paths.get(srcFilename);
++        if ( ! Files.exists(src) )
++            throw new RuntimeIOException("No such file: "+srcFilename);
++        
++        Path dst = Paths.get(dstFilename);
++        if ( Files.isDirectory(dst) )
++            dst = dst.resolve(src.getFileName());
++        
++        try { Files.copy(src, dst); }
++        catch (IOException ex) {
++            FmtLog.error(IOX.class, ex, "IOException copying %s to %s", srcFilename, dstFilename);
++            throw IOX.exception(ex);
++        }
++    }
++    
++    /** Create a directory - thgrow a runtime exception if theer are any problems.
++     * This function wraps {@code Files.createDirectory}.
++     */
++    public static void createDirectory(Path dir) {
++        try { Files.createDirectory(dir); }
++        catch (IOException ex) { throw IOX.exception(ex); }
++    }
++
++    /** Convert a {@link Path}  to a {@link Location}. */
++    public static Location asLocation(Path path) {
++        if ( ! Files.isDirectory(path) )
++            throw new RuntimeIOException("Path is not naming a directory: "+path);
++        return Location.create(path.toString());
++    }
++    
++//    /** Convert a {@link org.apache.jena.tdb.base.file.Location} to a {@link Path}. */
++//    public static Path asPath(org.apache.jena.tdb.base.file.Location location) {
++//        if ( location.isMem() )
++//            throw new RuntimeIOException("Location is a memory location: "+location);
++//        return Paths.get(location.getDirectoryPath());
++//    }
++
++    /** Convert a {@link Location} to a {@link Path}. */
++    public static Path asPath(org.seaborne.dboe.base.file.Location location) {
++        if ( location.isMem() )
++            throw new RuntimeIOException("Location is a memory location: "+location);
++        return Paths.get(location.getDirectoryPath());
++    }
++
++    /** Convert a {@link Location} to a {@link File}. */
++    public static File asFile(Location loc) {
++        return new File(loc.getDirectoryPath());
++    }
++
++    /** Read the whole of a file */
++    public static byte[] readAll(Path pathname) {
++        try {
++            return Files.readAllBytes(pathname);
++        } catch (IOException ex) { throw IOX.exception(ex); }
++    }
++    
++    /** Write the whole of a file */
++    public static void writeAll(Path pathname, byte[] value) {
++        try {
++            Files.write(pathname, value, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE);
++        } catch (IOException ex) { throw IOX.exception(ex); }
++    }
++
++//    /**
++//     * Ensure a directory exists, creating the directory and any missing
++//     * directories on the path to it.
++//     */
++//    public static void ensureDirectory(Path dir, FileAttribute<? >... attrs) {
++//        if ( Files.exists(dir) ) {
++//            if ( ! Files.isDirectory(dir) )
++//                throw new DeltaFileException("Exists but not a directory: "+dir);
++//        }
++//        try { Files.createDirectories(dir, attrs); }
++//        catch (IOException ex) { new DeltaFileException("Failed to create directory: "+dir, ex);}
++//    }
++//    
++//    /**
++//     * Ensure a file exists - create an empty one if not. This operation does
++//     * not create a directory path to the file.
++//     */
++//    public static void ensureFile(Path filePath, FileAttribute<? >... attrs) {
++//        if ( Files.exists(filePath) ) {
++//            if ( ! Files.isRegularFile(filePath) )
++//                throw new DeltaFileException("Exists but not a regular file: "+filePath);
++//        }
++//        try { Files.createFile(filePath); }
++//        catch (IOException ex) { new DeltaFileException("Failed to create file: "+filePath, ex);}
++//    }
++
++    /**
++     * Return a temporary filename path.
++     * <p>
++     * This operation is thread-safe.
++     */
++    public static Path createTempFile(Path dir, String prefix, String suffix, FileAttribute<? >... attrs) {
++        try {
++            return Files.createTempFile(dir, prefix, suffix, attrs);
++        } catch (IOException ex) { throw IOX.exception(ex); }
++    }
++    
++    /** Generate a unique place related to path; 
++     * Optionally, provide a mapping of old name to new nae base.
++     * This method always adds "-1", "-2" etc. 
++     */  
++    public static Path uniqueDerivedPath(Path path, Function<String, String> basenameMapping) {
++        String base = path.getFileName().toString();
++        if ( basenameMapping != null )
++            base = basenameMapping.apply(base);
++        // Some large limit "just in case"
++        for(int x = 0 ; x < 10_000 ; x++ ) {
++            String destname = base+"-"+x; 
++            Path destpath = path.resolveSibling(destname);
++            if ( ! Files.exists(destpath) )
++                return destpath;
++        }
++        return null ;
++    }
++    
++    private static Logger LOG = LoggerFactory.getLogger(Util.class);
++    
++    /** Find the files in this directory that have namebase as a prefix and 
++     *  are then numbered.
++     *  <p>
++     *  Returns a sorted list from, low to high index.
++     */
++    public static List<Path> scanForDirByPattern(Path directory, String namebase, String nameSep) {
++        Pattern pattern = Pattern.compile(Pattern.quote(namebase)+
++                                          Pattern.quote(nameSep)+
++                                          "[\\d]+");
++        List<Path> paths = new ArrayList<>();
++        try (DirectoryStream<Path> stream = Files.newDirectoryStream(directory, namebase + "*")) {
++            for ( Path entry : stream ) {
++                if ( !pattern.matcher(entry.getFileName().toString()).matches() ) {
++                    throw new DBOpEnvException("Invalid filename for matching: "+entry.getFileName());
++                    // Alternative: Skip bad trailing parts but more likely there is a naming problem.  
++                    //   LOG.warn("Invalid filename for matching: {} skipped", entry.getFileName());
++                    //   continue;
++                }
++                // Follows symbolic links.
++                if ( !Files.isDirectory(entry) )
++                    throw new DBOpEnvException("Not a directory: "+entry);
++                paths.add(entry);
++            }
++        }
++        catch (IOException ex) {
++            FmtLog.warn(LOG, "Can't inspect directory: (%s, %s)", directory, namebase);
++            throw new DBOpEnvException(ex);
++        }
++        Comparator<Path> comp = (f1, f2) -> {
++            int num1 = extractIndex(f1.getFileName().toString(), namebase, nameSep);
++            int num2 = extractIndex(f2.getFileName().toString(), namebase, nameSep);
++            return Integer.compare(num1, num2);
++        };
++        paths.sort(comp);
++        //indexes.sort(Long::compareTo);
++        return paths;
++    }
++
++    /** Given a filename in "base-NNNN" format, return the value of NNNN */
++    public static int extractIndex(String name, String namebase, String nameSep) {
++        int i = namebase.length()+nameSep.length();
++        String numStr = name.substring(i);
++        int num = Integer.parseInt(numStr);
++        return num;
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/InitTDB2.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/InitTDB2.java
index 0000000,0000000..5d82fe3
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/InitTDB2.java
@@@ -1,0 -1,0 +1,40 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.sys;
++
++import org.apache.jena.system.JenaSubsystemLifecycle ;
++import org.seaborne.tdb2.TDB2 ;
++
++public class InitTDB2 implements JenaSubsystemLifecycle {
++
++    @Override
++    public void start() {
++        TDB2.init() ;
++    }
++
++    @Override
++    public void stop() {
++        // This is savage and does not take account of in-flight transactions.
++        TDB2.closedown() ; 
++    }
++    
++    @Override
++    public int level() {
++        return 42 ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/LockMRSWLite.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/LockMRSWLite.java
index 0000000,0000000..cf381ef
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/LockMRSWLite.java
@@@ -1,0 -1,0 +1,69 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.sys;
++
++import java.util.concurrent.locks.ReadWriteLock ;
++import java.util.concurrent.locks.ReentrantReadWriteLock ;
++
++import org.apache.jena.shared.JenaException ;
++import org.apache.jena.shared.Lock ;
++
++/** Light weight (?) MRSW lock implementation that assumes 
++ * correct use of enterCriticalSection/leaveCriticalSection. 
++ * That is, there is no real checking.
++ */
++public class LockMRSWLite implements Lock
++{
++    public LockMRSWLite() {}
++    
++    private ReadWriteLock mrswLock = new ReentrantReadWriteLock() ;
++    // >0 for read lock, -1 for write lock.
++    private int count = 0 ;
++    
++    @Override
++    public synchronized void enterCriticalSection(boolean readLockRequested)
++    {
++        // Once we have the lock, we can record the lock state
++        // because we know whether the actiev thread (us) is a read or write
++        // operation, then a valid leaveCriticalSection can only be read or
++        // write.
++        if ( readLockRequested ) {
++            mrswLock.readLock().lock() ;
++            count++ ;
++        } else {
++            mrswLock.writeLock().lock() ;
++            count = -1 ;
++        }
++    }
++
++    @Override
++    public synchronized void leaveCriticalSection()
++    {
++        if ( count == 0 )
++            throw new JenaException("Bad lock release - don't appear to be in a critical section") ;
++
++        if ( count < 0 ) {
++            mrswLock.writeLock().unlock() ;
++            count = 0 ;
++            return ;
++        } else {
++            mrswLock.readLock().unlock() ;
++            count-- ;
++        }
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/StoreConnection.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/StoreConnection.java
index 0000000,0000000..21d5377
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/StoreConnection.java
@@@ -1,0 -1,0 +1,194 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.sys ;
++
++import java.io.IOException;
++import java.nio.file.Path;
++import java.nio.file.Paths;
++import java.util.HashSet ;
++import java.util.Map ;
++import java.util.Set ;
++import java.util.concurrent.ConcurrentHashMap ;
++
++import org.apache.jena.atlas.io.IO;
++import org.apache.jena.atlas.lib.FileOps;
++import org.apache.jena.sparql.core.DatasetGraph ;
++import org.seaborne.dboe.base.file.ChannelManager ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.dboe.base.file.ProcessFileLock;
++import org.seaborne.dboe.sys.Names;
++import org.seaborne.dboe.transaction.txn.TransactionCoordinator ;
++import org.seaborne.dboe.transaction.txn.TransactionException ;
++import org.seaborne.tdb2.setup.StoreParams ;
++import org.seaborne.tdb2.setup.TDBBuilder ;
++import org.seaborne.tdb2.store.DatasetGraphTDB ;
++
++/** A StoreConnection is the reference to the underlying storage.
++ *  There is JVM-wide cache of backing datasets.
++ */
++public class StoreConnection
++{
++    private static Map<Location, StoreConnection> cache = new ConcurrentHashMap<>() ;
++    
++    /** Get the {@code StoreConnection} to a location, 
++     *  creating the storage structures if it does not exist. */ 
++    public synchronized static StoreConnection connectCreate(Location location) {
++        return connectCreate(location, null) ;
++    }
++
++    /** Get the {@code StoreConnection} to a location, 
++     *  creating the storage structures if it does not exist.
++     *  Use the provided {@link StoreParams} - any persistent setting 
++     *  already at the location take precedence.
++     */ 
++    public synchronized static StoreConnection connectCreate(Location location, StoreParams params) {
++        return make(location, params) ;
++    }
++
++    /** Get the {@code StoreConnection} for a location, but do not create it.
++     *  Returns null for "not setup". 
++     */
++    public synchronized static StoreConnection connectExisting(Location location) {
++        StoreConnection sConn = cache.get(location) ;
++        return sConn ;
++    }
++
++    public synchronized static boolean isSetup(Location location) {
++        return cache.containsKey(location) ;
++    }
++
++    /**
++     * Return a {@code StoreConnection} for a particular location,
++     * creating it if it does not exist in storage.
++     */
++    private synchronized static StoreConnection make(Location location, StoreParams params) {
++        StoreConnection sConn = cache.get(location) ;
++        if ( sConn == null ) {
++            ProcessFileLock lock = null;
++            if (SystemTDB.DiskLocationMultiJvmUsagePrevention && ! location.isMem() ) {
++                lock = lockForLocation(location);
++                // Take the lock.  This is atomic.
++                lock.lockEx();
++            }
++            
++            // Recovery happens when TransactionCoordinator.start is called
++            // during the building of the DatasetGraphTxn.
++            
++            DatasetGraphTDB dsg = (DatasetGraphTDB)TDBBuilder.build(location, params) ;
++            
++            sConn = new StoreConnection(dsg, lock) ;
++            if (!location.isMemUnique())
++                cache.put(location, sConn) ;
++        }
++        return sConn ;
++    }
++    
++    /** 
++     * Stop managing all locations. 
++     * Use with extreme care.
++     * This is intended to support internal testing.
++     */
++    public static synchronized void internalReset() {
++        // Copy to avoid potential CME.
++        Set<Location> x = new HashSet<>(cache.keySet()) ;
++        for (Location loc : x)
++            internalExpel(loc, true) ;
++        cache.clear() ;
++        ChannelManager.reset(); 
++    }
++
++    /** Stop managing a location. There should be no transactions running. */
++    public static synchronized void release(Location location) {
++        internalExpel(location, false) ;
++    }
++
++    /** Use via {@link TDBInternal#expel} wherever possible.
++     * <p>
++     * Stop managing a location.<br/> 
++     * Use with great care (testing only).
++     */
++    public static synchronized void internalExpel(Location location, boolean force) {
++        StoreConnection sConn = cache.get(location) ;
++        if (sConn == null) return ;
++
++        TransactionCoordinator txnCoord = sConn.getDatasetGraphTDB().getTxnSystem().getTxnMgr();
++        if (!force && txnCoord.countActive() > 0 ) 
++            throw new TransactionException("Can't expel: Active transactions for location: " + location) ;
++
++        // No transactions at this point 
++        // (or we don't care and are clearing up forcefully.)
++        
++        sConn.getDatasetGraphTDB().shutdown() ;
++        // Done by DatasetGraphTDB()
++        //txnCoord.shutdown();
++        
++        sConn.isValid = false ;
++        cache.remove(location) ;
++
++        // Release the lock after the cache is emptied.
++        if (SystemTDB.DiskLocationMultiJvmUsagePrevention && ! location.isMem() ) {
++            if ( ! sConn.lock.isLockedHere() )
++                SystemTDB.errlog.warn("Location " + location.getDirectoryPath() + " was not locked by this process.");
++            sConn.lock.unlock();
++            ProcessFileLock.release(sConn.lock);
++        }
++    }
++
++    /** Create or fetch a {@link ProcessFileLock} for a Location */
++    public static ProcessFileLock lockForLocation(Location location) {
++        FileOps.ensureDir(location.getDirectoryPath());
++        String lockFilename = location.getPath(Names.TDB_LOCK_FILE);
++        Path path = Paths.get(lockFilename);
++        try {
++            path.toFile().createNewFile();
++        } catch(IOException ex) { IO.exception(ex); return null; }
++        return ProcessFileLock.create(lockFilename);
++    }
++    
++    private final DatasetGraphTDB   datasetGraph ;
++    // This is the location of the database itself, not the TDB2 container directory. 
++    private final Location          location ;
++    private final ProcessFileLock   lock ;
++    private boolean                 isValid = true ;
++    private volatile boolean        haveUsedInTransaction = false ;
++
++    private StoreConnection(DatasetGraphTDB dsg, ProcessFileLock fileLock)
++    {
++        this.datasetGraph = dsg ;
++        this.location = dsg.getLocation() ;
++        this.lock = fileLock;
++    }
++
++    public DatasetGraph getDatasetGraph() {
++        return datasetGraph;
++    }
++
++    public DatasetGraphTDB getDatasetGraphTDB() {
++        return datasetGraph;
++    }
++    
++    public Location getLocation() {
++        return location ;
++    }
++    
++    public ProcessFileLock getLock() {
++        return lock ;
++    }
++
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/SystemTDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/SystemTDB.java
index 0000000,0000000..51ef995
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/SystemTDB.java
@@@ -1,0 -1,0 +1,416 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.sys;
++
++import java.io.FileNotFoundException ;
++import java.io.IOException ;
++import java.util.Properties ;
++
++import org.apache.jena.atlas.io.IO ;
++import org.apache.jena.atlas.lib.PropertyUtils ;
++import org.apache.jena.atlas.logging.Log ;
++import org.apache.jena.query.ARQ ;
++import org.apache.jena.sparql.engine.optimizer.reorder.ReorderLib ;
++import org.apache.jena.sparql.engine.optimizer.reorder.ReorderTransformation ;
++import org.apache.jena.sparql.util.Symbol ;
++import org.apache.jena.system.JenaSystem ;
++import org.seaborne.dboe.base.block.FileMode ;
++import org.seaborne.dboe.base.file.ProcessFileLock;
++import org.seaborne.dboe.base.record.RecordFactory ;
++import org.seaborne.dboe.sys.Sys;
++import org.seaborne.tdb2.TDB2 ;
++import org.seaborne.tdb2.TDBException ;
++import org.seaborne.tdb2.store.NodeId ;
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++public class SystemTDB
++{
++    static { JenaSystem.init(); }
++    
++    // NB Same logger as the TDB class because this class is the system info but kept out of TDB javadoc.
++    // It's visibility is TDB, not really public. 
++    private static final Logger log = LoggerFactory.getLogger(TDB2.class) ;
++    
++    /** TDB System log - use for general messages (a few) and warnings.
++     *  Generally, do not log events unless you want every user to see them every time.
++     *  TDB is an embedded database - libraries and embedded systems should be seen and not heard.
++     *  @see #errlog 
++     */
++    // This was added quite late in TDB so need to check it's used appropriately - check for Log.*
++    public static final Logger syslog = LoggerFactory.getLogger("TDB") ;
++    /** Send warnings and error */
++    public static final Logger errlog = LoggerFactory.getLogger("TDB") ;
++    
++    // ---- Constants that can't be changed without invalidating on-disk data.  
++    
++//    /** Size, in bytes, of a Java long */
++//    public static final int SizeOfLong              = Long.SIZE/Byte.SIZE ;
++//    
++//    /** Size, in bytes, of a Java int */
++//    public static final int SizeOfInt               = Integer.SIZE/Byte.SIZE ;
++    
++    /** Size, in bytes, of the persistent representation of a node id */
++    public static final int SizeOfNodeId            = NodeId.SIZE ;
++
++    /** Size, in bytes, of a pointer between blocks */
++    public static final int SizeOfPointer           = Sys.SizeOfInt ;
++    
++    // ---- Node table related
++    
++    /** Size, in bytes, of a triple index record. */
++    public static final int LenIndexTripleRecord    = 3 * NodeId.SIZE ;
++    /** Size, in bytes, of a quad index record. */
++    public static final int LenIndexQuadRecord      = 4 * NodeId.SIZE ;
++    
++    /** Size, in bytes, of a Node hash.
++     * In TDB 0.7.X and before this was 8 bytes (64/8).
++     * In TDB 0.8.0 and above it is 16 bytes (128/8).
++     * These two systems are not compatible.
++     */
++    //public static final int LenNodeHash             = SizeOfLong ; // TDB <= 0.7.X
++    public static final int LenNodeHash             = 128/8 ; // TDB >= 0.8.0
++
++    // ---- Symbols and similar
++    
++    // ---- Record factories
++    public final static RecordFactory indexRecordTripleFactory = new RecordFactory(LenIndexTripleRecord, 0) ;
++    public final static RecordFactory indexRecordQuadFactory = new RecordFactory(LenIndexQuadRecord, 0) ;
++    public final static RecordFactory nodeRecordFactory = new RecordFactory(LenNodeHash, SizeOfNodeId) ;
++
++    // Test show no visable effect.
++//    /** Unit of flushing the data when loading - data phase.  -1 means off, sync at end of load only.  */
++//    public final static long LoadFlushTickPrimary   = -1 ; // 10*1000*1000 ;
++//    /** Unit of flushing the data when loading - index phase  -1 means off, sync at end of load only.  */
++//    public final static long LoadFlushTickSecondary = -1 ; // 10*1000*1000 ;
++
++    /** Root of TDB-defined parameter names */
++    public static final String symbolNamespace      = "http://jena.hpl.hp.com/TDB#" ;
++
++    /** Root of TDB-defined parameter short names */  
++    public static final String tdbSymbolPrefix      = "tdb" ;
++    
++    /** Root of any TDB-defined Java system properties */   
++    public static final String tdbPropertyRoot      = "org.apache.jena.tdb" ;
++
++    /** Log duplicates during loading */
++    public static final Symbol symLogDuplicates     = allocSymbol("logDuplicates") ;
++
++    /** File mode : one of "direct", "mapped", "default" */ 
++    public static final Symbol symFileMode          = allocSymbol("fileMode") ;
++
++    /** Index type */
++    public static final Symbol symIndexType         = allocSymbol("indexType") ;
++
++    /** Experimental : triple and quad filtering at scan level */
++    public static final Symbol symTupleFilter       = allocSymbol("tupleFilter") ;
++
++    private static final String propertyFileKey1    = tdbPropertyRoot+".settings" ;
++    private static final String propertyFileKey2    = tdbSymbolPrefix+":settings" ;
++
++    private static String propertyFileName = null ;
++    static {
++        propertyFileName = System.getProperty(propertyFileKey1) ;
++        if ( propertyFileName == null )
++            propertyFileName = System.getProperty(propertyFileKey2) ;
++    }
++    
++    public static final boolean is64bitSystem = determineIf64Bit() ;
++
++    private static Properties properties = readPropertiesFile() ;
++
++    // To make the class initialize
++    static public void init() {}
++    
++    /** Size, in bytes, of a block */
++    public static final int BlockSize               = 8*1024 ; // intValue("BlockSize", 8*1024) ;
++
++    /** Size, in bytes, of a block for testing */
++    public static final int BlockSizeTest           = 1024 ; // intValue("BlockSizeTest", 1024) ;
++
++    /** Size, in bytes, of a block for testing */
++    public static final int BlockSizeTestMem         = 500 ;
++
++//    /** Size, in bytes, of a memory block */
++//    public static final int BlockSizeMem            = 32*8 ; //intValue("BlockSizeMem", 32*8 ) ;
++
++    /** order of an in-memory BTree or B+Tree */
++    public static final int OrderMem                = 5 ; // intValue("OrderMem", 5) ;
++    
++    /** Size, in bytes, of a segment (used for memory mapped files) */
++    public static final int SegmentSize             = 8*1024*1024 ; // intValue("SegmentSize", 8*1024*1024) ;
++    
++    // ---- Cache sizes (within the JVM)
++    
++    public static final int ObjectFileWriteCacheSize = 8*1024 ;
++    
++    /** Size of Node to NodeId cache.
++     *  Used to map from Node to NodeId spaces.
++     *  Used for loading and for query preparation.
++     */
++    public static final int Node2NodeIdCacheSize    = intValue("Node2NodeIdCacheSize", ( is64bitSystem ? 200*1000 : 20*1000 )) ;
++
++    /** Size of NodeId to Node cache.
++     *  Used to map from NodeId to Node spaces.
++     *  Used for retriveing results.
++     */
++    public static final int NodeId2NodeCacheSize    = intValue("NodeId2NodeCacheSize", ( is64bitSystem ? 750*1000 : 20*1000 ) ) ;
++    
++    /** Size of Node lookup miss cache. */
++    public static final int NodeMissCacheSize       = 1000 ;
++    
++    /** Size of the delayed-write block cache (32 bit systems only). Per file. */
++    public static final int BlockWriteCacheSize     = intValue("BlockWriteCacheSize", 1000) ;
++
++    /** Size of read block cache (32 bit systems only). Per file. */
++    public static final int BlockReadCacheSize      = intValue("BlockReadCacheSize", 5*1000) ;
++    
++    // ---- Misc
++    
++//    /** Number of adds/deletes between calls to sync (-ve to disable) */
++//    public static final int SyncTick                = intValue("SyncTick", -1) ;
++
++    /** Default BGP optimizer */
++    public static ReorderTransformation defaultReorderTransform = ReorderLib.fixed() ;
++
++    /** Unsupported (for non-standard setups) 
++     * @see #enableInlineLiterals
++     */
++    private static String propertyEnableInlineLiterals1 = "org.apache.jena.tdb.store.enableInlineLiterals" ;
++    /** Unsupported (for non-standard setups) 
++     * @see #enableInlineLiterals
++     */
++    private static String propertyEnableInlineLiterals2 = "tdb:store.enableInlineLiterals" ;
++    /** <b>Unsupported</b> (for non-standard setups).
++     * This controls whether literal values are inlined into NodeIds.
++     * This is a major efficiency boost and is the default setting.
++     * It can be set false with {@code -Dtdb:store.enableInlineLiterals=false}. 
++     * Do not mix databases created with this set to different values.
++     * Chaos and incorrect results will result.
++     * Use with care. No support. 
++     * Default setting is {@code true}   
++     */
++    public static final boolean enableInlineLiterals ;
++    static { // Set enableInlineLiterals from system properties.
++        Properties sysProperties = System.getProperties() ;
++        String key = null ; 
++        if ( sysProperties.containsKey(propertyEnableInlineLiterals1) )
++            key = propertyFileKey1 ;
++        else if ( sysProperties.containsKey(propertyEnableInlineLiterals2) )
++            key = propertyFileKey2 ;
++        if ( key == null )
++            enableInlineLiterals = true ;  // Normal value. 
++        else
++            enableInlineLiterals = Boolean.valueOf(sysProperties.getProperty(key)) ;
++    }
++
++//    public static void setNullOut(boolean nullOut)
++//    { SystemTDB.NullOut = nullOut ; }
++//
++//    /** Are we nulling out unused space in bytebuffers (records, points etc) */ 
++//    public static boolean getNullOut()
++//    { return SystemTDB.NullOut ; }
++
++    /** null out (with the FillByte) freed up space in buffers */
++    public static boolean NullOut = false ;
++    
++    /** FillByte value for NullOut */
++    public static final byte FillByte = (byte)0xFF ;
++
++    public static boolean Checking = false ;       // This isn't used enough!
++    
++    /**
++     * When enabled, a {@link ProcessFileLock} is used to block over rpcoesses oening this database.
++     */
++    public static boolean DiskLocationMultiJvmUsagePrevention = true;
++
++    public static void panic(Class<?> clazz, String string)
++    {
++        Log.error(clazz, string) ;
++        throw new TDBException(string) ;
++    }
++    
++    public static Symbol allocSymbol(String shortName)
++    { 
++        if ( shortName.startsWith(SystemTDB.tdbSymbolPrefix)) 
++            throw new TDBException("Symbol short name begins with the TDB namespace prefix: "+shortName) ;
++        if ( shortName.startsWith("http:")) 
++            throw new TDBException("Symbol short name begins with http: "+shortName) ;
++        return allocSymbol(SystemTDB.symbolNamespace, shortName) ;
++    }
++    
++    private static Symbol allocSymbol(String namespace, String shortName)
++    {
++        return Symbol.create(namespace+shortName) ;
++    }
++    
++    // ----
++    
++    private static int intValue(String prefix, String name, int defaultValue)
++    {
++        if ( ! prefix.endsWith(".") )
++            name = prefix+"."+name ;
++        else
++            name = prefix+name ;
++        return intValue(name, defaultValue) ;
++    }
++    
++    private static int intValue(String name, int defaultValue)
++    {
++        if ( name == null ) return defaultValue ;
++        if ( name.length() == 0 ) throw new TDBException("Empty string for value name") ;
++        
++        if ( properties == null )
++            return defaultValue ;
++
++        String x = properties.getProperty(name) ;
++        if ( x == null )
++            return defaultValue ; 
++        TDB2.logInfo.info("Set: "+name+" = "+x) ;
++        int v = Integer.parseInt(x) ;
++        return v ;
++    }
++
++    private static Properties readPropertiesFile()
++    {
++        if ( propertyFileName == null )
++            return null ;
++        
++        Properties p = new Properties() ;
++        try
++        {
++            TDB2.logInfo.info("Using properties from '"+propertyFileName+"'") ;
++            PropertyUtils.loadFromFile(p, propertyFileName) ;
++        } catch (FileNotFoundException ex)
++        { 
++            log.debug("No system properties file ("+propertyFileName+")") ;
++            return null ;
++        } catch (IOException ex) { IO.exception(ex) ; }
++        return p ;
++    }
++
++    // --------
++    
++    public static final boolean isWindows = determineIfWindows() ;	// Memory mapped files behave differently.
++
++    //Or look in File.listRoots.
++    //Alternative method:
++    //  http://stackoverflow.com/questions/1293533/name-of-the-operating-system-in-java-not-os-name
++    
++    private static boolean determineIfWindows() {
++    	String s = System.getProperty("os.name") ;
++    	if ( s == null )
++    		return false ;
++    	return s.startsWith("Windows ") ;
++	}
++
++    private static boolean determineIf64Bit()
++    {
++        String s = System.getProperty("sun.arch.data.model") ;
++        if ( s != null )
++        {
++            boolean b = s.equals("64") ; 
++            TDB2.logInfo.debug("System architecture: "+(b?"64 bit":"32 bit")) ;
++            return b ;
++        }
++        // Not a SUN VM
++        s = System.getProperty("java.vm.info") ;
++        if ( s == null )
++        {
++            log.warn("Can't determine the data model") ;
++            return false ;    
++        }
++        log.debug("Can't determine the data model from 'sun.arch.data.model' - using java.vm.info") ;
++        boolean b = s.contains("64") ;
++        TDB2.logInfo.debug("System architecture: (from java.vm.info) "+(b?"64 bit":"32 bit")) ;
++        return b ;
++    }
++    
++    // ---- File mode
++    
++    private static FileMode fileMode = null ;
++    public static FileMode fileMode()
++    { 
++        if ( fileMode == null )
++            fileMode = determineFileMode() ;
++        return fileMode ;
++    }
++
++    public static void setFileMode(FileMode newFileMode)
++    {
++        if ( fileMode != null )
++        {
++            log.warn("System file mode already determined - setting it has no effect") ;
++            return ;
++        }
++        fileMode = newFileMode ;
++    }
++    
++    // So the test suite can setup thing up ... very carefully.
++    /*package*/ static void internalSetFileMode(FileMode newFileMode)
++    {
++        fileMode = newFileMode ;
++    }
++    
++    private static FileMode determineFileMode()
++    {
++        // Be careful that this is not called very, very early, before --set might be seen.
++        // Hence delayed access above in fileMode().
++        
++        String x = ARQ.getContext().getAsString(SystemTDB.symFileMode, "default") ;
++
++        if ( x.equalsIgnoreCase("direct") )
++        {
++            TDB2.logInfo.info("File mode: direct (forced)") ;
++            return FileMode.direct ;
++        }
++        if ( x.equalsIgnoreCase("mapped") )
++        {
++            TDB2.logInfo.info("File mode: mapped (forced)") ;
++            return FileMode.mapped ;
++        }
++        
++        if ( x.equalsIgnoreCase("default") )
++        {
++            if ( is64bitSystem )
++            {
++                TDB2.logInfo.debug("File mode: Mapped") ;
++                return FileMode.mapped ;
++            }
++            TDB2.logInfo.debug("File mode: Direct") ;
++            return FileMode.direct ;
++        }
++        throw new TDBException("Unrecognized file mode (not one of 'default', 'direct' or 'mapped': "+x) ;
++    }
++
++//    public static Dataset setNonTransactional(Dataset dataset) {
++//        if ( dataset.isInTransaction() )
++//            return dataset ;    // And hope it's a write transaction.
++//        dataset.begin(ReadWrite.WRITE);
++//        //Or wrap DatasetGraphTDB?
++//        return dataset ;
++//    }
++//    
++//    public static DatasetGraph setNonTransactional(DatasetGraph dataset) {
++//        if ( dataset.isInTransaction() )
++//            return dataset ;
++//        dataset.begin(ReadWrite.WRITE);
++//        return dataset ;
++//    }
++
++}


[50/50] [abbrv] jena git commit: JENA-1396: Merge //github/afs/mantis as subdirectory jena-db/

Posted by an...@apache.org.
JENA-1396: Merge //github/afs/mantis as subdirectory jena-db/


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

Branch: refs/heads/jena-tdb2
Commit: d9da3592ff723d28b961de3c1591704ae0616cdd
Parents: 3fbbfcc 71a70fd
Author: Andy Seaborne <an...@apache.org>
Authored: Thu Sep 28 12:06:15 2017 +0100
Committer: Andy Seaborne <an...@apache.org>
Committed: Thu Sep 28 12:06:15 2017 +0100

----------------------------------------------------------------------
 jena-db/.gitignore                              |   46 +
 jena-db/.travis.yml                             |   17 +
 jena-db/LICENSE                                 |  202 +++
 jena-db/NOTICE                                  |   10 +
 jena-db/README.md                               |   64 +
 jena-db/dboe-base/.gitignore                    |    4 +
 jena-db/dboe-base/README.md                     |    1 +
 jena-db/dboe-base/pom.xml                       |   64 +
 .../org/seaborne/dboe/DBOpEnvException.java     |   28 +
 .../seaborne/dboe/base/StorageException.java    |   26 +
 .../org/seaborne/dboe/base/block/Block.java     |  165 ++
 .../dboe/base/block/BlockException.java         |   28 +
 .../org/seaborne/dboe/base/block/BlockMgr.java  |   99 ++
 .../seaborne/dboe/base/block/BlockMgrBase.java  |   51 +
 .../dboe/base/block/BlockMgrBuilder.java        |   24 +
 .../dboe/base/block/BlockMgrBuilderStd.java     |   28 +
 .../seaborne/dboe/base/block/BlockMgrCache.java |  325 ++++
 .../dboe/base/block/BlockMgrFactory.java        |  150 ++
 .../dboe/base/block/BlockMgrFileAccess.java     |  168 ++
 .../dboe/base/block/BlockMgrFreeChain.java      |   83 +
 .../dboe/base/block/BlockMgrLogger.java         |  180 +++
 .../dboe/base/block/BlockMgrReadonly.java       |   44 +
 .../dboe/base/block/BlockMgrSwitcher.java       |   39 +
 .../seaborne/dboe/base/block/BlockMgrSync.java  |  148 ++
 .../dboe/base/block/BlockMgrTracker.java        |  413 +++++
 .../dboe/base/block/BlockMgrWrapper.java        |  148 ++
 .../seaborne/dboe/base/block/BlockParams.java   |   26 +
 .../org/seaborne/dboe/base/block/BlockType.java |   68 +
 .../org/seaborne/dboe/base/block/FileMode.java  |   26 +
 .../seaborne/dboe/base/buffer/BufferBase.java   |  262 +++
 .../dboe/base/buffer/BufferException.java       |   26 +
 .../seaborne/dboe/base/buffer/PtrBuffer.java    |  124 ++
 .../seaborne/dboe/base/buffer/RecordBuffer.java |  235 +++
 .../dboe/base/buffer/RecordBufferIterator.java  |   97 ++
 .../seaborne/dboe/base/file/AlreadyLocked.java  |   29 +
 .../seaborne/dboe/base/file/BinaryDataFile.java |   99 ++
 .../dboe/base/file/BinaryDataFileMem.java       |  114 ++
 .../base/file/BinaryDataFileRandomAccess.java   |  148 ++
 .../dboe/base/file/BinaryDataFileSync.java      |   82 +
 .../dboe/base/file/BinaryDataFileWrapper.java   |   67 +
 .../base/file/BinaryDataFileWriteBuffered.java  |  162 ++
 .../seaborne/dboe/base/file/BlockAccess.java    |   64 +
 .../dboe/base/file/BlockAccessBase.java         |  175 ++
 .../dboe/base/file/BlockAccessByteArray.java    |  129 ++
 .../dboe/base/file/BlockAccessDirect.java       |  130 ++
 .../dboe/base/file/BlockAccessMapped.java       |  267 ++++
 .../seaborne/dboe/base/file/BlockAccessMem.java |  188 +++
 .../seaborne/dboe/base/file/BufferChannel.java  |   95 ++
 .../dboe/base/file/BufferChannelFile.java       |  171 ++
 .../dboe/base/file/BufferChannelMem.java        |  240 +++
 .../seaborne/dboe/base/file/ChannelManager.java |  114 ++
 .../seaborne/dboe/base/file/FileException.java  |   28 +
 .../seaborne/dboe/base/file/FileFactory.java    |   48 +
 .../org/seaborne/dboe/base/file/FileSet.java    |  115 ++
 .../org/seaborne/dboe/base/file/Location.java   |  264 +++
 .../org/seaborne/dboe/base/file/MetaFile.java   |  364 +++++
 .../dboe/base/file/ProcessFileLock.java         |  215 +++
 .../dboe/base/file/SegmentedMemBuffer.java      |  279 ++++
 .../seaborne/dboe/base/page/BlockConverter.java |   34 +
 .../java/org/seaborne/dboe/base/page/Page.java  |   44 +
 .../org/seaborne/dboe/base/page/PageBase.java   |   57 +
 .../seaborne/dboe/base/page/PageBlockMgr.java   |  212 +++
 .../org/seaborne/dboe/base/record/Record.java   |  188 +++
 .../dboe/base/record/RecordException.java       |   26 +
 .../dboe/base/record/RecordFactory.java         |  183 +++
 .../seaborne/dboe/base/record/RecordMapper.java |   30 +
 .../base/recordbuffer/RecordBufferPage.java     |  100 ++
 .../base/recordbuffer/RecordBufferPageBase.java |  105 ++
 .../base/recordbuffer/RecordBufferPageMgr.java  |   90 ++
 .../base/recordbuffer/RecordRangeIterator.java  |  161 ++
 .../main/java/org/seaborne/dboe/jenax/Txn.java  |   41 +
 .../main/java/org/seaborne/dboe/migrate/L.java  |  206 +++
 .../java/org/seaborne/dboe/sys/FileLib.java     |   85 +
 .../main/java/org/seaborne/dboe/sys/Names.java  |  150 ++
 .../org/seaborne/dboe/sys/ProcessUtils.java     |  156 ++
 .../main/java/org/seaborne/dboe/sys/Sys.java    |   96 ++
 .../java/org/seaborne/dboe/sys/SystemFile.java  |   23 +
 .../java/org/seaborne/dboe/sys/SystemIndex.java |  149 ++
 .../org/seaborne/dboe/test/BufferTestLib.java   |   62 +
 .../java/org/seaborne/dboe/test/RecordLib.java  |  109 ++
 .../java/org/seaborne/dboe/ConfigTestDBOE.java  |   76 +
 .../test/java/org/seaborne/dboe/TC_DBOE_IO.java |   60 +
 .../dboe/base/block/AbstractTestBlockMgr.java   |  133 ++
 .../org/seaborne/dboe/base/block/TS_Block.java  |   33 +
 .../dboe/base/block/TestBlockMgrDirect.java     |   48 +
 .../dboe/base/block/TestBlockMgrMapped.java     |   63 +
 .../dboe/base/block/TestBlockMgrMem.java        |   30 +
 .../dboe/base/block/TestBlockMgrTracked.java    |  141 ++
 .../seaborne/dboe/base/buffer/TS_Buffer.java    |   31 +
 .../dboe/base/buffer/TestPtrBuffer.java         |  315 ++++
 .../dboe/base/buffer/TestRecordBuffer.java      |  345 ++++
 .../base/file/AbstractTestBinaryDataFile.java   |   83 +
 .../file/AbstractTestBlockAccessFixedSize.java  |  101 ++
 .../file/AbstractTestBlockAccessVarSize.java    |   49 +
 .../dboe/base/file/AbstractTestChannel.java     |  144 ++
 .../org/seaborne/dboe/base/file/TS_File.java    |   48 +
 .../TestBinaryDataFileWriteBufferedFile.java    |   35 +
 .../TestBinaryDataFileWriteBufferedMem.java     |   31 +
 .../dboe/base/file/TestBinaryDataMem.java       |   31 +
 .../dboe/base/file/TestBinaryDataRAF.java       |   33 +
 .../base/file/TestBinaryDataRAFInitial.java     |   57 +
 .../base/file/TestBlockAccessByteArray.java     |   32 +
 .../dboe/base/file/TestBlockAccessDirect.java   |   44 +
 .../dboe/base/file/TestBlockAccessMapped.java   |   48 +
 .../dboe/base/file/TestBlockAccessMem.java      |   37 +
 .../dboe/base/file/TestChannelFile.java         |   38 +
 .../seaborne/dboe/base/file/TestChannelMem.java |   34 +
 .../seaborne/dboe/base/file/TestMetaFile.java   |   74 +
 .../dboe/base/file/TestProcessFileLock.java     |  107 ++
 .../dboe/base/file/TestSegmentedMemBuffer.java  |  158 ++
 .../seaborne/dboe/base/record/TS_Record.java    |   34 +
 .../seaborne/dboe/base/record/TestRecord.java   |   77 +
 .../dboe/base/recordfile/TS_RecordFile.java     |   31 +
 .../base/recordfile/TestRecordBufferPage.java   |  124 ++
 .../src/test/resources/log4j.properties         |    6 +
 jena-db/dboe-index-test/.gitignore              |    1 +
 jena-db/dboe-index-test/README.md               |    1 +
 jena-db/dboe-index-test/pom.xml                 |   54 +
 .../java/org/seaborne/dboe/index/TS_Index.java  |   29 +
 .../org/seaborne/dboe/index/TestIndexMem.java   |   34 +
 .../dboe/index/test/AbstractTestIndex.java      |  159 ++
 .../dboe/index/test/AbstractTestRangeIndex.java |  434 +++++
 .../seaborne/dboe/index/test/BaseSoakTest.java  |  140 ++
 .../seaborne/dboe/index/test/IndexMaker.java    |   26 +
 .../dboe/index/test/IndexTestGenerator.java     |   44 +
 .../seaborne/dboe/index/test/IndexTestLib.java  |  218 +++
 .../dboe/index/test/RangeIndexMaker.java        |   27 +
 .../src/test/resources/log4j.properties         |    6 +
 jena-db/dboe-index/pom.xml                      |   42 +
 .../java/org/seaborne/dboe/index/Index.java     |   91 ++
 .../org/seaborne/dboe/index/IndexChange.java    |   40 +
 .../org/seaborne/dboe/index/IndexConst.java     |   52 +
 .../org/seaborne/dboe/index/IndexLogger.java    |   64 +
 .../java/org/seaborne/dboe/index/IndexMap.java  |  171 ++
 .../org/seaborne/dboe/index/IndexParams.java    |   37 +
 .../org/seaborne/dboe/index/IndexWrapper.java   |   91 ++
 .../org/seaborne/dboe/index/RangeIndex.java     |   38 +
 .../seaborne/dboe/index/RangeIndexLogger.java   |   71 +
 .../seaborne/dboe/index/RangeIndexWrapper.java  |  105 ++
 .../src/test/resources/log4j.properties         |    6 +
 jena-db/dboe-trans-data/pom.xml                 |   63 +
 jena-db/dboe-trans-data/randtest                |   57 +
 .../dboe/trans/bplustree/AccessPath.java        |   69 +
 .../org/seaborne/dboe/trans/bplustree/BPT.java  |  248 +++
 .../dboe/trans/bplustree/BPTStateMgr.java       |   89 ++
 .../dboe/trans/bplustree/BPTreeException.java   |   26 +
 .../dboe/trans/bplustree/BPTreeNode.java        | 1507 ++++++++++++++++++
 .../dboe/trans/bplustree/BPTreeNodeMgr.java     |  228 +++
 .../dboe/trans/bplustree/BPTreePage.java        |  135 ++
 .../trans/bplustree/BPTreeRangeIterator.java    |  152 ++
 .../dboe/trans/bplustree/BPTreeRecords.java     |  388 +++++
 .../dboe/trans/bplustree/BPTreeRecordsMgr.java  |  115 ++
 .../dboe/trans/bplustree/BPlusTree.java         |  544 +++++++
 .../dboe/trans/bplustree/BPlusTreeFactory.java  |  332 ++++
 .../dboe/trans/bplustree/BPlusTreeParams.java   |  235 +++
 .../dboe/trans/bplustree/BPlusTreeTools.java    |  148 ++
 .../dboe/trans/bplustree/BlockTracker.java      |  426 +++++
 .../dboe/trans/bplustree/BptTxnState.java       |   63 +
 .../org/seaborne/dboe/trans/bplustree/Mode.java |   53 +
 .../bplustree/rewriter/BPTreeNodeBuilder.java   |  180 +++
 .../bplustree/rewriter/BPlusTreeRewriter.java   |  361 +++++
 .../rewriter/BPlusTreeRewriterUtils.java        |  104 ++
 .../rewriter/RecordBufferPageLinker.java        |   81 +
 .../rewriter/RecordBufferPagePacker.java        |   90 ++
 .../dboe/trans/data/TransBinaryDataFile.java    |  237 +++
 .../org/seaborne/dboe/trans/data/TransBlob.java |  209 +++
 .../org/seaborne/dboe/trans/TC_TransData.java   |   33 +
 .../dboe/trans/bplustree/BPlusTreeMaker.java    |   55 +
 .../dboe/trans/bplustree/TS_TxnBPTree.java      |   41 +
 .../dboe/trans/bplustree/TestBPTreeModes.java   |   77 +
 .../bplustree/TestBPTreeRecordsNonTxn.java      |  326 ++++
 .../bplustree/TestBPlusTreeIndexNonTxn.java     |   60 +
 .../trans/bplustree/TestBPlusTreeNonTxn.java    |   90 ++
 .../dboe/trans/bplustree/TestBPlusTreeTxn.java  |  205 +++
 .../rewriter/TestBPlusTreeRewriterNonTxn.java   |  179 +++
 .../trans/bplustree/soak/CmdTestBPlusTree.java  |  104 ++
 .../soak/CmdTestBPlusTreeRewriter.java          |   67 +
 .../seaborne/dboe/trans/data/TS_TransData.java  |   34 +
 .../trans/data/TestTransBinaryDataFile.java     |  219 +++
 .../data/TestTransBinaryDataFileGeneral.java    |   58 +
 .../seaborne/dboe/trans/data/TestTransBlob.java |  129 ++
 .../trans/data/TestTransBlobPersistent.java     |  116 ++
 .../dboe/trans/data/TestTransIndexMem.java      |   34 +
 .../dboe/trans/recovery/TestRecovery.java       |  126 ++
 .../src/test/resources/log4j.properties         |    6 +
 jena-db/dboe-transaction/pom.xml                |   41 +
 .../dboe/transaction/ComponentIdRegistry.java   |   92 ++
 .../seaborne/dboe/transaction/ThreadTxn.java    |  144 ++
 .../seaborne/dboe/transaction/TransInteger.java |  242 +++
 .../seaborne/dboe/transaction/TransLogger.java  |  151 ++
 .../seaborne/dboe/transaction/TransMonitor.java |  191 +++
 .../dboe/transaction/Transactional.java         |   98 ++
 .../dboe/transaction/TransactionalFactory.java  |   59 +
 .../dboe/transaction/TransactionalMonitor.java  |   38 +
 .../dboe/transaction/txn/ComponentGroup.java    |   77 +
 .../dboe/transaction/txn/ComponentId.java       |  123 ++
 .../dboe/transaction/txn/ComponentIds.java      |   61 +
 .../dboe/transaction/txn/PrepareState.java      |   40 +
 .../dboe/transaction/txn/QuorumGenerator.java   |   27 +
 .../dboe/transaction/txn/StateMgrBase.java      |  175 ++
 .../dboe/transaction/txn/StateMgrData.java      |   87 +
 .../dboe/transaction/txn/StateMgrDataIdx.java   |   44 +
 .../seaborne/dboe/transaction/txn/SysTrans.java |   52 +
 .../dboe/transaction/txn/SysTransState.java     |   35 +
 .../dboe/transaction/txn/Transaction.java       |  294 ++++
 .../transaction/txn/TransactionCoordinator.java |  804 ++++++++++
 .../txn/TransactionCoordinatorState.java        |   33 +
 .../transaction/txn/TransactionException.java   |   29 +
 .../dboe/transaction/txn/TransactionInfo.java   |   75 +
 .../dboe/transaction/txn/TransactionalBase.java |  249 +++
 .../transaction/txn/TransactionalComponent.java |  175 ++
 .../txn/TransactionalComponentBase.java         |   76 +
 .../txn/TransactionalComponentLifecycle.java    |  480 ++++++
 .../txn/TransactionalComponentWrapper.java      |  107 ++
 .../dboe/transaction/txn/TransactionalMRSW.java |  136 ++
 .../transaction/txn/TransactionalSystem.java    |   82 +
 .../seaborne/dboe/transaction/txn/TxnId.java    |   63 +
 .../dboe/transaction/txn/TxnIdFactory.java      |   60 +
 .../dboe/transaction/txn/TxnIdGenerator.java    |   38 +
 .../dboe/transaction/txn/TxnIdSimple.java       |   83 +
 .../dboe/transaction/txn/TxnIdUuid.java         |  108 ++
 .../seaborne/dboe/transaction/txn/TxnState.java |   21 +
 .../dboe/transaction/txn/journal/Journal.java   |  333 ++++
 .../transaction/txn/journal/JournalControl.java |  296 ++++
 .../transaction/txn/journal/JournalEntry.java   |   89 ++
 .../txn/journal/JournalEntryType.java           |   57 +
 .../dboe/transaction/txn/package-info.java      |   12 +
 .../dboe/transaction/AbstractTestTxn.java       |   63 +
 .../dboe/transaction/TS_Transactions.java       |   43 +
 .../seaborne/dboe/transaction/TestJournal.java  |  149 ++
 .../dboe/transaction/TestStateMgrData.java      |   75 +
 .../transaction/TestThreadingTransactions.java  |  120 ++
 .../transaction/TestTransactionCoordinator.java |  177 ++
 .../TestTransactionCoordinatorControl.java      |  141 ++
 .../transaction/TestTransactionLifecycle.java   |  261 +++
 .../transaction/TestTransactionLifecycle2.java  |  298 ++++
 .../seaborne/dboe/transaction/TestTxnId.java    |   52 +
 .../seaborne/dboe/transaction/TestTxnLib.java   |  197 +++
 .../seaborne/dboe/transaction/TestTxnLib2.java  |   65 +
 .../dboe/transaction/TestTxnSwitching.java      |  192 +++
 .../dboe/transaction/TransactionalInteger.java  |   59 +
 .../src/test/resources/log4j.properties         |    6 +
 jena-db/fuseki-tdb2/pom.xml                     |  129 ++
 .../org/seaborne/fuseki/cmd/FusekiTDB2Cmd.java  |   33 +
 jena-db/pom.xml                                 |  421 +++++
 jena-db/tdb2-cmds/.gitignore                    |    2 +
 jena-db/tdb2-cmds/pom.xml                       |  122 ++
 jena-db/tdb2-cmds/src/main/java/tdb2/Intro.java |   31 +
 .../src/main/java/tdb2/cmdline/CmdSub.java      |   71 +
 .../src/main/java/tdb2/cmdline/CmdTDB.java      |   84 +
 .../src/main/java/tdb2/cmdline/CmdTDBGraph.java |   81 +
 .../src/main/java/tdb2/cmdline/ModLocation.java |   55 +
 .../src/main/java/tdb2/cmdline/ModModel.java    |   66 +
 .../main/java/tdb2/cmdline/ModTDBAssembler.java |   89 ++
 .../main/java/tdb2/cmdline/ModTDBDataset.java   |  138 ++
 .../tdb2-cmds/src/main/java/tdb2/tdbbackup.java |   55 +
 .../src/main/java/tdb2/tdbcompact.java          |   48 +
 .../tdb2-cmds/src/main/java/tdb2/tdbdump.java   |   66 +
 .../tdb2-cmds/src/main/java/tdb2/tdbloader.java |  175 ++
 .../tdb2-cmds/src/main/java/tdb2/tdbquery.java  |   51 +
 .../tdb2-cmds/src/main/java/tdb2/tdbstats.java  |   99 ++
 .../tdb2-cmds/src/main/java/tdb2/tdbupdate.java |   56 +
 .../src/main/resources/META-INF/LICENSE         |  175 ++
 .../src/main/resources/META-INF/NOTICE          |   10 +
 ...rg.apache.jena.system.JenaSubsystemLifecycle |    1 +
 .../org/apache/jena/tdb/tdb-properties.xml      |    8 +
 jena-db/tdb2-cmds/tdb2                          |   99 ++
 .../testing/Assembler/tdb-dataset-embed.ttl     |   23 +
 .../tdb2-cmds/testing/Assembler/tdb-dataset.ttl |   23 +
 .../testing/Assembler/tdb-graph-embed.ttl       |   27 +
 .../testing/Assembler/tdb-graph-ref-dataset.ttl |   31 +
 .../tdb2-cmds/testing/Assembler/tdb-graph.ttl   |   27 +
 .../testing/Assembler/tdb-named-graph-1.ttl     |   29 +
 .../testing/Assembler/tdb-named-graph-2.ttl     |   28 +
 jena-db/tdb2-cmds/testing/Basic/basic-00.rq     |    4 +
 jena-db/tdb2-cmds/testing/Basic/basic-1-O.rq    |    4 +
 jena-db/tdb2-cmds/testing/Basic/basic-1-P.rq    |    4 +
 jena-db/tdb2-cmds/testing/Basic/basic-1-PO.rq   |    4 +
 jena-db/tdb2-cmds/testing/Basic/basic-1-S.rq    |    4 +
 jena-db/tdb2-cmds/testing/Basic/basic-1-SO.rq   |    4 +
 jena-db/tdb2-cmds/testing/Basic/basic-1-SP.rq   |    4 +
 jena-db/tdb2-cmds/testing/Basic/basic-1-SPO.rq  |    4 +
 jena-db/tdb2-cmds/testing/Basic/basic-2-SO.rq   |    4 +
 jena-db/tdb2-cmds/testing/Basic/basic-3-O.rq    |    4 +
 jena-db/tdb2-cmds/testing/Basic/basic-3-P.rq    |    4 +
 jena-db/tdb2-cmds/testing/Basic/basic-3-PO.rq   |    4 +
 jena-db/tdb2-cmds/testing/Basic/basic-3-S.rq    |    4 +
 jena-db/tdb2-cmds/testing/Basic/basic-3-SO.rq   |    4 +
 jena-db/tdb2-cmds/testing/Basic/basic-3-SP.rq   |    4 +
 jena-db/tdb2-cmds/testing/Basic/data-1.ttl      |   17 +
 jena-db/tdb2-cmds/testing/Basic/manifest.ttl    |  101 ++
 jena-db/tdb2-cmds/testing/Data/solver-data.ttl  |    7 +
 jena-db/tdb2-cmds/testing/Loader/data-1.nq      |    1 +
 jena-db/tdb2-cmds/testing/Loader/data-2.nt      |    1 +
 jena-db/tdb2-cmds/testing/Loader/data-3.trig    |    3 +
 jena-db/tdb2-cmds/testing/Loader/data-4.ttl     |    4 +
 jena-db/tdb2-cmds/testing/Pattern/data-1.ttl    |   17 +
 jena-db/tdb2-cmds/testing/Pattern/manifest.ttl  |   35 +
 jena-db/tdb2-cmds/testing/Pattern/pattern-1.rq  |    7 +
 jena-db/tdb2-cmds/testing/Pattern/pattern-2.rq  |    8 +
 jena-db/tdb2-cmds/testing/Quads/data-1.ttl      |    5 +
 jena-db/tdb2-cmds/testing/Quads/data-2.ttl      |    5 +
 jena-db/tdb2-cmds/testing/Quads/data-dft.ttl    |   10 +
 jena-db/tdb2-cmds/testing/Quads/manifest.ttl    |   51 +
 jena-db/tdb2-cmds/testing/Quads/quad-01.rq      |    9 +
 jena-db/tdb2-cmds/testing/Quads/quad-02.rq      |    8 +
 jena-db/tdb2-cmds/testing/UnionGraph/data-1.ttl |    7 +
 jena-db/tdb2-cmds/testing/UnionGraph/data-2.ttl |    5 +
 .../tdb2-cmds/testing/UnionGraph/data-dft.ttl   |    5 +
 .../tdb2-cmds/testing/UnionGraph/manifest.ttl   |  107 ++
 .../testing/UnionGraph/merge-1-results.srx      |   57 +
 jena-db/tdb2-cmds/testing/UnionGraph/merge-1.rq |    8 +
 .../testing/UnionGraph/merge-2-results.srx      |   66 +
 jena-db/tdb2-cmds/testing/UnionGraph/merge-2.rq |    8 +
 .../testing/UnionGraph/merge-3-results.srx      |   31 +
 jena-db/tdb2-cmds/testing/UnionGraph/merge-3.rq |    8 +
 .../testing/UnionGraph/merge-4-results.srx      |   99 ++
 jena-db/tdb2-cmds/testing/UnionGraph/merge-4.rq |    8 +
 .../testing/UnionGraph/merge-5-results.srx      |   33 +
 jena-db/tdb2-cmds/testing/UnionGraph/merge-5.rq |    8 +
 .../testing/UnionGraph/merge-6-results.srx      |   43 +
 jena-db/tdb2-cmds/testing/UnionGraph/merge-6.rq |    7 +
 .../testing/UnionGraph/merge-A-results.srx      |   99 ++
 jena-db/tdb2-cmds/testing/UnionGraph/merge-A.rq |    8 +
 .../testing/UnionGraph/merge-B-results.srx      |   49 +
 jena-db/tdb2-cmds/testing/UnionGraph/merge-B.rq |    8 +
 jena-db/tdb2-cmds/testing/Update/create-1.ru    |    1 +
 jena-db/tdb2-cmds/testing/Update/drop-1.ru      |    1 +
 jena-db/tdb2-cmds/testing/Update/update-1.ru    |    3 +
 jena-db/tdb2-cmds/testing/Update/update-2.ru    |    5 +
 jena-db/tdb2-cmds/testing/Update/update-3.ru    |    7 +
 jena-db/tdb2-cmds/testing/Values/data-1.ttl     |   23 +
 jena-db/tdb2-cmds/testing/Values/manifest.ttl   |   76 +
 jena-db/tdb2-cmds/testing/Values/value-00.rq    |    1 +
 jena-db/tdb2-cmds/testing/Values/value-01.rq    |    1 +
 jena-db/tdb2-cmds/testing/Values/value-02.rq    |    1 +
 jena-db/tdb2-cmds/testing/Values/value-03.rq    |    1 +
 jena-db/tdb2-cmds/testing/Values/value-04.rq    |    1 +
 jena-db/tdb2-cmds/testing/Values/value-05.rq    |    5 +
 jena-db/tdb2-cmds/testing/Values/value-06.rq    |    6 +
 jena-db/tdb2-cmds/testing/Values/value-07.rq    |    6 +
 jena-db/tdb2-cmds/testing/Values/value-08.rq    |    6 +
 jena-db/tdb2-cmds/testing/Values/value-09.rq    |    7 +
 jena-db/tdb2-cmds/testing/manifest.ttl          |   31 +
 jena-db/tdb2/.gitignore                         |    2 +
 jena-db/tdb2/pom.xml                            |  108 ++
 .../java/org/seaborne/tdb2/DatabaseMgr.java     |  114 ++
 .../src/main/java/org/seaborne/tdb2/TDB2.java   |  268 ++++
 .../java/org/seaborne/tdb2/TDB2Factory.java     |  124 ++
 .../java/org/seaborne/tdb2/TDBException.java    |   28 +
 .../tdb2/assembler/DatasetAssemblerTDB.java     |   79 +
 .../tdb2/assembler/TDBGraphAssembler.java       |  124 ++
 .../java/org/seaborne/tdb2/assembler/Vocab.java |   36 +
 .../org/seaborne/tdb2/assembler/VocabTDB2.java  |   90 ++
 .../tdb2/graph/TransactionHandlerTDB.java       |   60 +
 .../main/java/org/seaborne/tdb2/lib/Async.java  |   84 +
 .../java/org/seaborne/tdb2/lib/NodeLib.java     |  141 ++
 .../java/org/seaborne/tdb2/lib/TupleLib.java    |  154 ++
 .../java/org/seaborne/tdb2/loader/Loader.java   |   78 +
 .../tdb2/loader/StreamRDFBatchSplit.java        |  224 +++
 .../main/java/org/seaborne/tdb2/migrate/A2.java |   37 +
 .../tdb2/migrate/TransformGraphRename.java      |   53 +
 .../seaborne/tdb2/modify/UpdateEngineTDB.java   |   52 +
 .../org/seaborne/tdb2/setup/ComponentIdMgr.java |  101 ++
 .../org/seaborne/tdb2/setup/StoreParams.java    |  441 +++++
 .../seaborne/tdb2/setup/StoreParamsBuilder.java |  308 ++++
 .../seaborne/tdb2/setup/StoreParamsCodec.java   |  189 +++
 .../seaborne/tdb2/setup/StoreParamsConst.java   |  104 ++
 .../seaborne/tdb2/setup/StoreParamsDynamic.java |   53 +
 .../seaborne/tdb2/setup/StoreParamsFactory.java |   93 ++
 .../org/seaborne/tdb2/setup/TDBBuilder.java     |  290 ++++
 .../seaborne/tdb2/setup/TDBDatasetDetails.java  |   75 +
 .../org/seaborne/tdb2/solver/Abortable.java     |   20 +
 .../org/seaborne/tdb2/solver/BindingNodeId.java |  112 ++
 .../org/seaborne/tdb2/solver/BindingTDB.java    |  154 ++
 .../seaborne/tdb2/solver/OpExecutorTDB1.java    |  427 +++++
 .../main/java/org/seaborne/tdb2/solver/QC2.java |   48 +
 .../seaborne/tdb2/solver/QueryEngineTDB.java    |  206 +++
 .../org/seaborne/tdb2/solver/QueryIterTDB.java  |   58 +
 .../org/seaborne/tdb2/solver/SolverLib.java     |  340 ++++
 .../tdb2/solver/StageGeneratorDirectTDB.java    |   57 +
 .../seaborne/tdb2/solver/StageMatchTuple.java   |  195 +++
 .../org/seaborne/tdb2/solver/stats/Stats.java   |  137 ++
 .../tdb2/solver/stats/StatsCollector.java       |   35 +
 .../tdb2/solver/stats/StatsCollectorBase.java   |   53 +
 .../tdb2/solver/stats/StatsCollectorNodeId.java |   55 +
 .../tdb2/solver/stats/StatsResults.java         |   52 +
 .../tdb2/store/DatasetGraphSwitchable.java      |  203 +++
 .../seaborne/tdb2/store/DatasetGraphTDB.java    |  480 ++++++
 .../seaborne/tdb2/store/DatasetGraphTxn.java    |   34 +
 .../tdb2/store/DatasetGraphWrapperTxn.java      |   34 +
 .../seaborne/tdb2/store/DatasetPrefixesTDB.java |  173 ++
 .../tdb2/store/GraphPrefixesProjectionTDB.java  |  163 ++
 .../java/org/seaborne/tdb2/store/GraphTDB.java  |  180 +++
 .../tdb2/store/GraphViewSwitchable.java         |  139 ++
 .../main/java/org/seaborne/tdb2/store/Hash.java |   56 +
 .../tdb2/store/IteratorCheckNotConcurrent.java  |   93 ++
 .../seaborne/tdb2/store/IteratorTxnTracker.java |   55 +
 .../java/org/seaborne/tdb2/store/NodeId.java    |  219 +++
 .../org/seaborne/tdb2/store/NodeIdFactory.java  |  260 +++
 .../org/seaborne/tdb2/store/NodeIdInline.java   |  328 ++++
 .../org/seaborne/tdb2/store/NodeIdType.java     |  259 +++
 .../seaborne/tdb2/store/PrefixMappingCache.java |  202 +++
 .../java/org/seaborne/tdb2/store/QuadTable.java |   90 ++
 .../org/seaborne/tdb2/store/StorageTDB.java     |   41 +
 .../java/org/seaborne/tdb2/store/TableBase.java |   48 +
 .../org/seaborne/tdb2/store/TripleTable.java    |   74 +
 .../tdb2/store/nodetable/NodeTable.java         |   81 +
 .../tdb2/store/nodetable/NodeTableCache.java    |  352 ++++
 .../tdb2/store/nodetable/NodeTableInline.java   |   70 +
 .../tdb2/store/nodetable/NodeTableNative.java   |  214 +++
 .../tdb2/store/nodetable/NodeTableOps.java      |   50 +
 .../tdb2/store/nodetable/NodeTableTRDF.java     |  108 ++
 .../tdb2/store/nodetable/NodeTableWrapper.java  |   93 ++
 .../nodetable/TReadAppendFileTransport.java     |   85 +
 .../store/nodetupletable/NodeTupleTable.java    |   71 +
 .../nodetupletable/NodeTupleTableConcrete.java  |  240 +++
 .../nodetupletable/NodeTupleTableView.java      |  112 ++
 .../nodetupletable/NodeTupleTableWrapper.java   |   97 ++
 .../tdb2/store/tupletable/TupleIndex.java       |   87 +
 .../tdb2/store/tupletable/TupleIndexBase.java   |  123 ++
 .../tdb2/store/tupletable/TupleIndexRecord.java |  237 +++
 .../TupleIndexRecordAsyncBulkAdd.java           |  262 +++
 .../store/tupletable/TupleIndexWrapper.java     |  117 ++
 .../tdb2/store/tupletable/TupleTable.java       |  218 +++
 .../seaborne/tdb2/store/value/DateTimeNode.java |  253 +++
 .../seaborne/tdb2/store/value/DecimalNode.java  |   22 +
 .../tdb2/store/value/DecimalNode56.java         |  140 ++
 .../seaborne/tdb2/store/value/DoubleNode62.java |  241 +++
 .../seaborne/tdb2/store/value/FloatNode.java    |   31 +
 .../seaborne/tdb2/store/value/IntegerNode.java  |   63 +
 .../seaborne/tdb2/sys/ConnectionTracker.java    |   23 +
 .../java/org/seaborne/tdb2/sys/CopyDSG.java     |   53 +
 .../seaborne/tdb2/sys/DatabaseConnection.java   |  180 +++
 .../java/org/seaborne/tdb2/sys/DatabaseOps.java |  264 +++
 .../main/java/org/seaborne/tdb2/sys/EnvTDB.java |   61 +
 .../org/seaborne/tdb2/sys/FilenameUtils.java    |  102 ++
 .../main/java/org/seaborne/tdb2/sys/IOX.java    |  292 ++++
 .../java/org/seaborne/tdb2/sys/InitTDB2.java    |   40 +
 .../org/seaborne/tdb2/sys/LockMRSWLite.java     |   69 +
 .../org/seaborne/tdb2/sys/StoreConnection.java  |  194 +++
 .../java/org/seaborne/tdb2/sys/SystemTDB.java   |  416 +++++
 .../java/org/seaborne/tdb2/sys/TDBInternal.java |  174 ++
 .../main/java/org/seaborne/tdb2/sys/Util.java   |   49 +
 .../tdb2/src/main/resources/META-INF/LICENSE    |  175 ++
 jena-db/tdb2/src/main/resources/META-INF/NOTICE |   10 +
 ...rg.apache.jena.system.JenaSubsystemLifecycle |    1 +
 .../org/apache/jena/tdb/tdb-properties.xml      |    8 +
 .../test/java/org/seaborne/tdb2/ConfigTest.java |   95 ++
 .../src/test/java/org/seaborne/tdb2/TC_TDB.java |   80 +
 .../test/java/org/seaborne/tdb2/TS_Factory.java |   32 +
 .../java/org/seaborne/tdb2/TestDatabaseMgr.java |   89 ++
 .../java/org/seaborne/tdb2/TestTDBFactory.java  |  130 ++
 .../tdb2/assembler/TS_TDBAssembler.java         |   32 +
 .../tdb2/assembler/TestTDBAssembler.java        |  142 ++
 .../tdb2/graph/AbstractTestGraphsTDB.java       |   50 +
 .../java/org/seaborne/tdb2/graph/TS_Graph.java  |   35 +
 .../tdb2/graph/TestDatasetGraphAccessorTDB.java |   47 +
 .../tdb2/graph/TestDatasetGraphTDB.java         |   52 +
 .../tdb2/graph/TestGraphOverDatasetTDB.java     |   63 +
 .../org/seaborne/tdb2/graph/TestGraphsTDB1.java |   42 +
 .../org/seaborne/tdb2/graph/TestGraphsTDB2.java |   51 +
 .../tdb2/graph/TestPrefixMappingTDB.java        |  106 ++
 .../tdb2/junit/AbstractTestGraphTDB.java        |   33 +
 .../java/org/seaborne/tdb2/junit/Base_TS.java   |   51 +
 .../org/seaborne/tdb2/junit/BuildTestLib.java   |   81 +
 .../org/seaborne/tdb2/junit/QueryTestTDB.java   |  192 +++
 .../seaborne/tdb2/junit/TDBTestException.java   |   28 +
 .../test/java/org/seaborne/tdb2/junit/TL.java   |   89 ++
 .../org/seaborne/tdb2/junit/TestFactoryTDB.java |   87 +
 .../java/org/seaborne/tdb2/lib/TS_LibTDB.java   |   33 +
 .../java/org/seaborne/tdb2/lib/TestNodeLib.java |   69 +
 .../org/seaborne/tdb2/setup/TS_TDBSetup.java    |   32 +
 .../seaborne/tdb2/setup/TestStoreParams.java    |  144 ++
 .../tdb2/setup/TestStoreParamsChoose.java       |  157 ++
 .../tdb2/setup/TestStoreParamsCreate.java       |  180 +++
 .../org/seaborne/tdb2/solver/TS_SolverTDB.java  |   30 +
 .../org/seaborne/tdb2/solver/TestSolverTDB.java |  171 ++
 .../org/seaborne/tdb2/solver/TestStats.java     |  117 ++
 .../AbstractTestStoreConnectionBasics.java      |  185 +++
 .../store/AbstractTestTransPromoteTDB2.java     |   71 +
 .../java/org/seaborne/tdb2/store/TS_Store.java  |   71 +
 .../org/seaborne/tdb2/store/TestDatasetTDB.java |  323 ++++
 .../tdb2/store/TestDatasetTDBPersist.java       |  111 ++
 .../tdb2/store/TestDynamicDatasetTDB.java       |   44 +
 .../seaborne/tdb2/store/TestGraphNamedTDB.java  |   60 +
 .../org/seaborne/tdb2/store/TestGraphTDB.java   |   57 +
 .../org/seaborne/tdb2/store/TestNodeId.java     |  127 ++
 .../org/seaborne/tdb2/store/TestQuadFilter.java |  110 ++
 .../tdb2/store/TestStoreConnectionDirect.java   |   47 +
 .../tdb2/store/TestStoreConnectionLock.java     |   71 +
 .../tdb2/store/TestStoreConnectionMapped.java   |   50 +
 .../tdb2/store/TestStoreConnectionMem.java      |   28 +
 .../seaborne/tdb2/store/TestSuiteGraphTDB.java  |   40 +
 .../tdb2/store/TestTransPromoteTDB.java         |   72 +
 .../tdb2/store/TestTransactionLifecycleTDB.java |   33 +
 .../seaborne/tdb2/store/TestTransactions.java   |  204 +++
 .../seaborne/tdb2/store/TestTripleTable.java    |  132 ++
 .../seaborne/tdb2/store/Test_SPARQL_TDB.java    |  221 +++
 .../store/nodetable/AbstractTestNodeTable.java  |   66 +
 .../tdb2/store/nodetable/TS_NodeTable.java      |   33 +
 .../tdb2/store/nodetable/TestNodeTable.java     |   37 +
 .../tdb2/store/nodetable/TestNodeTableBase.java |   39 +
 .../store/nodetable/TestNodeTableStored.java    |   45 +
 .../nodetable/TestNodeTableStoredBase.java      |   46 +
 .../tupletable/AbstractTestTupleIndex.java      |  264 +++
 .../seaborne/tdb2/store/tupletable/NData.java   |   37 +
 .../tdb2/store/tupletable/TS_TupleTable.java    |   31 +
 .../store/tupletable/TestTupleIndexRecord.java  |   42 +
 .../tupletable/TestTupleIndexRecordDirect.java  |  314 ++++
 .../tdb2/store/tupletable/TestTupleTable.java   |  151 ++
 .../tdb2/store/value/TestDoubleNode62.java      |  123 ++
 .../tdb2/store/value/TestNodeIdInline.java      |  285 ++++
 .../test/java/org/seaborne/tdb2/sys/TS_Sys.java |   33 +
 .../tdb2/sys/TestDatabaseConnection.java        |   69 +
 .../org/seaborne/tdb2/sys/TestDatabaseOps.java  |  172 ++
 .../java/org/seaborne/tdb2/sys/TestOps.java     |   31 +
 .../java/org/seaborne/tdb2/sys/TestSys.java     |   32 +
 .../testing/Assembler/tdb-dataset-embed.ttl     |   23 +
 jena-db/tdb2/testing/Assembler/tdb-dataset.ttl  |   23 +
 .../tdb2/testing/Assembler/tdb-graph-embed.ttl  |   27 +
 .../testing/Assembler/tdb-graph-ref-dataset.ttl |   31 +
 jena-db/tdb2/testing/Assembler/tdb-graph.ttl    |   27 +
 .../testing/Assembler/tdb-named-graph-1.ttl     |   29 +
 .../testing/Assembler/tdb-named-graph-2.ttl     |   28 +
 jena-db/tdb2/testing/Basic/basic-00.rq          |    4 +
 jena-db/tdb2/testing/Basic/basic-1-O.rq         |    4 +
 jena-db/tdb2/testing/Basic/basic-1-P.rq         |    4 +
 jena-db/tdb2/testing/Basic/basic-1-PO.rq        |    4 +
 jena-db/tdb2/testing/Basic/basic-1-S.rq         |    4 +
 jena-db/tdb2/testing/Basic/basic-1-SO.rq        |    4 +
 jena-db/tdb2/testing/Basic/basic-1-SP.rq        |    4 +
 jena-db/tdb2/testing/Basic/basic-1-SPO.rq       |    4 +
 jena-db/tdb2/testing/Basic/basic-2-SO.rq        |    4 +
 jena-db/tdb2/testing/Basic/basic-3-O.rq         |    4 +
 jena-db/tdb2/testing/Basic/basic-3-P.rq         |    4 +
 jena-db/tdb2/testing/Basic/basic-3-PO.rq        |    4 +
 jena-db/tdb2/testing/Basic/basic-3-S.rq         |    4 +
 jena-db/tdb2/testing/Basic/basic-3-SO.rq        |    4 +
 jena-db/tdb2/testing/Basic/basic-3-SP.rq        |    4 +
 jena-db/tdb2/testing/Basic/data-1.ttl           |   17 +
 jena-db/tdb2/testing/Basic/manifest.ttl         |  101 ++
 jena-db/tdb2/testing/Data/solver-data.ttl       |    7 +
 jena-db/tdb2/testing/Loader/data-1.nq           |    1 +
 jena-db/tdb2/testing/Loader/data-2.nt           |    1 +
 jena-db/tdb2/testing/Loader/data-3.trig         |    3 +
 jena-db/tdb2/testing/Loader/data-4.ttl          |    4 +
 jena-db/tdb2/testing/Pattern/data-1.ttl         |   17 +
 jena-db/tdb2/testing/Pattern/manifest.ttl       |   35 +
 jena-db/tdb2/testing/Pattern/pattern-1.rq       |    7 +
 jena-db/tdb2/testing/Pattern/pattern-2.rq       |    8 +
 jena-db/tdb2/testing/Quads/data-1.ttl           |    5 +
 jena-db/tdb2/testing/Quads/data-2.ttl           |    5 +
 jena-db/tdb2/testing/Quads/data-dft.ttl         |   10 +
 jena-db/tdb2/testing/Quads/manifest.ttl         |   51 +
 jena-db/tdb2/testing/Quads/quad-01.rq           |    9 +
 jena-db/tdb2/testing/Quads/quad-02.rq           |    8 +
 jena-db/tdb2/testing/UnionGraph/data-1.ttl      |    7 +
 jena-db/tdb2/testing/UnionGraph/data-2.ttl      |    5 +
 jena-db/tdb2/testing/UnionGraph/data-dft.ttl    |    5 +
 jena-db/tdb2/testing/UnionGraph/manifest.ttl    |  107 ++
 .../tdb2/testing/UnionGraph/merge-1-results.srx |   57 +
 jena-db/tdb2/testing/UnionGraph/merge-1.rq      |    8 +
 .../tdb2/testing/UnionGraph/merge-2-results.srx |   66 +
 jena-db/tdb2/testing/UnionGraph/merge-2.rq      |    8 +
 .../tdb2/testing/UnionGraph/merge-3-results.srx |   31 +
 jena-db/tdb2/testing/UnionGraph/merge-3.rq      |    8 +
 .../tdb2/testing/UnionGraph/merge-4-results.srx |   99 ++
 jena-db/tdb2/testing/UnionGraph/merge-4.rq      |    8 +
 .../tdb2/testing/UnionGraph/merge-5-results.srx |   33 +
 jena-db/tdb2/testing/UnionGraph/merge-5.rq      |    8 +
 .../tdb2/testing/UnionGraph/merge-6-results.srx |   43 +
 jena-db/tdb2/testing/UnionGraph/merge-6.rq      |    7 +
 .../tdb2/testing/UnionGraph/merge-A-results.srx |   99 ++
 jena-db/tdb2/testing/UnionGraph/merge-A.rq      |    8 +
 .../tdb2/testing/UnionGraph/merge-B-results.srx |   49 +
 jena-db/tdb2/testing/UnionGraph/merge-B.rq      |    8 +
 jena-db/tdb2/testing/Update/create-1.ru         |    1 +
 jena-db/tdb2/testing/Update/drop-1.ru           |    1 +
 jena-db/tdb2/testing/Update/update-1.ru         |    3 +
 jena-db/tdb2/testing/Update/update-2.ru         |    5 +
 jena-db/tdb2/testing/Update/update-3.ru         |    7 +
 jena-db/tdb2/testing/Values/data-1.ttl          |   23 +
 jena-db/tdb2/testing/Values/manifest.ttl        |   76 +
 jena-db/tdb2/testing/Values/value-00.rq         |    1 +
 jena-db/tdb2/testing/Values/value-01.rq         |    1 +
 jena-db/tdb2/testing/Values/value-02.rq         |    1 +
 jena-db/tdb2/testing/Values/value-03.rq         |    1 +
 jena-db/tdb2/testing/Values/value-04.rq         |    1 +
 jena-db/tdb2/testing/Values/value-05.rq         |    5 +
 jena-db/tdb2/testing/Values/value-06.rq         |    6 +
 jena-db/tdb2/testing/Values/value-07.rq         |    6 +
 jena-db/tdb2/testing/Values/value-08.rq         |    6 +
 jena-db/tdb2/testing/Values/value-09.rq         |    7 +
 jena-db/tdb2/testing/manifest.ttl               |   31 +
 jena-db/use-fuseki-tdb2.md                      |   60 +
 jena-db/use-tdb2-cmds.md                        |   21 +
 jena-db/use-tdb2.md                             |   27 +
 598 files changed, 55053 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/.gitignore
----------------------------------------------------------------------
diff --cc jena-db/.gitignore
index 0000000,0000000..18ee681
new file mode 100644
--- /dev/null
+++ b/jena-db/.gitignore
@@@ -1,0 -1,0 +1,46 @@@
++*.class
++
++## Package Files
++*.jar
++*.war
++*.ear
++
++hs_err_pid*
++tmp/
++*.bak
++
++## From https://www.gitignore.io
++
++### Maven ###
++target/
++pom.xml.tag
++pom.xml.releaseBackup
++pom.xml.versionsBackup
++pom.xml.next
++release.properties
++dependency-reduced-pom.xml
++buildNumber.properties
++.mvn/timing.properties
++
++### Gradle ###
++**/.gradle/
++build/
++# Ignore Gradle GUI config
++gradle-app.setting
++# Don't ignore the Gradle wrapper jar file
++!gradle-wrapper.jar
++# Cache of project
++.gradletasknamecache
++
++
++## Eclipse
++.classpath
++.project
++.settings/
++.recommenders
++.metadata
++
++## Database files.
++DB*/
++## Fuseki area
++run/

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/.travis.yml
----------------------------------------------------------------------
diff --cc jena-db/.travis.yml
index 0000000,0000000..d7b228d
new file mode 100644
--- /dev/null
+++ b/jena-db/.travis.yml
@@@ -1,0 -1,0 +1,17 @@@
++---
++language: java
++jdk:
++  - oraclejdk8
++
++notifications:
++  email:
++    # [always|never|change]
++    on_success: change
++    on_failure: change
++
++## sudo: false
++## install: mvn dependency:go-offline
++## script: mvn verify
++## env:
++##   global:
++##     - JAVA_OPTS="-Xmx3072M -Xms512M -XX:+UseG1GC"

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/LICENSE
----------------------------------------------------------------------
diff --cc jena-db/LICENSE
index 0000000,0000000..e06d208
new file mode 100644
--- /dev/null
+++ b/jena-db/LICENSE
@@@ -1,0 -1,0 +1,202 @@@
++Apache License
++                           Version 2.0, January 2004
++                        http://www.apache.org/licenses/
++
++   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
++
++   1. Definitions.
++
++      "License" shall mean the terms and conditions for use, reproduction,
++      and distribution as defined by Sections 1 through 9 of this document.
++
++      "Licensor" shall mean the copyright owner or entity authorized by
++      the copyright owner that is granting the License.
++
++      "Legal Entity" shall mean the union of the acting entity and all
++      other entities that control, are controlled by, or are under common
++      control with that entity. For the purposes of this definition,
++      "control" means (i) the power, direct or indirect, to cause the
++      direction or management of such entity, whether by contract or
++      otherwise, or (ii) ownership of fifty percent (50%) or more of the
++      outstanding shares, or (iii) beneficial ownership of such entity.
++
++      "You" (or "Your") shall mean an individual or Legal Entity
++      exercising permissions granted by this License.
++
++      "Source" form shall mean the preferred form for making modifications,
++      including but not limited to software source code, documentation
++      source, and configuration files.
++
++      "Object" form shall mean any form resulting from mechanical
++      transformation or translation of a Source form, including but
++      not limited to compiled object code, generated documentation,
++      and conversions to other media types.
++
++      "Work" shall mean the work of authorship, whether in Source or
++      Object form, made available under the License, as indicated by a
++      copyright notice that is included in or attached to the work
++      (an example is provided in the Appendix below).
++
++      "Derivative Works" shall mean any work, whether in Source or Object
++      form, that is based on (or derived from) the Work and for which the
++      editorial revisions, annotations, elaborations, or other modifications
++      represent, as a whole, an original work of authorship. For the purposes
++      of this License, Derivative Works shall not include works that remain
++      separable from, or merely link (or bind by name) to the interfaces of,
++      the Work and Derivative Works thereof.
++
++      "Contribution" shall mean any work of authorship, including
++      the original version of the Work and any modifications or additions
++      to that Work or Derivative Works thereof, that is intentionally
++      submitted to Licensor for inclusion in the Work by the copyright owner
++      or by an individual or Legal Entity authorized to submit on behalf of
++      the copyright owner. For the purposes of this definition, "submitted"
++      means any form of electronic, verbal, or written communication sent
++      to the Licensor or its representatives, including but not limited to
++      communication on electronic mailing lists, source code control systems,
++      and issue tracking systems that are managed by, or on behalf of, the
++      Licensor for the purpose of discussing and improving the Work, but
++      excluding communication that is conspicuously marked or otherwise
++      designated in writing by the copyright owner as "Not a Contribution."
++
++      "Contributor" shall mean Licensor and any individual or Legal Entity
++      on behalf of whom a Contribution has been received by Licensor and
++      subsequently incorporated within the Work.
++
++   2. Grant of Copyright License. Subject to the terms and conditions of
++      this License, each Contributor hereby grants to You a perpetual,
++      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
++      copyright license to reproduce, prepare Derivative Works of,
++      publicly display, publicly perform, sublicense, and distribute the
++      Work and such Derivative Works in Source or Object form.
++
++   3. Grant of Patent License. Subject to the terms and conditions of
++      this License, each Contributor hereby grants to You a perpetual,
++      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
++      (except as stated in this section) patent license to make, have made,
++      use, offer to sell, sell, import, and otherwise transfer the Work,
++      where such license applies only to those patent claims licensable
++      by such Contributor that are necessarily infringed by their
++      Contribution(s) alone or by combination of their Contribution(s)
++      with the Work to which such Contribution(s) was submitted. If You
++      institute patent litigation against any entity (including a
++      cross-claim or counterclaim in a lawsuit) alleging that the Work
++      or a Contribution incorporated within the Work constitutes direct
++      or contributory patent infringement, then any patent licenses
++      granted to You under this License for that Work shall terminate
++      as of the date such litigation is filed.
++
++   4. Redistribution. You may reproduce and distribute copies of the
++      Work or Derivative Works thereof in any medium, with or without
++      modifications, and in Source or Object form, provided that You
++      meet the following conditions:
++
++      (a) You must give any other recipients of the Work or
++          Derivative Works a copy of this License; and
++
++      (b) You must cause any modified files to carry prominent notices
++          stating that You changed the files; and
++
++      (c) You must retain, in the Source form of any Derivative Works
++          that You distribute, all copyright, patent, trademark, and
++          attribution notices from the Source form of the Work,
++          excluding those notices that do not pertain to any part of
++          the Derivative Works; and
++
++      (d) If the Work includes a "NOTICE" text file as part of its
++          distribution, then any Derivative Works that You distribute must
++          include a readable copy of the attribution notices contained
++          within such NOTICE file, excluding those notices that do not
++          pertain to any part of the Derivative Works, in at least one
++          of the following places: within a NOTICE text file distributed
++          as part of the Derivative Works; within the Source form or
++          documentation, if provided along with the Derivative Works; or,
++          within a display generated by the Derivative Works, if and
++          wherever such third-party notices normally appear. The contents
++          of the NOTICE file are for informational purposes only and
++          do not modify the License. You may add Your own attribution
++          notices within Derivative Works that You distribute, alongside
++          or as an addendum to the NOTICE text from the Work, provided
++          that such additional attribution notices cannot be construed
++          as modifying the License.
++
++      You may add Your own copyright statement to Your modifications and
++      may provide additional or different license terms and conditions
++      for use, reproduction, or distribution of Your modifications, or
++      for any such Derivative Works as a whole, provided Your use,
++      reproduction, and distribution of the Work otherwise complies with
++      the conditions stated in this License.
++
++   5. Submission of Contributions. Unless You explicitly state otherwise,
++      any Contribution intentionally submitted for inclusion in the Work
++      by You to the Licensor shall be under the terms and conditions of
++      this License, without any additional terms or conditions.
++      Notwithstanding the above, nothing herein shall supersede or modify
++      the terms of any separate license agreement you may have executed
++      with Licensor regarding such Contributions.
++
++   6. Trademarks. This License does not grant permission to use the trade
++      names, trademarks, service marks, or product names of the Licensor,
++      except as required for reasonable and customary use in describing the
++      origin of the Work and reproducing the content of the NOTICE file.
++
++   7. Disclaimer of Warranty. Unless required by applicable law or
++      agreed to in writing, Licensor provides the Work (and each
++      Contributor provides its Contributions) on an "AS IS" BASIS,
++      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
++      implied, including, without limitation, any warranties or conditions
++      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
++      PARTICULAR PURPOSE. You are solely responsible for determining the
++      appropriateness of using or redistributing the Work and assume any
++      risks associated with Your exercise of permissions under this License.
++
++   8. Limitation of Liability. In no event and under no legal theory,
++      whether in tort (including negligence), contract, or otherwise,
++      unless required by applicable law (such as deliberate and grossly
++      negligent acts) or agreed to in writing, shall any Contributor be
++      liable to You for damages, including any direct, indirect, special,
++      incidental, or consequential damages of any character arising as a
++      result of this License or out of the use or inability to use the
++      Work (including but not limited to damages for loss of goodwill,
++      work stoppage, computer failure or malfunction, or any and all
++      other commercial damages or losses), even if such Contributor
++      has been advised of the possibility of such damages.
++
++   9. Accepting Warranty or Additional Liability. While redistributing
++      the Work or Derivative Works thereof, You may choose to offer,
++      and charge a fee for, acceptance of support, warranty, indemnity,
++      or other liability obligations and/or rights consistent with this
++      License. However, in accepting such obligations, You may act only
++      on Your own behalf and on Your sole responsibility, not on behalf
++      of any other Contributor, and only if You agree to indemnify,
++      defend, and hold each Contributor harmless for any liability
++      incurred by, or claims asserted against, such Contributor by reason
++      of your accepting any such warranty or additional liability.
++
++   END OF TERMS AND CONDITIONS
++
++   APPENDIX: How to apply the Apache License to your work.
++
++      To apply the Apache License to your work, attach the following
++      boilerplate notice, with the fields enclosed by brackets "{}"
++      replaced with your own identifying information. (Don't include
++      the brackets!)  The text should be enclosed in the appropriate
++      comment syntax for the file format. We also recommend that a
++      file or class name and description of purpose be included on the
++      same "printed page" as the copyright notice for easier
++      identification within third-party archives.
++
++   Copyright {yyyy} {name of copyright owner}
++
++   Licensed 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.
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/NOTICE
----------------------------------------------------------------------
diff --cc jena-db/NOTICE
index 0000000,0000000..5ba7ec0
new file mode 100644
--- /dev/null
+++ b/jena-db/NOTICE
@@@ -1,0 -1,0 +1,10 @@@
++Mantis Database Operating Environment
++
++Copyright 2013-2017 Andy Seaborne
++Copyright 2014, 2015 Epimorphics Ltd.
++
++Portions of this software derive from software from Apache Jena:
++  - Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP
++  - Copyright 2010, 2011 Epimorphics Ltd.
++  - Copyright 2010, 2011 Talis Systems Ltd.
++These have been licensed to the Apache Software Foundation under a software grant.

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/README.md
----------------------------------------------------------------------
diff --cc jena-db/README.md
index 0000000,0000000..f9c3d43
new file mode 100644
--- /dev/null
+++ b/jena-db/README.md
@@@ -1,0 -1,0 +1,64 @@@
++[![Build Status](https://api.travis-ci.org/afs/mantis.png)](https://travis-ci.org/afs/mantis)
++
++# Mantis - Database Operating Environment
++
++Mantis is a set of database-related components. It includes:
++
++* Transaction Coordination
++* Copy-on-write data structures
++* Query algebra evaluation library
++* Abstraction of basic file operations
++
++TDB2 is a reworking for TDB that provides scalable transactions as well
++as refining the machinary for reliability.
++
++License: Apache License 
++
++See [LICENSE](LICENSE) and [NOTICE](NOTICE) for details.
++
++Continuous integration: [TravisCI](https://travis-ci.org/afs/mantis)
++
++Notes on using [TDB2 in a Java application](use-tdb2.md).
++
++Notes on using [TDB2 from the command line](use-tdb2-cmds.md).
++
++Notes on using [TDB2 with Apache Jena Fuseki](use-fuseki-tdb2.md).
++
++Status: currently snapshot builds only.
++
++It depends on [Apache Jena](https://jena.apache.org/) snapshots.
++
++Maven repositories setup:
++
++```
++  <repositories>
++    <!-- Apache Snapshot Repository -->
++    <repository>
++      <id>apache-repository-snapshots</id>
++      <url>https://repository.apache.org/snapshots</url>
++      <releases>
++        <enabled>false</enabled>
++      </releases>
++      <snapshots>
++        <enabled>true</enabled>
++        <updatePolicy>daily</updatePolicy>
++        <checksumPolicy>fail</checksumPolicy>
++      </snapshots>
++    </repository>
++
++    <!-- Sonatype snapshot repository -->
++    <repository>
++      <id>sonatype.public</id>
++      <name>Sonatype Snapshots Repository</name>
++      <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
++      <releases>
++        <enabled>false</enabled>
++      </releases>
++      <snapshots>
++        <enabled>true</enabled>
++      </snapshots>
++    </repository>
++
++  </repositories>
++```
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/.gitignore
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/.gitignore
index 0000000,0000000..b49c563
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/.gitignore
@@@ -1,0 -1,0 +1,4 @@@
++/target/
++/target/
++/target/
++/target/

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/README.md
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/README.md
index 0000000,0000000..5969da0
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/README.md
@@@ -1,0 -1,0 +1,1 @@@
++Basic file-level abstractions.

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/pom.xml
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/pom.xml
index 0000000,0000000..986c95f
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/pom.xml
@@@ -1,0 -1,0 +1,64 @@@
++<?xml version="1.0" encoding="utf-8"?>
++<!--
++   Licensed 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.
++ 
++   See the NOTICE file distributed with this work for additional
++   information regarding copyright ownership.
++-->
++
++<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
++  <modelVersion>4.0.0</modelVersion>
++
++  <artifactId>dboe-base</artifactId>
++  <packaging>jar</packaging>
++  <name>Mantis (${project.artifactId})</name>
++
++  <parent>
++    <groupId>org.seaborne.mantis</groupId>
++    <artifactId>mantis</artifactId>
++    <version>0.3.1-SNAPSHOT</version>
++  </parent> 
++
++  <dependencies>
++
++    <dependency>
++      <groupId>org.apache.jena</groupId>
++      <artifactId>jena-arq</artifactId>
++    </dependency>
++
++    <dependency>
++      <groupId>org.apache.jena</groupId>
++      <artifactId>jena-arq</artifactId>
++      <classifier>tests</classifier>
++      <scope>test</scope>
++    </dependency>
++
++  </dependencies>
++
++  <build>
++    <plugins>
++      <plugin>
++        <groupId>org.apache.maven.plugins</groupId>
++        <artifactId>maven-jar-plugin</artifactId>
++        <executions>
++          <execution>
++            <goals>
++              <goal>test-jar</goal>
++            </goals>
++          </execution>
++        </executions>
++      </plugin>
++    </plugins>
++  </build>
++  
++</project>

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/DBOpEnvException.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/DBOpEnvException.java
index 0000000,0000000..77b489f
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/DBOpEnvException.java
@@@ -1,0 -1,0 +1,28 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe;
++
++import org.apache.jena.shared.JenaException;
++
++public class DBOpEnvException extends JenaException
++{
++    public DBOpEnvException()                          { super() ; }
++    public DBOpEnvException(String msg)                { super(msg) ; }
++    public DBOpEnvException(Throwable th)              { super(th) ; }
++    public DBOpEnvException(String msg, Throwable th)  { super(msg, th) ; }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/StorageException.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/StorageException.java
index 0000000,0000000..c67c618
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/StorageException.java
@@@ -1,0 -1,0 +1,26 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base;
++
++public class StorageException extends RuntimeException
++{
++    public StorageException()                          { super() ; }
++    public StorageException(String msg)                { super(msg) ; }
++    public StorageException(Throwable th)              { super(th) ; }
++    public StorageException(String msg, Throwable th)  { super(msg, th) ; }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/Block.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/Block.java
index 0000000,0000000..043b043
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/Block.java
@@@ -1,0 -1,0 +1,165 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.block;
++
++import java.io.ByteArrayOutputStream ;
++import java.io.PrintStream ;
++import java.nio.ByteBuffer ;
++import org.apache.jena.atlas.lib.ByteBufferLib ;
++
++public final class Block
++{
++    // XXX Note development code added to enforce read-only use. Marked [RO]
++    
++    // While the general mechanisms support long block id,
++    // some uses make restrictions.
++    // BlockMgrs:
++    //   Blocks for indexes (B+Trees) are addressed by int - one int is 2G of 8K units = 16T
++    // Blocks for objects are addressed by long - this is file offset so 2G is not enough. 
++    
++    private final Long id ;          // Keep as object.  It's the cache key.
++    //private BlockRef blockRef ;
++    
++
++    // These are information carrying flags -- not enforced
++    // although .write checks the readOnly flags.
++    private boolean readOnly = false ;
++    private boolean modified = false ;
++    
++    private /*[[Dev-RO]] final*/ ByteBuffer byteBuffer ;
++    // If the byteBuffer is, say, a slice of another one,
++    // this can be used to carry a ref to the real ByteBuffer.  
++    private ByteBuffer underlyingByteBuffer ;
++
++    public Block(long id, ByteBuffer byteBuffer) {
++        this(Long.valueOf(id), byteBuffer) ;
++    }
++
++    public Block(Long id, ByteBuffer byteBuffer) {
++        // ByteBuffer is whole disk space from byte 0 for this disk unit.
++        this.id = id ;
++        this.byteBuffer = byteBuffer ;
++        // this.blockRef = null ;
++        // Default - writeable, unmodified block.
++        this.readOnly = false ;
++        this.modified = false ;
++        this.underlyingByteBuffer = null ;
++    }
++
++    public final Long getId() {
++        return id ;
++    }
++
++    public final ByteBuffer getByteBuffer() {
++        return byteBuffer ;
++    }
++
++    public boolean isReadOnly() {
++        return readOnly ;
++    }
++
++    public void setReadOnly(boolean readonly) {
++        if ( readonly && modified )
++            throw new BlockException("Attempt to mark a modified block as read-only") ;
++        // Hard to force a read-only byte buffer because higher levels
++        // may slice this and have their own byte buffers or even 
++        // intBuffers etc.
++        this.readOnly = readonly ;
++    }
++
++    public boolean isModified() {
++        return modified ;
++    }
++
++    public void setModified(boolean modified) {
++        if ( readOnly && modified )
++            throw new BlockException("Attempt to mark a readonly block as modified") ;
++        this.modified = modified ;
++    }
++
++    public ByteBuffer getUnderlyingByteBuffer()
++    { return underlyingByteBuffer ; }
++
++    public void setUnderlyingByteBuffer(ByteBuffer underlyingByteBuffer)
++    { this.underlyingByteBuffer = underlyingByteBuffer ; }
++    
++    @Override
++    public String toString() {
++        ByteBuffer bb = getByteBuffer() ;
++        if ( true )
++            // Short form.
++            return String.format("Block: %d (posn=%d, limit=%d, cap=%d)", id, bb.position(), bb.limit(), bb.capacity()) ;
++        // Long form - with some bytes from the ByteBuffer.
++        ByteArrayOutputStream out = new ByteArrayOutputStream() ;
++        PrintStream x = new PrintStream(out) ;
++        ByteBufferLib.print(x, bb) ;
++        x.flush() ;
++        String str = out.toString() ;
++        return String.format("Block: %d %s", id, str) ;
++    }
++
++    /** Deep copy, including ByteBuffer contents into a HeapByteBuffer. */
++    public Block replicate() {
++        ByteBuffer dstBuffer = ByteBuffer.allocate(getByteBuffer().capacity()) ;
++        return replicate(dstBuffer) ;
++    }
++    
++    /**
++     * Deep copy, including ByteBuffer contents, using the supplied ByteBuffer
++     * to hold the contents and to be used when constructing the new Block. The
++     * capacity of the supplied ByteBuffer must be equal to or greater than this
++     * block's capacity.
++     */
++    private Block replicate(ByteBuffer dstBuffer) {
++        replicateByteBuffer(getByteBuffer(), dstBuffer) ;
++        Block b = new Block(getId(), dstBuffer) ;
++        b.setModified(modified) ;
++        b.setReadOnly(readOnly) ;
++        // b.blockRef = null ;
++        return b ;
++    }
++
++    public static void replicate(Block srcBlock, Block dstBlock) {
++        if ( !srcBlock.getId().equals(dstBlock.getId()) )
++            throw new BlockException("FileAccessMem: Attempt to copy across blocks: " + srcBlock.getId() + " => " + dstBlock.getId()) ;
++        replicate(srcBlock.getByteBuffer(), dstBlock.getByteBuffer()) ;
++    }
++
++    private static ByteBuffer replicateByteBuffer(ByteBuffer srcBlk, ByteBuffer dstBlk) {
++        int x = srcBlk.position() ;
++        int y = srcBlk.limit() ;
++        srcBlk.clear() ;
++
++        if ( srcBlk.hasArray() && dstBlk.hasArray() )
++            System.arraycopy(srcBlk.array(), 0, dstBlk.array(), 0, srcBlk.capacity()) ;
++        else
++            dstBlk.put(srcBlk) ;
++
++        srcBlk.position(x) ;
++        dstBlk.position(x) ;
++        srcBlk.limit(y) ;
++        dstBlk.limit(y) ;
++        return dstBlk ;
++    }
++
++    private static void replicate(ByteBuffer srcBlk, ByteBuffer dstBlk) {
++        srcBlk.position(0) ;
++        dstBlk.position(0) ;
++        dstBlk.put(srcBlk) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockException.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockException.java
index 0000000,0000000..5a7ab89
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockException.java
@@@ -1,0 -1,0 +1,28 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.block;
++
++import org.seaborne.dboe.base.file.FileException ;
++
++public class BlockException extends FileException
++{
++    public BlockException()                          { super() ; }
++    public BlockException(String msg)                { super(msg) ; }
++    public BlockException(Throwable th)              { super(th) ; }
++    public BlockException(String msg, Throwable th)  { super(msg, th) ; }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgr.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgr.java
index 0000000,0000000..4fd8dc3
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgr.java
@@@ -1,0 -1,0 +1,99 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.block;
++
++import org.apache.jena.atlas.lib.Closeable ;
++import org.apache.jena.atlas.lib.Sync ;
++
++
++public interface BlockMgr extends Sync, Closeable /*UnitMgr<Block>*/
++{
++    /** Allocate an uninitialized block - writable - call only inside a update sequence. 
++     *  If blockSize is -1, means "default/fixed size" for this BlockMgr
++     */
++    public Block allocate(int blockSize) ;
++    
++    /** Answer whether there are any blocks in the collection being managed */
++    public boolean isEmpty() ; 
++    
++    /** The fixed allocated blocks are in the range [0,allocLimit).
++     * Allocation units need not be +1 increments */ 
++    public long allocLimit() ; 
++
++    /** Reset the allocation limit, should be a number previously obtained from allocLimit */
++    public void resetAlloc(long boundary) ;
++
++    /** Fetch a block, use for read only */
++    public Block getRead(long id);
++    
++    /** Fetch a block, use for write and read - only inside "update" */
++    public Block getWrite(long id);
++
++    /** Release a block, unmodified or already written. */
++    public void release(Block block) ;
++
++    /** Promote to writeable : it's OK to promote an already writeable block */ 
++    public Block promote(Block block);
++
++    // Bad name?  "endWrite", "put" -- for a mapped block, the changes are made directly, not on the write() */   
++    /** Write a block back - it still needs releasing. */ 
++    public void write(Block block) ;
++    
++    /** Replace the contents of a block slot with new contents. Block does not need releasing.
++     * The write() operation may not do real work if the block is mapped - this operation
++     * really does replace the contents with the new contents.  
++     */ 
++    public void overwrite(Block blk) ;
++    
++   /** Announce a block is no longer in use (i.e it's now freed) */ 
++    public void free(Block block);
++  
++    /** Is this a valid block id? (may be a free block)*/
++    public boolean valid(int id) ;
++    
++    /** Close the block manager */
++    @Override
++    public void close() ;
++    
++    /** Is this block manager still usable?  Closed block managers can not perform any operations except this one. */  
++    public boolean isClosed() ; 
++    
++    /** Sync the block manager */
++    @Override
++    public void sync() ;
++    
++    /** Sync the block manager : system operation to ensure sync() is passed down */
++    public void syncForce() ;
++
++    // begin/end markers.
++
++    /** Start of update */
++    public void beginUpdate() ;
++    
++    /** Completion of update */
++    public void endUpdate() ;
++
++    /** Start of read */
++    public void beginRead() ;
++
++    /** Completion of read */
++    public void endRead() ;
++
++    /* Label for helping trace which BlockMgr is which */
++    public String getLabel() ;
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrBase.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrBase.java
index 0000000,0000000..6a7a71b
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrBase.java
@@@ -1,0 -1,0 +1,51 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.block;
++
++import org.slf4j.Logger ;
++
++public abstract class BlockMgrBase implements BlockMgr
++{
++    protected final int blockSize ;
++    private String label ;
++    protected abstract Logger log() ;
++
++    // Fixed size, fixed block type.
++    protected BlockMgrBase(String label, int blockSize)
++    {
++        this.label = label ;
++        this.blockSize = blockSize ;
++    }
++
++    @Override
++    public final Block allocate(int blkSize)
++    {
++        if ( blkSize > 0 && blkSize != this.blockSize )
++            throw new BlockException("Fixed blocksize BlockMgr: request= "+blkSize+"  fixed size="+this.blockSize) ;
++        return allocate() ;
++    }
++    
++    protected abstract Block allocate() ;
++    
++    @Override final public String getLabel() { return label ; } 
++
++    @Override public void endUpdate()       {}
++    @Override public void beginUpdate()     {}
++    @Override public void beginRead()       {}
++    @Override public void endRead()         {}
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrBuilder.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrBuilder.java
index 0000000,0000000..ebf55d0
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrBuilder.java
@@@ -1,0 -1,0 +1,24 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.block;
++
++import org.seaborne.dboe.base.file.FileSet ;
++
++public interface BlockMgrBuilder {
++    BlockMgr buildBlockMgr(FileSet fileSet, String ext, BlockParams indexParams) ;
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrBuilderStd.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrBuilderStd.java
index 0000000,0000000..26ef1f5
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrBuilderStd.java
@@@ -1,0 -1,0 +1,28 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.block;
++
++import org.seaborne.dboe.base.file.FileSet ;
++
++public class BlockMgrBuilderStd implements BlockMgrBuilder {
++    @Override
++    public BlockMgr buildBlockMgr(FileSet fileSet, String ext, BlockParams indexParams) {
++        return BlockMgrFactory.create(fileSet, ext, indexParams) ;
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrCache.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrCache.java
index 0000000,0000000..753f6a1
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrCache.java
@@@ -1,0 -1,0 +1,325 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.block ;
++
++import java.util.ArrayList ;
++import java.util.Iterator ;
++import java.util.List ;
++
++import org.apache.jena.atlas.lib.Cache ;
++import org.apache.jena.atlas.lib.CacheFactory ;
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++/** Caching block manager - this is an LRU cache */
++public class BlockMgrCache extends BlockMgrSync {
++    // Actually, this is two cache one on the read blocks and one on the write
++    // blocks.
++    // The overridden public operations are sync'ed.
++    // As sync is on "this", it also covers all the other operations via
++    // BlockMgrSync
++
++    private static Logger            log            = LoggerFactory.getLogger(BlockMgrCache.class) ;
++    // Read cache : always present.
++    private final Cache<Long, Block> readCache ;
++
++    // Delayed dirty writes. May be present, may not.
++    private final Cache<Long, Block> writeCache ;
++
++    // Also enable the logging level.
++    public static boolean            globalLogging  = false ;    
++    private boolean                  logging        = false ;
++    // ---- stats
++    long                             cacheReadHits  = 0 ;
++    long                             cacheMisses    = 0 ;
++    long                             cacheWriteHits = 0 ;
++
++    static BlockMgr create(int readSlots, int writeSlots, final BlockMgr blockMgr) {
++        if ( readSlots < 0 && writeSlots < 0 )
++            return blockMgr ;
++        return new BlockMgrCache(readSlots, writeSlots, blockMgr) ;
++    }
++
++    private BlockMgrCache(int readSlots, int writeSlots, final BlockMgr blockMgr) {
++        super(blockMgr) ;
++        // Caches are related so we can't use a Getter for cache management.
++        if ( readSlots < -1 )
++            readCache = CacheFactory.createNullCache() ;
++        else
++            readCache = CacheFactory.createCache(readSlots) ;
++        if ( writeSlots <= 0 )
++            writeCache = null ;
++        else {
++            writeCache = CacheFactory.createCache(writeSlots) ;
++            writeCache.setDropHandler((id, block) -> { 
++                // We're inside a synchronized operation at this point.
++                log("Cache spill: write block: %d", id) ;
++                if ( block == null ) {
++                    log.warn("Write cache: " + id + " dropping an entry that isn't there") ;
++                    return ;
++                }
++                // Force the block to be writtern
++                // by sending it to the wrapped BlockMgr
++                BlockMgrCache.super.write(block) ;
++            }) ;
++        }
++    }
++
++    @Override
++    synchronized public void resetAlloc(long boundary) {
++        // On abort, need to clear the caches of inaccesible blocks.
++        // An abort is rare (?). We do very carefully. 
++        // Could (probably) delete in the loop or use Iteator.remove on keys().
++        // (Check the Cache contract)
++        List<Long> removals = new ArrayList<>() ;
++        readCache.keys().forEachRemaining((x)->{
++            if ( x >= boundary )
++                removals.add(x) ; 
++        }) ;
++        removals.forEach(readCache::remove) ;
++        removals.clear() ;
++        writeCache.keys().forEachRemaining((x)->{
++            if ( x >= boundary )
++                removals.add(x) ; 
++        }) ;
++        removals.forEach(writeCache::remove) ;
++        super.resetAlloc(boundary); 
++    }
++    
++    @Override
++    synchronized public Block getRead(long id) {
++        // A Block may be in the read cache or the write cache.
++        // It can be just in the write cache because the read cache is finite.
++        Block blk = readCache.getIfPresent(id) ;
++        if ( blk != null ) {
++            cacheReadHits++ ;
++            log("Hit(r->r) : %d", id) ;
++            return blk ;
++        }
++
++        // A requested block may be in the other cache.
++        // Writable blocks are readable.
++        // readable blocks are not writeable (see below).
++        if ( writeCache != null )
++            // Might still be in the dirty blocks.
++            // Leave in write cache
++            blk = writeCache.getIfPresent(id) ;
++        if ( blk != null ) {
++            cacheWriteHits++ ;
++            log("Hit(r->w) : %d", id) ;
++            return blk ;
++        }
++
++        cacheMisses++ ;
++        log("Miss/r: %d", id) ;
++        blk = super.getRead(id) ;
++        readCache.put(id, blk) ;
++        return blk ;
++    }
++
++    @Override
++    synchronized public Block getWrite(long _id) {
++        Long id = _id ;
++        Block blk = null ;
++        if ( writeCache != null )
++            blk = writeCache.getIfPresent(id) ;
++        if ( blk != null ) {
++            cacheWriteHits++ ;
++            log("Hit(w->w) : %d", id) ;
++            return blk ;
++        }
++
++        // blk is null.
++        // A requested block may be in the other cache. Promote it.
++
++        if ( readCache.containsKey(id) ) {
++            blk = readCache.getIfPresent(id) ;
++            cacheReadHits++ ;
++            log("Hit(w->r) : %d", id) ;
++            blk = promote(blk) ;
++            return blk ;
++        }
++
++        // Did not find.
++        cacheMisses++ ;
++        log("Miss/w: %d", id) ;
++        // Pass operation to wrapper.
++        blk = super.getWrite(id) ;
++        if ( writeCache != null )
++            writeCache.put(id, blk) ;
++        return blk ;
++    }
++
++    @Override
++    synchronized public Block promote(Block block) {
++        Long id = block.getId() ;
++        readCache.remove(id) ;
++        Block block2 = super.promote(block) ;
++        if ( writeCache != null )
++            writeCache.put(id, block2) ;
++        return block ;
++    }
++
++    @Override
++    synchronized public void write(Block block) {
++        writeCache(block) ;
++        super.write(block) ;
++    }
++
++    @Override
++    synchronized public void overwrite(Block block) {
++        Long id = block.getId() ;
++        // It can be a read block (by the transaction), now being written for
++        // real (enacting a transaction).
++        super.overwrite(block) ;
++        // Keep read cache up-to-date.
++        // Must at least expel the read block (which is not the overwrite
++        // block).
++        readCache.put(id, block) ;
++    }
++
++    private void writeCache(Block block) {
++        Long id = block.getId() ;
++        log("WriteCache : %d", id) ;
++        // Should not be in the read cache due to a getWrite earlier.
++        if ( readCache.containsKey(id) )
++            log.warn("write: Block in the read cache") ;
++        if ( writeCache != null ) {
++            writeCache.put(id, block) ;
++            return ;
++        }
++    }
++
++    @Override
++    synchronized public void free(Block block) {
++        Long id = block.getId() ;
++        log("Free  : %d", id) ;
++        if ( readCache.containsKey(id) ) {
++            log.warn("Freeing block from read cache") ;
++            readCache.remove(id) ;
++        }
++        if ( writeCache != null )
++            writeCache.remove(id) ;
++        super.free(block) ;
++    }
++
++    @Override
++    synchronized public void sync() {
++        _sync(false) ;
++    }
++
++    @Override
++    synchronized public void syncForce() {
++        _sync(true) ;
++    }
++
++    @Override
++    synchronized public void close() {
++        if ( writeCache != null )
++            log("close (" + writeCache.size() + " blocks)") ;
++        syncFlush() ;
++        super.close() ;
++    }
++
++    @Override
++    public String toString() {
++        return "Cache:" + super.blockMgr.toString() ;
++    }
++
++    private void log(String fmt, Object... args) {
++        if ( !logging && !globalLogging )
++            return ;
++        String msg = String.format(fmt, args) ;
++        if ( getLabel() != null )
++            msg = getLabel() + " : " + msg ;
++        log.debug(msg) ;
++    }
++
++    private void _sync(boolean force) {
++        if ( true ) {
++            String x = "" ;
++            if ( getLabel() != null )
++                x = getLabel() + " : " ;
++            log("%sH=%d, M=%d, W=%d", x, cacheReadHits, cacheMisses, cacheWriteHits) ;
++        }
++
++        if ( writeCache != null )
++            log("sync (%d blocks)", writeCache.size()) ;
++        else
++            log("sync") ;
++        boolean somethingWritten = syncFlush() ;
++
++        if ( force ) {
++            log("syncForce underlying BlockMgr") ;
++            super.syncForce() ;
++        } else if ( somethingWritten ) {
++            log("sync underlying BlockMgr") ;
++            super.sync() ;
++        } else
++            log("Empty sync") ;
++    }
++
++    private boolean syncFlush() {
++        if ( writeCache == null )
++            return false ;
++
++        boolean didSync = false ;
++
++        log("Flush (write cache)") ;
++
++        long N = writeCache.size() ;
++        Long[] ids = new Long[(int)N] ;
++
++        // Single writer (sync is a write operation MRSW)
++        // Iterating is safe.
++
++        Iterator<Long> iter = writeCache.keys() ;
++        if ( iter.hasNext() )
++            didSync = true ;
++
++        // Need to get all then delete else concurrent modification exception.
++        for ( int i = 0 ; iter.hasNext() ; i++ )
++            ids[i] = iter.next() ;
++
++        for ( int i = 0 ; i < N ; i++ ) {
++            Long id = ids[i] ;
++            expelEntry(id) ;
++        }
++        if ( didSync )
++            super.sync() ;
++        return didSync ;
++    }
++
++    // Write out when flushed.
++    // Do not call from drop handler.
++    private void expelEntry(Long id) {
++        Block block = writeCache.getIfPresent(id) ;
++        if ( block == null ) {
++            log.warn("Write cache: " + id + " expelling entry that isn't there") ;
++            return ;
++        }
++        log("Expel (write cache): %d", id) ;
++        // This pushes the block to the BlockMgr being cached.
++        super.write(block) ;
++        writeCache.remove(id) ;
++
++        // Move it into the readCache because it's often read after writing
++        // and the read cache is often larger.
++        readCache.put(id, block) ;
++    }
++}


[16/50] [abbrv] jena git commit: [maven-release-plugin] prepare for next development iteration

Posted by an...@apache.org.
[maven-release-plugin] prepare for next development iteration


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

Branch: refs/heads/jena-tdb2
Commit: dcfa94e1e3854ebfbdd1e106c110c29215ec48cb
Parents: a23065e
Author: Andy Seaborne <an...@seaborne.org>
Authored: Mon Sep 4 12:56:37 2017 +0100
Committer: Andy Seaborne <an...@seaborne.org>
Committed: Mon Sep 4 12:56:37 2017 +0100

----------------------------------------------------------------------
 dboe-base/pom.xml        |  2 +-
 dboe-index-test/pom.xml  |  4 ++--
 dboe-index/pom.xml       |  4 ++--
 dboe-trans-data/pom.xml  | 10 +++++-----
 dboe-transaction/pom.xml |  4 ++--
 fuseki-tdb2/pom.xml      |  4 ++--
 pom.xml                  |  4 ++--
 tdb2-cmds/pom.xml        |  4 ++--
 tdb2/pom.xml             |  4 ++--
 9 files changed, 20 insertions(+), 20 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/dcfa94e1/dboe-base/pom.xml
----------------------------------------------------------------------
diff --git a/dboe-base/pom.xml b/dboe-base/pom.xml
index 4179450..986c95f 100644
--- a/dboe-base/pom.xml
+++ b/dboe-base/pom.xml
@@ -26,7 +26,7 @@
   <parent>
     <groupId>org.seaborne.mantis</groupId>
     <artifactId>mantis</artifactId>
-    <version>0.3.0</version>
+    <version>0.3.1-SNAPSHOT</version>
   </parent> 
 
   <dependencies>

http://git-wip-us.apache.org/repos/asf/jena/blob/dcfa94e1/dboe-index-test/pom.xml
----------------------------------------------------------------------
diff --git a/dboe-index-test/pom.xml b/dboe-index-test/pom.xml
index 48c1b63..b06a773 100644
--- a/dboe-index-test/pom.xml
+++ b/dboe-index-test/pom.xml
@@ -26,7 +26,7 @@
   <parent>
     <groupId>org.seaborne.mantis</groupId>
     <artifactId>mantis</artifactId>
-    <version>0.3.0</version>
+    <version>0.3.1-SNAPSHOT</version>
   </parent> 
 
   <dependencies>
@@ -34,7 +34,7 @@
     <dependency>
       <groupId>org.seaborne.mantis</groupId>
       <artifactId>dboe-index</artifactId>
-      <version>0.3.0</version>
+      <version>0.3.1-SNAPSHOT</version>
     </dependency>
 
     <dependency>

http://git-wip-us.apache.org/repos/asf/jena/blob/dcfa94e1/dboe-index/pom.xml
----------------------------------------------------------------------
diff --git a/dboe-index/pom.xml b/dboe-index/pom.xml
index dcc92cb..28943de 100644
--- a/dboe-index/pom.xml
+++ b/dboe-index/pom.xml
@@ -26,7 +26,7 @@
   <parent>
     <groupId>org.seaborne.mantis</groupId>
     <artifactId>mantis</artifactId>
-    <version>0.3.0</version>
+    <version>0.3.1-SNAPSHOT</version>
   </parent> 
 
   <dependencies>
@@ -34,7 +34,7 @@
     <dependency>
       <groupId>org.seaborne.mantis</groupId>
       <artifactId>dboe-base</artifactId>
-      <version>0.3.0</version>
+      <version>0.3.1-SNAPSHOT</version>
     </dependency>
 
   </dependencies>

http://git-wip-us.apache.org/repos/asf/jena/blob/dcfa94e1/dboe-trans-data/pom.xml
----------------------------------------------------------------------
diff --git a/dboe-trans-data/pom.xml b/dboe-trans-data/pom.xml
index a579aa4..50756aa 100644
--- a/dboe-trans-data/pom.xml
+++ b/dboe-trans-data/pom.xml
@@ -25,7 +25,7 @@
   <parent>
     <groupId>org.seaborne.mantis</groupId>
     <artifactId>mantis</artifactId>
-    <version>0.3.0</version>
+    <version>0.3.1-SNAPSHOT</version>
   </parent> 
 
   <dependencies>
@@ -33,26 +33,26 @@
     <dependency>
       <groupId>org.seaborne.mantis</groupId>
       <artifactId>dboe-transaction</artifactId>
-      <version>0.3.0</version>
+      <version>0.3.1-SNAPSHOT</version>
     </dependency>
 
     <dependency>
       <groupId>org.seaborne.mantis</groupId>
       <artifactId>dboe-index</artifactId>
-      <version>0.3.0</version>
+      <version>0.3.1-SNAPSHOT</version>
     </dependency>
 
     <dependency>
       <groupId>org.seaborne.mantis</groupId>
       <artifactId>dboe-index-test</artifactId>
-      <version>0.3.0</version>
+      <version>0.3.1-SNAPSHOT</version>
       <scope>test</scope>
     </dependency>
 
     <dependency>
       <groupId>org.seaborne.mantis</groupId>
       <artifactId>dboe-base</artifactId>
-      <version>0.3.0</version>
+      <version>0.3.1-SNAPSHOT</version>
       <classifier>tests</classifier>
       <scope>test</scope>
     </dependency>

http://git-wip-us.apache.org/repos/asf/jena/blob/dcfa94e1/dboe-transaction/pom.xml
----------------------------------------------------------------------
diff --git a/dboe-transaction/pom.xml b/dboe-transaction/pom.xml
index f45beb7..8c4b21f 100644
--- a/dboe-transaction/pom.xml
+++ b/dboe-transaction/pom.xml
@@ -25,7 +25,7 @@
   <parent>
     <groupId>org.seaborne.mantis</groupId>
     <artifactId>mantis</artifactId>
-    <version>0.3.0</version>
+    <version>0.3.1-SNAPSHOT</version>
   </parent> 
 
   <dependencies>
@@ -33,7 +33,7 @@
     <dependency>
       <groupId>org.seaborne.mantis</groupId>
       <artifactId>dboe-base</artifactId>
-      <version>0.3.0</version>
+      <version>0.3.1-SNAPSHOT</version>
     </dependency>
 
   </dependencies>

http://git-wip-us.apache.org/repos/asf/jena/blob/dcfa94e1/fuseki-tdb2/pom.xml
----------------------------------------------------------------------
diff --git a/fuseki-tdb2/pom.xml b/fuseki-tdb2/pom.xml
index 6c3383c..8205005 100644
--- a/fuseki-tdb2/pom.xml
+++ b/fuseki-tdb2/pom.xml
@@ -25,7 +25,7 @@
   <parent>
     <groupId>org.seaborne.mantis</groupId>
     <artifactId>mantis</artifactId>
-    <version>0.3.0</version>
+    <version>0.3.1-SNAPSHOT</version>
   </parent> 
 
   <packaging>jar</packaging>
@@ -36,7 +36,7 @@
     <dependency>
       <groupId>org.seaborne.mantis</groupId>
       <artifactId>tdb2</artifactId>
-      <version>0.3.0</version>
+      <version>0.3.1-SNAPSHOT</version>
     </dependency>
 
     <dependency>

http://git-wip-us.apache.org/repos/asf/jena/blob/dcfa94e1/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 72b8aba..9be78e4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -23,7 +23,7 @@
   <artifactId>mantis</artifactId>
   <packaging>pom</packaging>
   <url>https://github.com/afs/mantis</url>
-  <version>0.3.0</version>
+  <version>0.3.1-SNAPSHOT</version>
   <name>Mantis :: Database Operation Environment</name>
   <description>Mantis :: Database Operation Environment</description>
 
@@ -56,7 +56,7 @@
     <url>https://github.com/afs/mantis</url>
     <connection>scm:git:https://github.com/afs/mantis</connection>
     <developerConnection>scm:git:git@github.com:afs/mantis.git</developerConnection>
-    <tag>mantis-0.3.0</tag>
+    <tag>HEAD</tag>
   </scm>
   
   <properties>

http://git-wip-us.apache.org/repos/asf/jena/blob/dcfa94e1/tdb2-cmds/pom.xml
----------------------------------------------------------------------
diff --git a/tdb2-cmds/pom.xml b/tdb2-cmds/pom.xml
index 9885c77..d55b4c8 100644
--- a/tdb2-cmds/pom.xml
+++ b/tdb2-cmds/pom.xml
@@ -27,7 +27,7 @@
   <parent>
     <groupId>org.seaborne.mantis</groupId>
     <artifactId>mantis</artifactId>
-    <version>0.3.0</version>
+    <version>0.3.1-SNAPSHOT</version>
   </parent> 
 
   <dependencies>
@@ -35,7 +35,7 @@
     <dependency>
       <groupId>org.seaborne.mantis</groupId>
       <artifactId>tdb2</artifactId>
-      <version>0.3.0</version>
+      <version>0.3.1-SNAPSHOT</version>
     </dependency>
 
     <dependency>

http://git-wip-us.apache.org/repos/asf/jena/blob/dcfa94e1/tdb2/pom.xml
----------------------------------------------------------------------
diff --git a/tdb2/pom.xml b/tdb2/pom.xml
index 886e2da..80cfc5a 100644
--- a/tdb2/pom.xml
+++ b/tdb2/pom.xml
@@ -27,7 +27,7 @@
   <parent>
     <groupId>org.seaborne.mantis</groupId>
     <artifactId>mantis</artifactId>
-    <version>0.3.0</version>
+    <version>0.3.1-SNAPSHOT</version>
   </parent> 
 
   <dependencies>
@@ -43,7 +43,7 @@
     <dependency>
       <groupId>org.seaborne.mantis</groupId>
       <artifactId>dboe-trans-data</artifactId>
-      <version>0.3.0</version>
+      <version>0.3.1-SNAPSHOT</version>
     </dependency>
 
     <dependency>


[03/50] [abbrv] jena git commit: Switchable conatiner datasets

Posted by an...@apache.org.
Switchable conatiner datasets


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

Branch: refs/heads/jena-tdb2
Commit: d72f69611db396c556281da83de90db57c0c5387
Parents: 46862c3
Author: Andy Seaborne <an...@seaborne.org>
Authored: Wed Aug 30 18:43:32 2017 +0100
Committer: Andy Seaborne <an...@seaborne.org>
Committed: Wed Aug 30 18:43:32 2017 +0100

----------------------------------------------------------------------
 .../transaction/txn/TransactionCoordinator.java |   8 +
 .../java/org/seaborne/tdb2/DatabaseMgr.java     | 119 ++++++++
 .../java/org/seaborne/tdb2/TDB2Factory.java     |  70 ++---
 .../tdb2/assembler/DatasetAssemblerTDB.java     |   4 +-
 .../tdb2/graph/TransactionHandlerTDB.java       |  47 ++-
 .../org/seaborne/tdb2/repack/DatabaseMgr.java   | 171 -----------
 .../tdb2/repack/DatasetGraphSwitchable.java     | 165 ++++++++++-
 .../java/org/seaborne/tdb2/repack/sys/IOX.java  | 278 ------------------
 .../org/seaborne/tdb2/setup/TDBBuilder.java     |   2 -
 .../org/seaborne/tdb2/store/DatasetControl.java |  44 +++
 .../seaborne/tdb2/store/DatasetControlMRSW.java | 144 +++++++++
 .../seaborne/tdb2/store/DatasetControlNone.java |  48 +++
 .../tdb2/store/DatasetControlReadOnly.java      |  59 ++++
 .../seaborne/tdb2/store/DatasetPrefixesTDB.java |   8 +-
 .../tdb2/store/GraphPrefixesProjectionTDB.java  |   9 +-
 .../java/org/seaborne/tdb2/store/GraphTDB.java  | 104 +------
 .../seaborne/tdb2/store/PrefixMappingCache.java |  33 ++-
 .../java/org/seaborne/tdb2/store/QuadTable.java |   1 -
 .../java/org/seaborne/tdb2/store/TableBase.java |   1 -
 .../org/seaborne/tdb2/store/TripleTable.java    |   1 -
 .../nodetable/TReadAppendFileTransport.java     |   2 +-
 .../store/nodetupletable/NodeTupleTable.java    |   2 +-
 .../nodetupletable/NodeTupleTableConcrete.java  |   2 +-
 .../nodetupletable/NodeTupleTableWrapper.java   |   2 +-
 .../seaborne/tdb2/sys/ConnectionTracker.java    |  23 ++
 .../seaborne/tdb2/sys/DatabaseConnection.java   | 164 +++++++++++
 .../java/org/seaborne/tdb2/sys/DatabaseOps.java | 261 +++++++++++++++++
 .../org/seaborne/tdb2/sys/DatasetControl.java   |  44 ---
 .../seaborne/tdb2/sys/DatasetControlMRSW.java   | 146 ---------
 .../tdb2/sys/DatasetControlMutable.java         |  54 ----
 .../seaborne/tdb2/sys/DatasetControlNone.java   |  48 ---
 .../tdb2/sys/DatasetControlReadOnly.java        |  59 ----
 .../main/java/org/seaborne/tdb2/sys/IOX.java    | 293 +++++++++++++++++++
 .../java/org/seaborne/tdb2/TestTDBFactory.java  |  62 ++--
 .../test/java/org/seaborne/tdb2/junit/TL.java   |   3 +-
 .../tdb2/store/TestTransPromoteTDB.java         |   4 +-
 36 files changed, 1462 insertions(+), 1023 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionCoordinator.java
----------------------------------------------------------------------
diff --git a/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionCoordinator.java b/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionCoordinator.java
index 56cfbac..56104bf 100644
--- a/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionCoordinator.java
+++ b/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionCoordinator.java
@@ -459,6 +459,14 @@ public class TransactionCoordinator {
         Objects.nonNull(readWrite) ;
         checkActive() ;
         
+        // XXX Flag to bounce writers fpor long term "block writers"
+        if ( false /* bounceWritersAtTheMoment */) {
+            if ( readWrite == WRITE ) {
+                throw new TransactionException("Writers currently being rejected");
+            }
+        }
+            
+        
         if ( canBlock )
             exclusivitylock.readLock().lock() ;
         else {

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/main/java/org/seaborne/tdb2/DatabaseMgr.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/DatabaseMgr.java b/tdb2/src/main/java/org/seaborne/tdb2/DatabaseMgr.java
new file mode 100644
index 0000000..33a4a6b
--- /dev/null
+++ b/tdb2/src/main/java/org/seaborne/tdb2/DatabaseMgr.java
@@ -0,0 +1,119 @@
+/*
+ * 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.seaborne.tdb2;
+
+import org.apache.commons.lang3.NotImplementedException ;
+import org.apache.jena.query.Dataset ;
+import org.apache.jena.sparql.core.DatasetGraph ;
+import org.seaborne.dboe.base.file.Location ;
+import org.seaborne.tdb2.repack.DatasetGraphSwitchable ;
+import org.seaborne.tdb2.store.DatasetGraphTDB ;
+import org.seaborne.tdb2.sys.DatabaseOps ;
+import org.seaborne.tdb2.sys.StoreConnection ;
+
+/** Operations for TDBS DatasetGraph, including admin operations 
+ * See {@link TDB2Factory} for creating API-level {@link Dataset Datasets}.
+ * 
+ * @see TDB2Factory
+ */
+public class DatabaseMgr {
+
+    // All creation of DatasetGraph for TDB2 goes through this method.
+    private static DatasetGraph DB_ConnectCreate(Location location) {
+        //return DatabaseOps.connectOrCreate(location);
+        // One level.
+        StoreConnection sConn = StoreConnection.connectCreate(location) ;
+        return sConn.getDatasetGraph() ; 
+    }
+
+    /** Create or connect to a TDB2-backed dataset */
+    public static DatasetGraph connectDatasetGraph(Location location) {
+        return DB_ConnectCreate(location); 
+    }
+
+    /** Create or connect to a TDB2-backed dataset */
+    public static DatasetGraph connectDatasetGraph(String location) {
+        return connectDatasetGraph(Location.create(location)) ;
+    }
+    
+    /**
+     * Compact a datasets which must be a switchable TDB database.
+     * This is the normal dataset type for on-disk TDB2 databases.
+     *  
+     * @param container
+     */
+    public static void compact(DatasetGraph container) {
+        DatasetGraphSwitchable dsg = requireSwitchable(container);
+        DatabaseOps.compact(dsg);
+    }
+
+    /**
+     * Create a backup for a switchable TDB database. This is the normal dataset type for
+     * on-disk TDB2 databases.
+     * <p>
+     * The backup is created in the databases folder, under "Backups".
+     * <p>
+     * Backup creates a consistent copy og the database. It is performed as a read-transaction
+     * and does not lock out other use of the dataset.
+     * 
+     * @param container
+     */
+    public static void backup(DatasetGraph container) {
+        DatasetGraphSwitchable dsg = requireSwitchable(container);
+        throw new NotImplementedException("DatasetGraph backup not implemented yet");
+    }
+
+
+    /** Create an in-memory TDB2-backed dataset (for testing) */
+    public static DatasetGraph createDatasetGraph() {
+        return connectDatasetGraph(Location.mem()) ;
+    }
+
+    /** Return the location of a dataset if it is backed by TDB, else null */ 
+    public static boolean isBackedByTDB(DatasetGraph datasetGraph) {
+        DatasetGraphTDB dsg = requireDirect(datasetGraph);
+        if ( dsg == null )
+            return false ;
+        return true;
+    }
+
+    /** Return the location of a DatasetGraph if it is backed by TDB, else null */
+    public static Location location(DatasetGraph datasetGraph) {
+        DatasetGraphTDB dsg = requireDirect(datasetGraph);
+        if ( dsg == null )
+            return null ;
+        return dsg.getLocation();
+    }
+
+    private static DatasetGraphSwitchable requireSwitchable(DatasetGraph datasetGraph) {
+        if ( datasetGraph instanceof DatasetGraphSwitchable )
+            return (DatasetGraphSwitchable)datasetGraph;
+        throw new TDBException("Not a switchable TDB database");
+    }
+
+    static DatasetGraphTDB requireDirect(DatasetGraph datasetGraph) {
+        DatasetGraph dsg = datasetGraph;
+        if ( datasetGraph instanceof DatasetGraphSwitchable )
+            dsg = ((DatasetGraphSwitchable)datasetGraph).get();
+        if ( datasetGraph instanceof DatasetGraphTDB )
+            return ((DatasetGraphTDB)datasetGraph);
+        throw new TDBException("Not a TDB database (argument is neither a switchable nor direct TDB DatasetGraph)");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/main/java/org/seaborne/tdb2/TDB2Factory.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/TDB2Factory.java b/tdb2/src/main/java/org/seaborne/tdb2/TDB2Factory.java
index d784fa3..93cc3d2 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/TDB2Factory.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/TDB2Factory.java
@@ -21,26 +21,20 @@ import org.apache.jena.query.Dataset ;
 import org.apache.jena.query.DatasetFactory ;
 import org.apache.jena.sparql.core.DatasetGraph ;
 import org.seaborne.dboe.base.file.Location ;
-import org.seaborne.tdb2.store.DatasetGraphTDB;
-import org.seaborne.tdb2.sys.StoreConnection ;
 
-/** Public factory for connecting to and creating datasets backed by TDB2 storage */
+/**
+ *  Public factory for connecting to and creating datasets backed by TDB2 storage.
+ */
 public class TDB2Factory
 {
     private TDB2Factory() {} 
 
-    /** @deprecated Use {@link #connectDatasetGraph(Location)} */
+    /** @deprecated Use {@link DatabaseMgr#connectDatasetGraph(Location)} */
     @Deprecated
     public static DatasetGraph createDatasetGraph(Location location) {
-        return connectDatasetGraph(location);
+        return DatabaseMgr.connectDatasetGraph(location);
     }
     
-    /** Create or connect to a TDB2-backed dataset */
-    public static DatasetGraph connectDatasetGraph(Location location) {
-        StoreConnection sConn = StoreConnection.connectCreate(location) ;
-        return sConn.getDatasetGraph() ; 
-    }
-
     /** @deprecated Use {@link #connectDataset(Location)} */
     @Deprecated
     public static Dataset createDataset(Location location) {
@@ -49,21 +43,16 @@ public class TDB2Factory
     
     /** Create or connect to a TDB2-backed dataset */
     public static Dataset connectDataset(Location location) {
-        DatasetGraph dsg = connectDatasetGraph(location) ;
+        DatasetGraph dsg = DatabaseMgr.connectDatasetGraph(location) ;
         return DatasetFactory.wrap(dsg) ;
     }
 
-    /** @deprecated Use {@link #connectDatasetGraph(String)} */
+    /** @deprecated Use {@link DatabaseMgr#connectDatasetGraph(String)} */
     @Deprecated
     public static DatasetGraph createDatasetGraph(String location) {
-        return connectDatasetGraph(location);
+        return DatabaseMgr.connectDatasetGraph(location);
     }
     
-    /** Create or connect to a TDB2-backed dataset */
-    public static DatasetGraph connectDatasetGraph(String location) {
-        return connectDatasetGraph(Location.create(location)) ;
-    }
-
     /** @deprecated Use {@link #connectDataset(String)} */
     @Deprecated
     public static Dataset createDataset(String location) {
@@ -74,17 +63,19 @@ public class TDB2Factory
     public static Dataset connectDataset(String location) {
         return connectDataset(Location.create(location)) ;    }
 
-    /** Create an in-memory TDB2-backed dataset (for testing) */
-    public static DatasetGraph createDatasetGraph() {
-        return connectDatasetGraph(Location.mem()) ;
-    }
+    /**
+     * Create an in-memory TDB2-backed dataset (for testing). In-memory TDB2 datasets are use
+     * a simple simulation of disk I/O to give exact semantics, which is useful to create
+     * tests that run fast where setup and teardown of datasets can be the major cost.
+     * <p> 
+     * In-memory TDB2 datasets are not designed to scale, nor provide efficient execution for
+     * applications for long-term use. 
+     */ 
+    public static Dataset createDataset() { return connectDataset(Location.mem()) ; }
 
-    /** Create an in-memory TDB2-backed dataset (for testing) */
-    public static Dataset createDataset() {
-        return connectDataset(Location.mem()) ;
-    }
-
-//    /** Read the file and assembler a dataset */
+//    /**
+//     *  Read the file and assembler a dataset
+//     */
 //    public static Dataset assembleDataset(String assemblerFile) {
 //        return (Dataset)AssemblerUtils.build(assemblerFile, VocabTDB.tDatasetTDB) ;
 //    }
@@ -99,31 +90,16 @@ public class TDB2Factory
 //        _release(location(dataset)) ;
 //    }
 
-    /** Return the location of a dataset if it is backed by TDB, else null */ 
+    /** Tes whether a dataset is backed by TDB or not. */ 
     public static boolean isBackedByTDB(Dataset dataset) {
         DatasetGraph dsg = dataset.asDatasetGraph() ;
-        return isBackedByTDB(dsg) ;
+        return DatabaseMgr.isBackedByTDB(dsg) ;
     }
     
-    /** Return the location of a dataset if it is backed by TDB, else null */ 
-    public static boolean isBackedByTDB(DatasetGraph datasetGraph) {
-        if ( datasetGraph instanceof DatasetGraphTDB )
-            // A transaction or the base storage.
-            return true ;
-        return false ;
-    }
-
     /** Return the location of a dataset if it is backed by TDB, else null */
     public static Location location(Dataset dataset) {
         DatasetGraph dsg = dataset.asDatasetGraph() ;
-        return location(dsg) ;
-    }
-
-    /** Return the location of a DatasetGraph if it is backed by TDB, else null */
-    public static Location location(DatasetGraph datasetGraph) {
-        if ( datasetGraph instanceof DatasetGraphTDB )
-            return ((DatasetGraphTDB)datasetGraph).getLocation() ;
-        return null ;
+        return DatabaseMgr.location(dsg) ;
     }
 
 //    /** Set the {@link StoreParams} for specific Location.

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/main/java/org/seaborne/tdb2/assembler/DatasetAssemblerTDB.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/assembler/DatasetAssemblerTDB.java b/tdb2/src/main/java/org/seaborne/tdb2/assembler/DatasetAssemblerTDB.java
index ab2ba9c..dc2d67a 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/assembler/DatasetAssemblerTDB.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/assembler/DatasetAssemblerTDB.java
@@ -36,8 +36,8 @@ import org.apache.jena.sparql.core.assembler.DatasetAssembler ;
 import org.apache.jena.sparql.expr.NodeValue ;
 import org.apache.jena.system.JenaSystem ;
 import org.seaborne.dboe.base.file.Location ;
+import org.seaborne.tdb2.DatabaseMgr ;
 import org.seaborne.tdb2.TDB2 ;
-import org.seaborne.tdb2.TDB2Factory ;
 
 public class DatasetAssemblerTDB extends DatasetAssembler
 {
@@ -55,7 +55,7 @@ public class DatasetAssemblerTDB extends DatasetAssembler
 
         String dir = getStringValue(root, pLocation) ;
         Location loc = Location.create(dir) ;
-        DatasetGraph dsg = TDB2Factory.connectDatasetGraph(loc) ;
+        DatasetGraph dsg = DatabaseMgr.connectDatasetGraph(loc) ;
 
         if ( root.hasProperty(pUnionDefaultGraph) ) {
             Node b = root.getProperty(pUnionDefaultGraph).getObject().asNode() ;

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/main/java/org/seaborne/tdb2/graph/TransactionHandlerTDB.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/graph/TransactionHandlerTDB.java b/tdb2/src/main/java/org/seaborne/tdb2/graph/TransactionHandlerTDB.java
index bcd5b3d..664b005 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/graph/TransactionHandlerTDB.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/graph/TransactionHandlerTDB.java
@@ -17,45 +17,44 @@
 
 package org.seaborne.tdb2.graph;
 
-import org.apache.jena.graph.Graph ;
 import org.apache.jena.graph.impl.TransactionHandlerBase ;
-import org.seaborne.tdb2.TDB2 ;
+import org.apache.jena.query.ReadWrite;
+import org.seaborne.dboe.transaction.txn.TransactionCoordinator;
+import org.seaborne.tdb2.store.DatasetGraphTDB;
 import org.seaborne.tdb2.store.GraphTDB ;
 
-/** Support for when TDB is used non-transactionally. Does not support ACID transactions.  
- *  Flushes if commit is called although it denies supporting transactions
- */
-
 public class TransactionHandlerTDB extends TransactionHandlerBase //implements TransactionHandler 
 {
-    private final Graph graph ;
+    private final GraphTDB graph ;
+    private final DatasetGraphTDB dsg;
 
-    public TransactionHandlerTDB(GraphTDB graph)
-    {
-        this.graph = graph ;
+    public TransactionHandlerTDB(GraphTDB graph) {
+        this.graph = graph;
+        this.dsg = graph.getDSG();
     }
-    
+
     @Override
-    public void abort()
-    {
-        // Not the Jena old-style transaction interface
-        throw new UnsupportedOperationException("TDB: 'abort' of a transaction not supported") ;
-        //log.warn("'Abort' of a transaction not supported - ignored") ;
+    public void abort() {
+        graph.getDSG().abort();
+        graph.getDSG().end();
     }
 
     @Override
-    public void begin()
-    {}
+    public void begin() {
+        if ( TransactionCoordinator.promotion )
+            dsg.begin(ReadWrite.READ);
+        else
+            dsg.begin(ReadWrite.WRITE);
+    }
 
     @Override
-    public void commit()
-    {
-        TDB2.sync(graph) ;
+    public void commit() {
+        dsg.commit();
+        dsg.end();
     }
 
     @Override
-    public boolean transactionsSupported()
-    {
-        return false ;
+    public boolean transactionsSupported() {
+        return true;
     }
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/main/java/org/seaborne/tdb2/repack/DatabaseMgr.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/repack/DatabaseMgr.java b/tdb2/src/main/java/org/seaborne/tdb2/repack/DatabaseMgr.java
deleted file mode 100644
index 30b01d6..0000000
--- a/tdb2/src/main/java/org/seaborne/tdb2/repack/DatabaseMgr.java
+++ /dev/null
@@ -1,171 +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.seaborne.tdb2.repack;
-
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.List;
-
-import org.apache.jena.sparql.core.DatasetGraph;
-import org.seaborne.dboe.base.file.Location;
-import org.seaborne.dboe.transaction.txn.TransactionCoordinator;
-import org.seaborne.dboe.transaction.txn.TransactionalSystem;
-import org.seaborne.tdb2.TDBException;
-import org.seaborne.tdb2.repack.sys.CopyDSG;
-import org.seaborne.tdb2.repack.sys.FilenameUtils;
-import org.seaborne.tdb2.repack.sys.IOX;
-import org.seaborne.tdb2.repack.sys.Util;
-import org.seaborne.tdb2.setup.StoreParams;
-import org.seaborne.tdb2.store.DatasetGraphTDB;
-import org.seaborne.tdb2.sys.StoreConnection;
-
-/** Create TDB2 databases. 
- * 
- *  TDB2 uses a hierarchical structure to manage on disk.
- * 
- *  If in-memory (non-scalable, not-performant, perfect simulation for functionality using a RAM disk, for testing mainly),
- *  create a 
- *    
- * 
- * Directory and files:
- * <ul>
- * <li> {@code Data-NNNN/} -- databases by version. Compacting creates a new directory, leaving 
- * <li> store params, static (disk layout related - can not chnage once created) and dynamic (settings related to in-memory).
- * <li> {@code Backup-DATE/} -- backups.
- * <li> External indexes like {@code TextIndex/} -- the text index only applies to the current, latest database.
- * </ul>
- */
-public class DatabaseMgr {
-    private static final String dbPrefix   = "Data";
-    private static final String SEP        = "-";
-    private static final String startCount = "0001";
-    
-    // ***** Need to share DatasetGraphSwitchable -> push down into StoreConnection.
-    
-    public static DatasetGraph connectOrCreate(Location location) {
-        if ( location.isMem() )
-            return StoreConnection.connectCreate(location).getDatasetGraph();
-        // Exists?
-       if ( ! location.exists() )
-           throw new TDBException("No such location: "+location);
-       Path path = IOX.asPath(location);
-       // Scan for DBs
-       Path db = findLocation(path, dbPrefix);
-       // XXX [StoreParams]
-       StoreParams params;
-       if ( db == null ) {
-           db = path.resolve(dbPrefix+SEP+startCount);
-           IOX.createDirectory(db);
-       }
-       Location loc2 = IOX.asLocation(db);
-       DatasetGraphTDB dsg = StoreConnection.connectCreate(loc2).getDatasetGraphTDB();
-       DatasetGraph appDSG = new DatasetGraphSwitchable(path, dsg);
-       return appDSG;
-    }
-    
-    // JVM-wide :-(
-    private static Object compactionLock = new Object();
-    
-    public static void compact(DatasetGraphSwitchable container) {
-        synchronized(compactionLock) {
-            
-            Path base = container.getContainerPath();
-            
-            Location loc1a = ((DatasetGraphTDB)container.get()).getLocation();
-            
-            if ( loc1a.isMem() ) {}
-            if ( ! loc1a.exists() )
-                throw new TDBException("No such location: "+loc1a);
-            
-            // Checks
-            Path db1 = findLocation(base, dbPrefix);
-            Location loc1 = IOX.asLocation(db1);
-            // Is this the same database location?
-            if ( ! loc1.equals(loc1a) )
-                throw new TDBException("Inconsistent (not latested?) : "+loc1a+" : "+loc1);
-            
-            // Version
-            int v = IOX.extractIndex(db1.getFileName().toString(), dbPrefix, SEP);
-            String next = FilenameUtils.filename(dbPrefix, SEP, v+1);
-            
-            Path db2 = db1.getParent().resolve(next);
-            IOX.createDirectory(db2);
-            Location loc2 = IOX.asLocation(db2);
-            System.out.printf("Compact %s -> %s\n", db1.getFileName(), db2.getFileName());
-            
-            compact(container, loc1, loc2);
-        }
-    }
-    
-    // XXX Later - switch in a recording dataset, not block writers, and reply after
-    // switch over before releasing the new dataset to the container.
-    // Maybe copy indexes and switch the DSG over (drop switchable).
-    
-    /** Copy the latest version from one location to another. */
-    private static void compact(DatasetGraphSwitchable container, Location loc1, Location loc2) {
-        if ( loc1.isMem() || loc2.isMem() )
-            throw new TDBException("Compact involves a memory location: "+loc1+" : "+loc2);
-        StoreConnection srcConn = StoreConnection.connectExisting(loc1);
-        if ( srcConn == null ) {
-            throw new TDBException("No database at location : "+loc1); 
-        }
-        
-        DatasetGraphTDB dsgBase0 = (DatasetGraphTDB)container.get();
-        if ( ! dsgBase0.getLocation().equals(loc1) )
-            throw new TDBException("Inconsistent locations for base : "+dsgBase0.getLocation()+" , "+dsgBase0.getLocation());
-        
-        DatasetGraphTDB dsgBase = srcConn.getDatasetGraphTDB();
-        if ( dsgBase != dsgBase0 )
-            throw new TDBException("Inconsistent datasets : "+dsgBase0.getLocation()+" , "+dsgBase.getLocation());
-        
-        TransactionalSystem txnSystem = dsgBase.getTxnSystem();
-        TransactionCoordinator txnMgr = dsgBase.getTxnSystem().getTxnMgr();
-        
-        // Stop update.  On exit there are no writers and none will start until switched over.
-        txnMgr.blockWriters();
-        
-        // Copy the latest generation.
-        DatasetGraphTDB dsgCompact = StoreConnection.connectCreate(loc2).getDatasetGraphTDB();
-        CopyDSG.copy(dsgBase, dsgCompact);   
-
-        TransactionCoordinator txnMgr2 = dsgCompact.getTxnSystem().getTxnMgr();
-        txnMgr2.startExclusiveMode();
-
-        txnMgr.startExclusiveMode();
-
-        // No transactions on either database.
-        // Switch.
-        container.set(dsgCompact);
-        txnMgr2.finishExclusiveMode();
-        // New database running.
-
-        // Clean-up.
-        // txnMgr.finishExclusiveMode();
-        // Don't call : txnMgr.startWriters();
-        StoreConnection.release(dsgBase.getLocation());
-    }
-    
-    private static Path findLocation(Path directory, String namebase) {
-        if ( ! Files.exists(directory) )
-            return null;
-        // In-order, low to high.
-        List<Path> maybe = IOX.scanForDirByPattern(directory, namebase, SEP);
-        return Util.getLastOrNull(maybe);
-    }
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/main/java/org/seaborne/tdb2/repack/DatasetGraphSwitchable.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/repack/DatasetGraphSwitchable.java b/tdb2/src/main/java/org/seaborne/tdb2/repack/DatasetGraphSwitchable.java
index 870f8af..642fb61 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/repack/DatasetGraphSwitchable.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/repack/DatasetGraphSwitchable.java
@@ -18,17 +18,30 @@
 package org.seaborne.tdb2.repack;
 
 import java.nio.file.Path;
+import java.util.Map;
 import java.util.concurrent.atomic.AtomicReference;
 
-import org.apache.jena.sparql.core.DatasetGraph ;
-import org.apache.jena.sparql.core.DatasetGraphWrapper ;
+import org.apache.jena.graph.Graph;
+import org.apache.jena.graph.Node;
+import org.apache.jena.shared.PrefixMapping;
+import org.apache.jena.shared.impl.PrefixMappingImpl;
+import org.apache.jena.sparql.core.*;
+import org.seaborne.tdb2.store.DatasetGraphTDB;
 
-final // Encourage the JIT!
+final
 public class DatasetGraphSwitchable extends DatasetGraphWrapper
 {
+    // QueryEngineFactoryWrapper has a QueryEngineFactory that is always loaded that
+    // executes on the unwrapped DSG (recursively). Unwrapping is via getBase, calling
+    // getWrapped() which is implemented with get().
+    
+//    static { 
+//        // QueryEngineRegistry.addFactory(factory());
+//    }
+    
     private final AtomicReference<DatasetGraph> dsgx = new AtomicReference<>();
     // Null for in-memory datasets.
-    private final Path basePath; 
+    private final Path basePath;
     
     public DatasetGraphSwitchable(Path base, DatasetGraph dsg) {
         // Don't use the slot in datasetGraphWrapper - use the AtomicReference
@@ -37,6 +50,11 @@ public class DatasetGraphSwitchable extends DatasetGraphWrapper
         this.basePath = base;  
     }
 
+    /** Is this {@code DatasetGraphSwitchable} just a holder for a {@code DatasetGraph}?
+     *  If so, it does not have a location on disk.
+     */
+    public boolean hasContainerPath() { return basePath != null; } 
+    
     public Path getContainerPath() { return basePath; }
     
     /** The dataset to use for redirection - can be overridden.
@@ -53,5 +71,144 @@ public class DatasetGraphSwitchable extends DatasetGraphWrapper
     public DatasetGraph set(DatasetGraph dsg) { 
         return dsgx.getAndSet(dsg);
     }
+    
+    /** If and only if the current value is the given old value, set the base {@link DatasetGraph}  
+     * Returns true if a swap happened.
+     */ 
+    public boolean change(DatasetGraph oldDSG, DatasetGraph newDSG) { 
+        return dsgx.compareAndSet(oldDSG, newDSG);
+    }
+
+    // Used directly, Does not get prefix mapping right.
+    // GraphView has a private, empty prefix mapping
+
+    Graph dft = new GraphView(this, Quad.defaultGraphNodeGenerated) {
+        @Override
+        protected PrefixMapping createPrefixMapping() {
+            return prefixMapping(null);
+        } 
+    };
+    
+    @Override
+    public Graph getDefaultGraph() {
+        // Via the switchable.
+        return new GraphView(this, Quad.defaultGraphNodeGenerated) {
+            @Override
+            protected PrefixMapping createPrefixMapping() {
+                return prefixMapping(null);
+            }
+        };
+    }
+    
+    @Override
+    public Graph getGraph(Node gn) {
+        if ( Quad.isUnionGraph(gn) )
+            return GraphView.createUnionGraph(get());
+        
+        return new GraphView(this, gn) {
+            @Override
+            protected PrefixMapping createPrefixMapping() {
+                return prefixMapping(gn);
+            }
+        };
+    }
+
+    // TDB2 specific.
+    // Does not cope with blank nodes.
+    // A PrefixMapping sending operations via the switchable.
+    private PrefixMapping prefixMapping(Node graphName) {
+        
+        String gn = (graphName == null) ? "" : graphName.getURI(); 
+        
+        return new PrefixMappingImpl() {
+            
+            DatasetPrefixStorage dps() {
+                return ((DatasetGraphTDB)dsgx.get()).getPrefixes();
+            }
+            
+            Graph graph() {
+                DatasetGraphTDB dsg = (DatasetGraphTDB)dsgx.get();
+                if ( gn == null )
+                    return dsg.getDefaultGraph();
+                else
+                    return dsg.getGraph(graphName);
+            }
+            
+            PrefixMapping prefixMapping() {
+                if ( gn == null )
+                    return dps().getPrefixMapping();
+                else
+                    return dps().getPrefixMapping(gn); 
+            }
+
+            @Override
+            protected void set(String prefix, String uri) {
+                dps().insertPrefix(gn, prefix, uri);
+                super.set(prefix, uri);
+            }
+
+            @Override
+            protected String get(String prefix) {
+                return dps().readPrefix(gn, prefix);
+            }
+
+            @Override
+            protected void remove(String prefix) {
+                dps().getPrefixMapping().removeNsPrefix(prefix);
+                super.remove(prefix);
+            }
+            
+            @Override
+            public Map<String, String> getNsPrefixMap() {
+                return prefixMapping().getNsPrefixMap();
+                //return graph().getPrefixMapping().getNsPrefixMap();
+            }
+        };
+    }
+    
+    //static { register() ; }
+    
+    
+//    static QueryEngineFactory factory() {
+//        return new QueryEngineFactory() {
+//            @Override
+//            public boolean accept(Op op, DatasetGraph dataset, Context context) {
+//                DatasetGraphSwitchable dsg = extract(dataset) ;
+//                if ( dsg == null ) return false;
+//                QueryEngineFactory f = QueryEngineRegistry.findFactory(op, dsg.get(), context);
+//                return f.accept(op, dataset, context);
+//            }
+//
+//            @Override
+//            public Plan create(Op op, DatasetGraph dataset, Binding inputBinding, Context context) {
+//                DatasetGraphSwitchable dsg = extract(dataset) ;
+//                if ( dsg == null ) return null;
+//                QueryEngineFactory f = QueryEngineRegistry.findFactory(op, dsg.get(), context);
+//                return f.create(op, dataset, inputBinding, context);
+//            }
+//
+//            private DatasetGraphSwitchable extract(DatasetGraph dataset) {
+//                if ( dataset instanceof DatasetGraphSwitchable )
+//                    return (DatasetGraphSwitchable)dataset;
+//                return null;
+//            }
+//
+//            @Override
+//            public boolean accept(Query query, DatasetGraph dataset, Context context) {
+//                DatasetGraphSwitchable dsg = extract(dataset) ;
+//                if ( dsg == null ) return false;
+//                QueryEngineFactory f = QueryEngineRegistry.findFactory(query, dsg.get(), context);
+//                return f.accept(query, dataset, context);
+//            }
+//
+//            @Override
+//            public Plan create(Query query, DatasetGraph dataset, Binding inputBinding, Context context) {
+//                DatasetGraphSwitchable dsg = extract(dataset) ;
+//                if ( dsg == null ) return null;
+//                QueryEngineFactory f = QueryEngineRegistry.findFactory(query, dsg.get(), context);
+//                return f.create(query, dataset, inputBinding, context);
+//            }
+//        };
+//    }
 }
 

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/main/java/org/seaborne/tdb2/repack/sys/IOX.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/repack/sys/IOX.java b/tdb2/src/main/java/org/seaborne/tdb2/repack/sys/IOX.java
deleted file mode 100644
index 183ef30..0000000
--- a/tdb2/src/main/java/org/seaborne/tdb2/repack/sys/IOX.java
+++ /dev/null
@@ -1,278 +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.seaborne.tdb2.repack.sys;
-
-import java.io.BufferedOutputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.file.*;
-import java.nio.file.attribute.FileAttribute;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
-import java.util.function.Function ;
-import java.util.regex.Pattern;
-
-import org.apache.jena.atlas.RuntimeIOException;
-import org.apache.jena.atlas.logging.FmtLog;
-import org.seaborne.dboe.DBOpEnvException;
-import org.seaborne.dboe.base.file.Location;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- *  PathX
- */
-public class IOX {
-    
-    public static Path currentDirectory = Paths.get(".");
-    
-    /** A Consumer that can throw {@link IOException}. */
-    public interface IOConsumer<X> {
-        void actionEx(X arg) throws IOException;
-    }
-
-    /** Convert an {@link IOException} into a {@link RuntimeIOException}.
-     * <p>
-     * Idiom:
-     * <pre>
-     *     catch(IOException ex) { throw new exception(ex); } 
-     * </pre>
-     * @param ioException
-     * @return RuntimeIOException
-     */
-    public static RuntimeIOException exception(IOException ioException) {
-        return new RuntimeIOException(ioException);
-    }
-    
-    /** Write a file safely - the change happens (the function returns true) or
-     * somthing went wrong (the function throws a runtime exception) and the file is not changed.
-     * Note that the tempfile must be in the same direct as the actual file so an OS-atomic rename can be done.  
-     */
-    public static boolean safeWrite(Path file, IOConsumer<OutputStream> writerAction) {
-        Path tmp = createTempFile(file.getParent(), file.getFileName().toString(), ".tmp");
-        return safeWrite(file, tmp, writerAction);
-    }
-
-    /** Write a file safely - the change happens (the function returns true) or
-     * somthing went wrong (the function throws a runtime exception) and the file is not changed.
-     * Note that the tempfile must be in the same direct as the actual file so an OS-atomic rename can be done.  
-     */
-    public static boolean safeWrite(Path file, Path tmpFile, IOConsumer<OutputStream> writerAction) {
-        try {
-            try(OutputStream out = new BufferedOutputStream(Files.newOutputStream(tmpFile)) ) {
-                writerAction.actionEx(out);
-            }
-            move(tmpFile, file);
-            return true;
-        } catch(IOException ex) { throw IOX.exception(ex); }
-    }
-
-    /** Delete a file. */
-    public static void delete(Path path) {
-        try { Files.delete(path); }
-        catch (IOException ex) {
-            FmtLog.error(IOX.class, ex, "IOException deleting %s", path);
-            throw IOX.exception(ex);
-        }
-    }
-    
-    /** Atomically move a file. */
-    public static void move(Path src, Path dst) {
-        try { Files.move(src, dst, StandardCopyOption.ATOMIC_MOVE) ; }
-        catch (IOException ex) {
-            FmtLog.error(IOX.class, ex, "IOException moving %s to %s", src, dst);
-            throw IOX.exception(ex);
-        }
-    }
-    
-    /** Copy a file, not atomic. *
-     * Can copy to a directory or over an existing file.
-     * @param srcFilename
-     * @param dstFilename
-     */
-    public static void copy(String srcFilename, String dstFilename) {
-        Path src = Paths.get(srcFilename);
-        if ( ! Files.exists(src) )
-            throw new RuntimeIOException("No such file: "+srcFilename);
-        
-        Path dst = Paths.get(dstFilename);
-        if ( Files.isDirectory(dst) )
-            dst = dst.resolve(src.getFileName());
-        
-        try { Files.copy(src, dst); }
-        catch (IOException ex) {
-            FmtLog.error(IOX.class, ex, "IOException copying %s to %s", srcFilename, dstFilename);
-            throw IOX.exception(ex);
-        }
-    }
-    
-    /** Create a directory - thgrow a runtime exception if theer are any problems.
-     * This function wraps {@code Files.createDirectory}.
-     */
-    public static void createDirectory(Path dir) {
-        try { Files.createDirectory(dir); }
-        catch (IOException ex) { throw IOX.exception(ex); }
-    }
-
-    /** Convert a {@link Path}  to a {@link Location}. */
-    public static Location asLocation(Path path) {
-        if ( ! Files.isDirectory(path) )
-            throw new RuntimeIOException("Path is not naming a directory: "+path);
-        return Location.create(path.toString());
-    }
-    
-//    /** Convert a {@link org.apache.jena.tdb.base.file.Location} to a {@link Path}. */
-//    public static Path asPath(org.apache.jena.tdb.base.file.Location location) {
-//        if ( location.isMem() )
-//            throw new RuntimeIOException("Location is a memory location: "+location);
-//        return Paths.get(location.getDirectoryPath());
-//    }
-
-    /** Convert a {@link Location} to a {@link Path}. */
-    public static Path asPath(org.seaborne.dboe.base.file.Location location) {
-        if ( location.isMem() )
-            throw new RuntimeIOException("Location is a memory location: "+location);
-        return Paths.get(location.getDirectoryPath());
-    }
-
-    /** Convert a {@link Location} to a {@link File}. */
-    public static File asFile(Location loc) {
-        return new File(loc.getDirectoryPath());
-    }
-
-    /** Read the whole of a file */
-    public static byte[] readAll(Path pathname) {
-        try {
-            return Files.readAllBytes(pathname);
-        } catch (IOException ex) { throw IOX.exception(ex); }
-    }
-    
-    /** Write the whole of a file */
-    public static void writeAll(Path pathname, byte[] value) {
-        try {
-            Files.write(pathname, value, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE);
-        } catch (IOException ex) { throw IOX.exception(ex); }
-    }
-
-//    /**
-//     * Ensure a directory exists, creating the directory and any missing
-//     * directories on the path to it.
-//     */
-//    public static void ensureDirectory(Path dir, FileAttribute<? >... attrs) {
-//        if ( Files.exists(dir) ) {
-//            if ( ! Files.isDirectory(dir) )
-//                throw new DeltaFileException("Exists but not a directory: "+dir);
-//        }
-//        try { Files.createDirectories(dir, attrs); }
-//        catch (IOException ex) { new DeltaFileException("Failed to create directory: "+dir, ex);}
-//    }
-//    
-//    /**
-//     * Ensure a file exists - create an empty one if not. This operation does
-//     * not create a directory path to the file.
-//     */
-//    public static void ensureFile(Path filePath, FileAttribute<? >... attrs) {
-//        if ( Files.exists(filePath) ) {
-//            if ( ! Files.isRegularFile(filePath) )
-//                throw new DeltaFileException("Exists but not a regular file: "+filePath);
-//        }
-//        try { Files.createFile(filePath); }
-//        catch (IOException ex) { new DeltaFileException("Failed to create file: "+filePath, ex);}
-//    }
-
-    /**
-     * Return a temporary filename path.
-     * <p>
-     * This operation is thread-safe.
-     */
-    public static Path createTempFile(Path dir, String prefix, String suffix, FileAttribute<? >... attrs) {
-        try {
-            return Files.createTempFile(dir, prefix, suffix, attrs);
-        } catch (IOException ex) { throw IOX.exception(ex); }
-    }
-    
-    /** Generate a unique place related to path; 
-     * Optionally, provide a mapping of old name to new nae base.
-     * This method always adds "-1", "-2" etc. 
-     */  
-    public static Path uniqueDerivedPath(Path path, Function<String, String> basenameMapping) {
-        String base = path.getFileName().toString();
-        if ( basenameMapping != null )
-            base = basenameMapping.apply(base);
-        // Some large limit "just in case"
-        for(int x = 0 ; x < 10_000 ; x++ ) {
-            String destname = base+"-"+x; 
-            Path destpath = path.resolveSibling(destname);
-            if ( ! Files.exists(destpath) )
-                return destpath;
-        }
-        return null ;
-    }
-    
-    private static Logger LOG = LoggerFactory.getLogger(Util.class);
-    
-    /** Find the files in this directory that have namebase as a prefix and 
-     *  are then numbered.
-     *  <p>
-     *  Returns a sorted list from, low to high index.
-     */
-    public static List<Path> scanForDirByPattern(Path directory, String namebase, String nameSep) {
-        Pattern pattern = Pattern.compile(Pattern.quote(namebase)+
-                                          Pattern.quote(nameSep)+
-                                          "[\\d]+");
-        List<Path> paths = new ArrayList<>();
-        try (DirectoryStream<Path> stream = Files.newDirectoryStream(directory, namebase + "*")) {
-            for ( Path entry : stream ) {
-                if ( !pattern.matcher(entry.getFileName().toString()).matches() ) {
-                    throw new DBOpEnvException("Invalid filename for matching: "+entry.getFileName());
-                    // Alternative: Skip bad trailing parts but more likely there is a naming problem.  
-                    //   LOG.warn("Invalid filename for matching: {} skipped", entry.getFileName());
-                    //   continue;
-                }
-                // Follows symbolic links.
-                if ( !Files.isDirectory(entry) )
-                    throw new DBOpEnvException("Not a directory: "+entry);
-                paths.add(entry);
-            }
-        }
-        catch (IOException ex) {
-            FmtLog.warn(LOG, "Can't inspect directory: (%s, %s)", directory, namebase);
-            throw new DBOpEnvException(ex);
-        }
-        Comparator<Path> comp = (f1, f2) -> {
-            int num1 = extractIndex(f1.getFileName().toString(), namebase, nameSep);
-            int num2 = extractIndex(f2.getFileName().toString(), namebase, nameSep);
-            return Integer.compare(num1, num2);
-        };
-        paths.sort(comp);
-        //indexes.sort(Long::compareTo);
-        return paths;
-    }
-
-    /** Given a filename in "base-NNNN" format, return the value of NNNN */
-    public static int extractIndex(String name, String namebase, String nameSep) {
-        int i = namebase.length()+nameSep.length();
-        String numStr = name.substring(i);
-        int num = Integer.parseInt(numStr);
-        return num;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/main/java/org/seaborne/tdb2/setup/TDBBuilder.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/setup/TDBBuilder.java b/tdb2/src/main/java/org/seaborne/tdb2/setup/TDBBuilder.java
index 4490e3f..66c5c89 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/setup/TDBBuilder.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/setup/TDBBuilder.java
@@ -49,8 +49,6 @@ import org.seaborne.tdb2.store.nodetupletable.NodeTupleTable ;
 import org.seaborne.tdb2.store.nodetupletable.NodeTupleTableConcrete ;
 import org.seaborne.tdb2.store.tupletable.TupleIndex ;
 import org.seaborne.tdb2.store.tupletable.TupleIndexRecord ;
-import org.seaborne.tdb2.sys.DatasetControl ;
-import org.seaborne.tdb2.sys.DatasetControlMRSW ;
 import org.seaborne.tdb2.sys.SystemTDB ;
 import org.slf4j.Logger ;
 import org.slf4j.LoggerFactory ;

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetControl.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetControl.java b/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetControl.java
new file mode 100644
index 0000000..b283ed4
--- /dev/null
+++ b/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetControl.java
@@ -0,0 +1,44 @@
+/*
+ *  Licensed 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.
+ *
+ *  See the NOTICE file distributed with this work for additional
+ *  information regarding copyright ownership.
+ */
+
+package org.seaborne.tdb2.store;
+
+import java.util.Iterator ;
+
+// Used in NodeTupleTables.
+
+/** A DatasetControl is an encapsulation of what to do on reads and writes.
+ *  In addition, iterators returned can be tied back to the original request
+ *  to check they are still valid.  
+ */
+public interface DatasetControl
+{
+    /** Fine grained, internal update - start */  
+    public void startUpdate() ;
+    
+    /** Fine grained, internal update - finish */
+    public void finishUpdate();
+
+    /** Signal the start of an internal read operation */
+    public void startRead();
+    
+    /** Signal the completion of an internal read operation */
+    public void finishRead();
+    
+    /** An iterator being created. */
+    public <T> Iterator<T> iteratorControl(Iterator<T> iter) ;
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetControlMRSW.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetControlMRSW.java b/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetControlMRSW.java
new file mode 100644
index 0000000..d4c9f57
--- /dev/null
+++ b/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetControlMRSW.java
@@ -0,0 +1,144 @@
+/*
+ *  Licensed 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.
+ *
+ *  See the NOTICE file distributed with this work for additional
+ *  information regarding copyright ownership.
+ */
+
+package org.seaborne.tdb2.store;
+
+import static java.lang.String.format ;
+
+import java.util.ConcurrentModificationException ;
+import java.util.Iterator ;
+import java.util.NoSuchElementException ;
+import java.util.concurrent.atomic.AtomicLong ;
+
+import org.apache.jena.atlas.iterator.Iter ;
+import org.apache.jena.atlas.lib.Closeable ;
+
+/** A policy that checks, but does not enforce, single writer or multiple writer locking policy */ 
+public class DatasetControlMRSW implements DatasetControl
+{
+    private final boolean concurrencyChecking = true ;
+    private final AtomicLong epoch = new AtomicLong(5) ;                // Update counters, used to check iterators. No need to start at 0.
+    private final AtomicLong readCounter = new AtomicLong(0) ;
+    private final AtomicLong writeCounter = new AtomicLong(0) ;
+    
+    public DatasetControlMRSW()
+    { }
+
+    @Override
+    public void startRead() {
+        readCounter.getAndIncrement() ;
+        checkConcurrency() ;
+    }
+
+    @Override
+    public void finishRead() {
+        readCounter.decrementAndGet() ;
+    }
+
+    @Override
+    public void startUpdate() {
+        epoch.getAndIncrement() ;
+        writeCounter.getAndIncrement() ;
+        checkConcurrency() ;
+    }
+
+    @Override
+    public void finishUpdate() {
+        writeCounter.decrementAndGet() ;
+    }
+
+    private void checkConcurrency() {
+        long R, W ;
+        synchronized (this) {
+            R = readCounter.get() ;
+            W = writeCounter.get() ;
+        }
+
+        if ( R > 0 && W > 0 )
+            policyError(R, W) ;
+        if ( W > 1 )
+            policyError(R, W) ;
+    }
+    
+    @Override
+    public <T> Iterator<T> iteratorControl(Iterator<T> iter) { return new IteratorCheckNotConcurrent<>(iter, epoch) ; }
+    
+    private static class IteratorCheckNotConcurrent<T> implements Iterator<T>, Closeable
+    {
+        private Iterator<T> iter ;
+        private AtomicLong eCount ;
+        private boolean finished = false ;
+        private long startEpoch ; 
+
+        IteratorCheckNotConcurrent(Iterator<T> iter, AtomicLong eCount) {
+            // Assumes correct locking to set up, i.e. eCount not changing
+            // (writer on separate thread).
+            this.iter = iter ;
+            this.eCount = eCount ;
+            this.startEpoch = eCount.get() ;
+        }
+
+        private void checkConcurrentModification() {
+            if ( finished )
+                return ;
+
+            long now = eCount.get() ;
+            if ( now != startEpoch ) {
+                policyError(format("Iterator: started at %d, now %d", startEpoch, now)) ;
+            }
+        }
+
+        @Override
+        public boolean hasNext() {
+            checkConcurrentModification() ;
+            boolean b = iter.hasNext() ;
+            if ( !b )
+                close() ;
+            return b ;
+        }
+
+        @Override
+        public T next() {
+            checkConcurrentModification() ;
+            try { return iter.next() ; }
+            catch (NoSuchElementException ex) {
+                close() ;
+                throw ex ;
+            }
+        }
+
+        @Override
+        public void remove() {
+            checkConcurrentModification() ;
+            iter.remove() ;
+        }
+
+        @Override
+        public void close() {
+            finished = true ;
+            Iter.close(iter) ;
+        }
+    }
+    
+    private static void policyError(long R, long W) {
+        policyError(format("Reader = %d, Writer = %d", R, W)) ;
+    }
+
+    private static void policyError(String message) {
+        throw new ConcurrentModificationException(message) ;
+    }    
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetControlNone.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetControlNone.java b/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetControlNone.java
new file mode 100644
index 0000000..b1bf51b
--- /dev/null
+++ b/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetControlNone.java
@@ -0,0 +1,48 @@
+/*
+ *  Licensed 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.
+ *
+ *  See the NOTICE file distributed with this work for additional
+ *  information regarding copyright ownership.
+ */
+
+package org.seaborne.tdb2.store;
+
+import java.util.Iterator ;
+
+/** Concurrency policy  that is a "free for all".
+ *  No checking, no locking.
+ */
+public final class DatasetControlNone implements DatasetControl
+{
+    @Override
+    public void startUpdate()
+    {}
+
+    @Override
+    public void finishUpdate()
+    {}
+
+    @Override
+    public void startRead()
+    {}
+
+    @Override
+    public void finishRead()
+    {}
+
+    @Override
+    public <T> Iterator<T> iteratorControl(Iterator<T> iter)
+    {
+        return iter ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetControlReadOnly.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetControlReadOnly.java b/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetControlReadOnly.java
new file mode 100644
index 0000000..85b31be
--- /dev/null
+++ b/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetControlReadOnly.java
@@ -0,0 +1,59 @@
+/*
+ *  Licensed 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.
+ *
+ *  See the NOTICE file distributed with this work for additional
+ *  information regarding copyright ownership.
+ */
+
+package org.seaborne.tdb2.store;
+
+import java.util.Iterator ;
+import java.util.concurrent.atomic.AtomicLong ;
+
+import org.seaborne.tdb2.TDBException ;
+
+/** A policy that provide read-only access */ 
+public class DatasetControlReadOnly implements DatasetControl
+{
+    private final AtomicLong readCounter = new AtomicLong(0) ;
+    
+    public DatasetControlReadOnly()
+    { }
+
+    @Override
+    public void startRead()
+    {
+        readCounter.getAndIncrement() ;
+    }
+
+    @Override
+    public void finishRead()
+    {
+        readCounter.decrementAndGet() ;
+    }
+
+    @Override
+    public void startUpdate()
+    {
+        throw new TDBException("Read-only") ;
+    }
+
+    @Override
+    public void finishUpdate()
+    {
+        throw new TDBException("Read-only") ;
+    }
+
+    @Override
+    public <T> Iterator<T> iteratorControl(Iterator<T> iter) { return iter ; }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetPrefixesTDB.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetPrefixesTDB.java b/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetPrefixesTDB.java
index fe21cd7..0b7f072 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetPrefixesTDB.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetPrefixesTDB.java
@@ -36,11 +36,8 @@ import org.seaborne.tdb2.store.nodetupletable.NodeTupleTable ;
  */
 public class DatasetPrefixesTDB implements DatasetPrefixStorage
 {
-    /* 
-     * Almost everything is cached in the prefix map asociated with the
-     * graph or dataset so this table is the persistent form and
-     * does not need a cache of it's own.   
-     */
+    // Consider a cache - like PrefixMappingCache was - but needs to respect transactions.
+    // See getPrefixMapping.
     
     static final RecordFactory factory = new RecordFactory(3*NodeId.SIZE, 0) ;
     static final String unamedGraphURI = "" ;
@@ -161,7 +158,6 @@ public class DatasetPrefixesTDB implements DatasetPrefixStorage
     @Override
     public PrefixMapping getPrefixMapping(String graphName) {
         PrefixMapping pm = new GraphPrefixesProjectionTDB(graphName, this) ;
-        pm = new PrefixMappingCache(pm) ;
         return pm ;
     }
     

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/main/java/org/seaborne/tdb2/store/GraphPrefixesProjectionTDB.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/GraphPrefixesProjectionTDB.java b/tdb2/src/main/java/org/seaborne/tdb2/store/GraphPrefixesProjectionTDB.java
index 330ee20..2c50740 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/store/GraphPrefixesProjectionTDB.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/store/GraphPrefixesProjectionTDB.java
@@ -28,8 +28,8 @@ import org.apache.jena.sparql.core.DatasetPrefixStorage ;
 public class GraphPrefixesProjectionTDB implements PrefixMapping {
     // Despite the name "TDB" this is general replacement for
     // PrefixMapping over DatasetPrefixStorage.
-    private String graphName ;
-    private DatasetPrefixStorage prefixes ; 
+    private final String graphName ;
+    private final DatasetPrefixStorage prefixes ; 
 
     public GraphPrefixesProjectionTDB(String graphName, DatasetPrefixStorage prefixes)
     { 
@@ -113,10 +113,11 @@ public class GraphPrefixesProjectionTDB implements PrefixMapping {
     @Override
     public String qnameFor(String uri) {
         int split = Util.splitNamespaceXML(uri) ;
-        String ns = uri.substring(0, split), local = uri.substring(split) ;
+        String ns = uri.substring(0, split); 
+        String local = uri.substring(split) ;
         if ( local.equals("") )
             return null ;
-        String prefix = prefixes.readByURI(graphName, uri) ;
+        String prefix = prefixes.readByURI(graphName, ns) ;
         return prefix == null ? null : prefix + ":" + local ;
     }
     

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/main/java/org/seaborne/tdb2/store/GraphTDB.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/GraphTDB.java b/tdb2/src/main/java/org/seaborne/tdb2/store/GraphTDB.java
index fc9c97f..a1e99ce 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/store/GraphTDB.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/store/GraphTDB.java
@@ -87,7 +87,7 @@ public class GraphTDB extends GraphView implements Closeable, Sync {
         if ( iterQuads == null )
             return org.apache.jena.util.iterator.NullIterator.instance() ;
         // Can't be duplicates - fixed graph node..
-        Iterator<Triple> iterTriples = new ProjectQuadsToTriples(Quad.defaultGraphIRI, iterQuads) ;
+        Iterator<Triple> iterTriples = projectQuadsToTriples(Quad.defaultGraphIRI, iterQuads) ;
         return WrappedIterator.createNoRemove(iterTriples) ;
     }
 
@@ -97,12 +97,11 @@ public class GraphTDB extends GraphView implements Closeable, Sync {
         if ( isUnionGraph(gn) )
             gn = Node.ANY ;
 
-        Iterator<Quad> iter = dataset.getQuadTable().find(gn, m.getMatchSubject(), m.getMatchPredicate(),
-                                                          m.getMatchObject()) ;
+        Iterator<Quad> iter = dataset.getQuadTable().find(gn, m.getMatchSubject(), m.getMatchPredicate(), m.getMatchObject()) ;
         if ( iter == null )
             return org.apache.jena.util.iterator.NullIterator.instance() ;
 
-        Iterator<Triple> iterTriples = new ProjectQuadsToTriples((gn == Node.ANY ? null : gn), iter) ;
+        Iterator<Triple> iterTriples = projectQuadsToTriples((gn == Node.ANY ? null : gn), iter) ;
 
         if ( gn == Node.ANY )
             iterTriples = Iter.distinct(iterTriples) ;
@@ -142,93 +141,18 @@ public class GraphTDB extends GraphView implements Closeable, Sync {
 			throw new TDBException("Expected a Tuple of 4, got: " + item);
 		return TupleFactory.tuple(item.get(1), item.get(2), item.get(3));
 	};
-
-    // Convert from Iterator<Quad> to Iterator<Triple>
-    static class ProjectQuadsToTriples implements Iterator<Triple> {
-        private final Iterator<Quad> iter ;
-        private final Node           graphNode ;
-
-        /**
-         * Project quads to triples - check the graphNode is as expected if not
-         * null
-         */
-        ProjectQuadsToTriples(Node graphNode, Iterator<Quad> iter) {
-            this.graphNode = graphNode ;
-            this.iter = iter ;
-        }
-
-        @Override
-        public boolean hasNext() {
-            return iter.hasNext() ;
-        }
-
-        @Override
-        public Triple next() {
-            Quad q = iter.next() ;
+	
+	private static Iterator<Triple> projectQuadsToTriples(Node graphNode, Iterator<Quad> iter) {
+	    // Checking.
+        Function<Quad, Triple> f = (q) -> {
             if ( graphNode != null && !q.getGraph().equals(graphNode) )
-                throw new InternalError("ProjectQuadsToTriples: Quads from unexpected graph (expected=" + graphNode
-                                        + ", got=" + q.getGraph() + ")") ;
-            return q.asTriple() ;
-        }
-
-        @Override
-        public void remove() {
-            iter.remove() ;
-        }
-    }
-
-    @Override
-    public Capabilities getCapabilities() {
-        if ( capabilities == null )
-            capabilities = new Capabilities() {
-                @Override
-                public boolean sizeAccurate() {
-                    return true ;
-                }
-
-                @Override
-                public boolean addAllowed() {
-                    return true ;
-                }
-
-                @Override
-                public boolean addAllowed(boolean every) {
-                    return true ;
-                }
-
-                @Override
-                public boolean deleteAllowed() {
-                    return true ;
-                }
-
-                @Override
-                public boolean deleteAllowed(boolean every) {
-                    return true ;
-                }
-
-                @Override
-                public boolean canBeEmpty() {
-                    return true ;
-                }
-
-                @Override
-                public boolean iteratorRemoveAllowed() {
-                    return false ;
-                } /* ** */
-
-                @Override
-                public boolean findContractSafe() {
-                    return true ;
-                }
-
-                @Override
-                public boolean handlesLiteralTyping() {
-                    return false ;
-                } /* ** */
-            } ;
-
-        return super.getCapabilities() ;
-    }
+                throw new InternalError("projectQuadsToTriples: Quads from unexpected graph (expected=" + graphNode + ", got=" + q.getGraph() + ")");
+            return q.asTriple();
+        };
+        // Without.
+        //Function<Quad, Triple> f = (q) -> q.asTriple();
+	    return Iter.map(iter, f);
+	}
 
     @Override
     public TransactionHandler getTransactionHandler() {

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/main/java/org/seaborne/tdb2/store/PrefixMappingCache.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/PrefixMappingCache.java b/tdb2/src/main/java/org/seaborne/tdb2/store/PrefixMappingCache.java
index 5fbe278..ca950d7 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/store/PrefixMappingCache.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/store/PrefixMappingCache.java
@@ -21,12 +21,13 @@ import java.util.Map ;
 import java.util.Optional ;
 import java.util.Map.Entry ;
 
+import org.apache.jena.atlas.iterator.Iter;
 import org.apache.jena.atlas.lib.Cache ;
 import org.apache.jena.atlas.lib.CacheFactory ;
 import org.apache.jena.rdf.model.impl.Util ;
 import org.apache.jena.shared.PrefixMapping ;
 
-// Non-transactional cache.
+// Unsafe. Non-transactional cache.
 public class PrefixMappingCache implements PrefixMapping {
 
     private final PrefixMapping other ;
@@ -121,6 +122,7 @@ public class PrefixMappingCache implements PrefixMapping {
 
     @Override
     public Map<String, String> getNsPrefixMap() {
+        // Ignore cache - get everything from the provider.
         return other.getNsPrefixMap() ;
     }
 
@@ -128,22 +130,21 @@ public class PrefixMappingCache implements PrefixMapping {
     // Libraryize?
     @Override
     public String expandPrefix(String prefixed) {
-        {
-            int colon = prefixed.indexOf(':') ;
-            if ( colon < 0 )
-                return prefixed ;
-            else {
-                String prefix = prefixed.substring(0, colon) ;
-                String uri = getNsPrefixURI(prefix) ;
-                return uri == null ? prefixed : uri + prefixed.substring(colon + 1) ;
-            }
+        int colon = prefixed.indexOf(':');
+        if ( colon < 0 )
+            return prefixed;
+        else {
+            String prefix = prefixed.substring(0, colon);
+            String uri = getNsPrefixURI(prefix);
+            return uri == null ? prefixed : uri + prefixed.substring(colon + 1);
         }
     }
 
     @Override
     public String qnameFor(String uri) {
         int split = Util.splitNamespaceXML(uri) ;
-        String ns = uri.substring(0, split), local = uri.substring(split) ;
+        String ns = uri.substring(0, split);
+        String local = uri.substring(split) ;
         if ( local.equals("") )
             return null ;
         String prefix = getNsURIPrefix(ns) ;
@@ -187,5 +188,15 @@ public class PrefixMappingCache implements PrefixMapping {
         return other.numPrefixes() ;
     }
 
+    private static String str(PrefixMapping pmap) {
+        return pmap.getNsPrefixMap().toString();
+    }
+    
+    @Override
+    public String toString() {
+        // Problem : only prints the cache.
+        String x = Iter.iter(prefixToUri.keys()).map(k->k+"->"+prefixToUri.getIfPresent(k)).asString(", ");
+        return "pm cache: ["+prefixToUri.size()+"] "+x+" : " + str(other);
+    }
 }
 

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/main/java/org/seaborne/tdb2/store/QuadTable.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/QuadTable.java b/tdb2/src/main/java/org/seaborne/tdb2/store/QuadTable.java
index 97dc784..15a14ff 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/store/QuadTable.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/store/QuadTable.java
@@ -27,7 +27,6 @@ import org.apache.jena.sparql.core.Quad ;
 import org.seaborne.tdb2.lib.TupleLib ;
 import org.seaborne.tdb2.store.nodetable.NodeTable ;
 import org.seaborne.tdb2.store.tupletable.TupleIndex ;
-import org.seaborne.tdb2.sys.DatasetControl ;
 
 /**
  * Quad table - a collection of TupleIndexes for 4-tuples together with a node

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/main/java/org/seaborne/tdb2/store/TableBase.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/TableBase.java b/tdb2/src/main/java/org/seaborne/tdb2/store/TableBase.java
index 81a9593..05b80ab 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/store/TableBase.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/store/TableBase.java
@@ -23,7 +23,6 @@ import org.seaborne.tdb2.store.nodetable.NodeTable ;
 import org.seaborne.tdb2.store.nodetupletable.NodeTupleTable ;
 import org.seaborne.tdb2.store.nodetupletable.NodeTupleTableConcrete ;
 import org.seaborne.tdb2.store.tupletable.TupleIndex ;
-import org.seaborne.tdb2.sys.DatasetControl ;
 
 public class TableBase implements Sync, Closeable
 {

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/main/java/org/seaborne/tdb2/store/TripleTable.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/TripleTable.java b/tdb2/src/main/java/org/seaborne/tdb2/store/TripleTable.java
index 2f17208..8e5aeb9 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/store/TripleTable.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/store/TripleTable.java
@@ -26,7 +26,6 @@ import org.apache.jena.graph.Triple ;
 import org.seaborne.tdb2.lib.TupleLib ;
 import org.seaborne.tdb2.store.nodetable.NodeTable ;
 import org.seaborne.tdb2.store.tupletable.TupleIndex ;
-import org.seaborne.tdb2.sys.DatasetControl ;
 
 /**
  * TripleTable - a collection of TupleIndexes for 3-tuples together with a node

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/TReadAppendFileTransport.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/TReadAppendFileTransport.java b/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/TReadAppendFileTransport.java
index 140047c..34eebfb 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/TReadAppendFileTransport.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/TReadAppendFileTransport.java
@@ -22,7 +22,7 @@ import java.util.Objects ;
 import org.apache.thrift.transport.TTransport ;
 import org.seaborne.dboe.base.file.BinaryDataFile ;
 
-/** A file transport that support random access read and
+/** A file transport that supports random access read and
  *  buffered append write.
  *  <p>
  *  Adapter TTransport -&gt; BinaryDataFile

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTable.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTable.java b/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTable.java
index 4a60695..89139c9 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTable.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTable.java
@@ -23,10 +23,10 @@ import org.apache.jena.atlas.lib.Closeable ;
 import org.apache.jena.atlas.lib.Sync ;
 import org.apache.jena.atlas.lib.tuple.Tuple ;
 import org.apache.jena.graph.Node ;
+import org.seaborne.tdb2.store.DatasetControl ;
 import org.seaborne.tdb2.store.NodeId ;
 import org.seaborne.tdb2.store.nodetable.NodeTable ;
 import org.seaborne.tdb2.store.tupletable.TupleTable ;
-import org.seaborne.tdb2.sys.DatasetControl ;
 
 public interface NodeTupleTable extends Sync, Closeable
 {

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableConcrete.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableConcrete.java b/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableConcrete.java
index 6d92abc..0d967f0 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableConcrete.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableConcrete.java
@@ -27,11 +27,11 @@ import org.apache.jena.atlas.lib.tuple.TupleFactory ;
 import org.apache.jena.graph.Node ;
 import org.seaborne.tdb2.TDBException ;
 import org.seaborne.tdb2.lib.TupleLib ;
+import org.seaborne.tdb2.store.DatasetControl ;
 import org.seaborne.tdb2.store.NodeId ;
 import org.seaborne.tdb2.store.nodetable.NodeTable ;
 import org.seaborne.tdb2.store.tupletable.TupleIndex ;
 import org.seaborne.tdb2.store.tupletable.TupleTable ;
-import org.seaborne.tdb2.sys.DatasetControl ;
 
 /** Group a tuple table and node table together to provide a real NodeTupleTable */
 public class NodeTupleTableConcrete implements NodeTupleTable

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableWrapper.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableWrapper.java b/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableWrapper.java
index f47d78e..e54f964 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableWrapper.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableWrapper.java
@@ -21,10 +21,10 @@ import java.util.Iterator ;
 
 import org.apache.jena.atlas.lib.tuple.Tuple ;
 import org.apache.jena.graph.Node ;
+import org.seaborne.tdb2.store.DatasetControl ;
 import org.seaborne.tdb2.store.NodeId ;
 import org.seaborne.tdb2.store.nodetable.NodeTable ;
 import org.seaborne.tdb2.store.tupletable.TupleTable ;
-import org.seaborne.tdb2.sys.DatasetControl ;
 
 public class NodeTupleTableWrapper implements NodeTupleTable
 {

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/main/java/org/seaborne/tdb2/sys/ConnectionTracker.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/sys/ConnectionTracker.java b/tdb2/src/main/java/org/seaborne/tdb2/sys/ConnectionTracker.java
new file mode 100644
index 0000000..f2950eb
--- /dev/null
+++ b/tdb2/src/main/java/org/seaborne/tdb2/sys/ConnectionTracker.java
@@ -0,0 +1,23 @@
+/*
+ * 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.seaborne.tdb2.sys;
+
+abstract class ConnectionTracker<X> {
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseConnection.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseConnection.java b/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseConnection.java
new file mode 100644
index 0000000..3fa4983
--- /dev/null
+++ b/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseConnection.java
@@ -0,0 +1,164 @@
+/*
+ * 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.seaborne.tdb2.sys;
+
+import java.io.IOException ;
+import java.nio.file.Path ;
+import java.nio.file.Paths ;
+import java.util.Map ;
+import java.util.concurrent.ConcurrentHashMap ;
+
+import org.apache.jena.atlas.io.IO ;
+import org.apache.jena.atlas.lib.FileOps ;
+import org.apache.jena.sparql.core.DatasetGraph ;
+import org.seaborne.dboe.base.file.Location ;
+import org.seaborne.dboe.base.file.ProcessFileLock ;
+import org.seaborne.dboe.sys.Names ;
+import org.seaborne.tdb2.TDBException ;
+import org.seaborne.tdb2.repack.DatasetGraphSwitchable ;
+import org.seaborne.tdb2.setup.StoreParams ;
+
+// StoreConnection, DatabaseConnection < Connection<X> 
+
+public class DatabaseConnection {
+    // ConnectionTracker<X> 
+    
+    private static Map<Location, DatabaseConnection> cache = new ConcurrentHashMap<>() ;
+    
+    /** Get the {@code DatabaseConnection} to a location, 
+     *  creating the storage structures if it does not exist. */ 
+    public synchronized static DatabaseConnection connectCreate(Location location) {
+        return connectCreate(location, null) ;
+    }
+
+    /** Get the {@code DatabaseConnection} to a location, 
+     *  creating the storage structures if it does not exist.
+     *  Use the provided {@link StoreParams} - any persistent setting 
+     *  already at the location take precedence.
+     */ 
+    public synchronized static DatabaseConnection connectCreate(Location location, StoreParams params) {
+        return make(location, params) ;
+    }
+    
+    /**
+     * Return a {@code StoreConnection} for a particular location,
+     * creating it if it does not exist in storage.
+     */
+    private synchronized static DatabaseConnection make(Location location, StoreParams params) {
+        if ( location.isMemUnique() ) {
+            // Uncached, in-memory. 
+            DatasetGraph dsg = DatabaseOps.create(location);
+            DatabaseConnection dbConn = new DatabaseConnection(dsg, location, null);
+            return dbConn;
+        }
+        // Cached by Location. Named in-memory or on-disk.
+        ProcessFileLock lock = null;
+        if (SystemTDB.DiskLocationMultiJvmUsagePrevention && ! location.isMem() ) {
+            lock = lockForLocation(location);
+            // Take the lock.  This is atomic.
+            lock.lockEx();
+        }
+        final ProcessFileLock lock2 = lock;
+        DatabaseConnection dbConn = cache.computeIfAbsent(location, (loc)->buildForCache(loc, lock2, params));
+        return dbConn ;
+    }
+    
+    private static DatabaseConnection buildForCache(Location location,  ProcessFileLock lock, StoreParams params) {
+        if ( location.isMemUnique() ) {
+            throw new TDBException("Can't buildForCache a memory-unique location");
+        }
+        DatasetGraph dsg = DatabaseOps.create(location);
+        return new DatabaseConnection(dsg, location, lock) ;
+    }
+    
+//    private static DatasetGraph buildMem(Location location, StoreParams params) {
+//        return StoreConnection.connectCreate(location, params).getDatasetGraph(); 
+//    }
+//
+//    private static DatasetGraph buildDisk(Location location, StoreParams params) {
+//        return DatabaseOps.create(location);
+//    }
+
+    // DRY
+    /** Create or fetch a {@link ProcessFileLock} for a Location */
+    public static ProcessFileLock lockForLocation(Location location) {
+        FileOps.ensureDir(location.getDirectoryPath());
+        String lockFilename = location.getPath(Names.TDB_LOCK_FILE);
+        Path path = Paths.get(lockFilename);
+        try {
+            path.toFile().createNewFile();
+        } catch(IOException ex) { IO.exception(ex); return null; }
+        return ProcessFileLock.create(lockFilename);
+    }
+    
+    /** Stop managing a location. Use with great care (testing only). */
+    public static synchronized void expel(Location location, boolean force) {
+        DatabaseConnection dbConn = cache.get(location) ;
+        if (dbConn == null) return ;
+        cache.remove(location) ;
+        dbConn.isValid = false;
+        //dbConn.datasetGraph = null;
+        //dbConn.datasetGraphSwitchable = null;
+        StoreConnection.expel(location, force);
+        
+        //    if (!force && sConn.transactionManager.activeTransactions()) 
+        //        throw new TDBTransactionException("Can't expel: Active transactions for location: " + location) ;
+
+        cache.remove(location) ;
+        // Release the lock after the cache is emptied.
+        if (SystemTDB.DiskLocationMultiJvmUsagePrevention && ! location.isMem() ) {
+            if ( ! dbConn.lock.isLockedHere() )
+                SystemTDB.errlog.warn("Location " + location.getDirectoryPath() + " was not locked by this process.");
+            dbConn.lock.unlock();
+            ProcessFileLock.release(dbConn.lock);
+        }
+    }
+    
+    // One of the other.
+    private final DatasetGraphSwitchable   datasetGraphSwitchable;
+    private final DatasetGraph             datasetGraph;
+    // This is the location of the TDB2 container directory. 
+    private final Location          location ;
+    private final ProcessFileLock   lock ;
+    private boolean                 isValid = true ;
+    
+    private DatabaseConnection(DatasetGraph dsg, Location location, ProcessFileLock fileLock)
+    {
+        this.datasetGraph = dsg;
+        this.datasetGraphSwitchable =  ( dsg instanceof DatasetGraphSwitchable ) ? (DatasetGraphSwitchable )dsg : null;
+        this.location = location ;
+        this.lock = fileLock;
+    }
+
+    public DatasetGraph getDatasetGraph() {
+        return datasetGraph;
+    }
+
+    public DatasetGraphSwitchable getDatasetGraphSwitchable() {
+        return datasetGraphSwitchable;
+    }
+    
+    public Location getLocation() {
+        return location ;
+    }
+    
+    public ProcessFileLock getLock() {
+        return lock ;
+    }
+}


[39/50] [abbrv] jena git commit: JENA-1396: Merge //github/afs/mantis as subdirectory jena-db/

Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/ComponentIds.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/ComponentIds.java
index 0000000,0000000..b7f27dc
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/ComponentIds.java
@@@ -1,0 -1,0 +1,61 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction.txn;
++
++
++/** Some fixed, marker, {@link ComponentId}s */
++public class ComponentIds {
++    
++    public static final ComponentId idNull          = make("Monitor",  -9999) ;
++    public static final ComponentId idSystem        = make("System",   -9988) ;
++    
++    private static ComponentId make(String string, int v) {
++        return ComponentId.alloc(string, null, v) ;
++    }
++    
++    
++//    
++//    // Linux : "uuid -v 4"
++//    
++//    /* For single instance components and base ids for incremental registration */
++//    public static final ComponentId idTxnMRSW       = make("MRSW",     "93a58341-ed53-4f0c-bac1-d9969ea38cf3") ;
++//    // TODO tie to registration (this predates)
++//    public static final ComponentId idTxnCounter    = make("Counter",  "6b901671-e6db-45c5-9217-7506d21a0000") ;
++//    public static final ComponentId idMonitor       = make("Monitor",  "c4d8a1e6-052b-413a-8d80-c5a6b442e608") ;
++//    public static final ComponentId idNull          = make("Monitor",  "e6e31271-b6dc-452c-b624-d4e099464365") ;
++//    public static final ComponentId idSystem        = make("System",   "95e0f729-ad29-48b2-bd70-e3738663c578") ;
++//    public static final ComponentId idBlobBase      = make("Blob",     "43436b91-87ce-4d6b-827c-c3b9ea6536ba") ;
++//    public static final ComponentId idDev           = make("Blob",     "82a6833a-1475-495a-83ca-10370c7c40cd") ;
++//    
++//    public static ComponentId make(String label, String uuidStr) {
++//        byte[] bytes = L.uuidAsBytes(uuidStr) ;
++//        return ComponentId.create(label, bytes) ;
++//    }
++    
++    /* For later
++    82a6833a-1475-495a-83ca-10370c7c40cd
++    1d32231b-aa11-47ed-8893-6b36673fe04c
++    27c4845e-c05a-410d-8c74-278a23b03bbd
++    f841ba46-a297-487d-b622-dd452c888dab
++    09efba54-7428-4689-929d-b1719a56c345
++    a9fbdc3c-442d-4086-8f40-b6ef773871b9
++    */
++
++
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/PrepareState.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/PrepareState.java
index 0000000,0000000..9f4431e
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/PrepareState.java
@@@ -1,0 -1,0 +1,40 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction.txn;
++
++import java.nio.ByteBuffer ;
++
++final
++public class PrepareState {
++    private final ComponentId component ;
++    private final ByteBuffer data ;
++    
++    public /*For testing*/ PrepareState(ComponentId compoent, ByteBuffer data) {
++        this.component = compoent ;
++        this.data = data ;
++    }
++
++    public ComponentId getComponent() {
++        return component ;
++    }
++
++    public ByteBuffer getData() {
++        return data ;
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/QuorumGenerator.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/QuorumGenerator.java
index 0000000,0000000..3303c30
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/QuorumGenerator.java
@@@ -1,0 -1,0 +1,27 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction.txn;
++
++import org.apache.jena.query.ReadWrite ;
++
++/** Generate  a transaction quorum for a transaction as it begins */  
++public interface QuorumGenerator {
++    public ComponentGroup genQuorum(ReadWrite mode) ;
++}
++
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/StateMgrBase.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/StateMgrBase.java
index 0000000,0000000..2a78548
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/StateMgrBase.java
@@@ -1,0 -1,0 +1,175 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction.txn;
++
++import java.nio.ByteBuffer ;
++
++import org.apache.jena.atlas.lib.Closeable ;
++import org.apache.jena.atlas.lib.Sync ;
++import org.seaborne.dboe.base.file.BufferChannel ;
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++/** Helper class for the manage the persistent state of a transactional.
++ * The persistent state is assumed to be a fixed size, or at least
++ * of known maximum size.
++ * May not be suitable for all transactional component implementations.
++ */
++
++public abstract class StateMgrBase implements Sync, Closeable {
++    /* Compare to TransBlob which is a similar idea but where it is a
++     * component in the transaction component group. That can lead to
++     * the wrong control, or at least unclear, of the writing during
++     * the transaction commit cycle.  
++     */
++    
++    private static Logger log = LoggerFactory.getLogger(StateMgrBase.class) ;
++
++    private final BufferChannel storage ;
++    // One ByteBuffer that is reused where possible.
++    // This is short-term usage 
++    // *  get disk state-> deserialize
++    // *  serialize->set disk state
++    // on a single thread (the transaction writer).
++    private ByteBuffer bb ;
++    // Is the internal state out of sync with the disk state? 
++    private boolean dirty = false ;
++    
++    protected StateMgrBase(BufferChannel storage, int sizeBytes) {
++        bb = ByteBuffer.allocate(sizeBytes) ;
++        this.storage = storage ;
++    }
++
++    /**
++     * After the default initial state is known, call this, for example, at the
++     * end of the constructor.  If no on-disk state is found, a clean copy is written.
++     */
++    protected void init() {
++        if ( ! storage.isEmpty() )
++            readState() ;
++        else
++            writeState() ;
++    }
++    
++    /* Serialize the necessary state into a ByteBuffer.
++     * The argument ByteBuffer can be used or a new one returned
++     * if it is the wrong size (e.g. too small).  The returned one will become the
++     * recycled ByteBuffer. The returned ByteBuffer should have posn/limit
++     * delimiting the necessary space to write. 
++     */
++    protected abstract ByteBuffer serialize(ByteBuffer bytes) ;
++    
++    /*
++     * Deserialize the persistent state from the ByteBuffer (delimited by posn/limit).
++     * The byte buffer will be the recycled one from last time.
++     * Most  
++     */
++    protected abstract void deserialize(ByteBuffer bytes) ;
++    
++    /** Note that the in-memory state is not known to be the same
++     * as the on-disk state.  
++     */
++    protected void setDirtyFlag() {
++        dirty = true ;
++    }
++
++    /**
++     * Event call for state writing. Called after successful 
++     * writing of the state.
++     */
++    protected abstract void writeStateEvent() ;
++    
++    /**
++     * Event call for state reading. Called after successful 
++     * deserializing of the state.
++     */
++    protected abstract void readStateEvent() ;
++    
++    /** Note that the in-memory state is the same
++     * as the on-disk state, or at least the on-disk state is
++     * acceptable for restart at any time. 
++     */
++    protected void clearDirtyFlag() {
++        dirty = false ;
++    }
++
++    /** Low level control - for example, used for cloning setup.
++     * Use with care.
++     */
++    public BufferChannel getBufferChannel() {
++        return storage ;
++    }    
++    
++    /** Return the serialized state using the internal ByteBuffer
++     * Typically called by "prepare" for the bytes to write to the journal.
++     * Calls {@link #serialize}.  
++     * This method does not perform an external I/O.
++     */
++    public ByteBuffer getState() {
++        bb.rewind() ;
++        serialize(bb) ;
++        return bb ;
++    }
++    
++    /** Set the in-memory state from a ByteBuffer, for example, from journal recovery.
++     * This method does not perform an external I/O.
++     * Call "writeState" to put the n-memory state as the disk state.
++     */
++    public void setState(ByteBuffer buff) {
++        buff.rewind() ;
++        deserialize(buff) ;
++        dirty = true ;
++    }
++
++    //public BufferChannel getChannel() { return storage ; }
++    
++    /** The write process : serialize, write, sync,
++     * After this, the bytes definitely are on disk, not in some OS cache
++     */
++    public void writeState() {
++        bb.rewind() ;
++        ByteBuffer bb1 = serialize(bb) ;
++        if ( bb1 != null )
++            bb = bb1 ;
++        bb.rewind() ;
++        int len = storage.write(bb, 0) ;
++        storage.sync() ;
++        dirty = false ;
++        writeStateEvent() ;
++    }
++
++    /** The read process : get all bytes on disk, deserialize */ 
++    public void readState() {
++        bb.rewind() ;
++        int len = storage.read(bb, 0) ;
++        bb.rewind() ;
++        deserialize(bb) ;
++        readStateEvent() ;
++    }
++
++    @Override
++    public void sync() {
++        if ( dirty )
++            writeState() ;
++    }
++
++    @Override
++    public void close() {
++        storage.close() ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/StateMgrData.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/StateMgrData.java
index 0000000,0000000..17db3be
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/StateMgrData.java
@@@ -1,0 -1,0 +1,87 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction.txn;
++
++import java.nio.ByteBuffer ;
++import java.util.Arrays ;
++
++import org.seaborne.dboe.base.file.BufferChannel ;
++
++/** StateManagement for a number of long values, a common need
++ * (might as well store ints as long on disk
++ * for small numbers of integers)
++ */
++public class StateMgrData extends StateMgrBase {
++    private final long[] data ;
++    
++    public StateMgrData(BufferChannel storage, long... initialData) {
++        super(storage, numBytes(initialData)) ;
++        data = copy(initialData) ;
++        super.init() ;
++    }
++
++    @Override
++    protected void init() { throw new TransactionException("Don't call init()") ; }  
++    
++    private static long[] copy(long[] data) { return Arrays.copyOf(data, data.length) ; }
++    
++    private static int numBytes(long[] data) { return data.length * Long.BYTES ; }
++    
++    // Protected - leave whether to expose these operations as "public"
++    // to the subclass.  A subclass may choose instead to make these
++    // to more meaningful names, or ensure that daat consistentecny rules are applied.
++    
++    protected long[] getData() {
++        return copy(data) ;
++    }
++
++    protected void setData(long... newData) {
++        if ( newData.length != data.length )
++            throw new IllegalArgumentException() ; 
++        System.arraycopy(newData, 0, data, 0, data.length);
++    }
++
++    protected long get(int i) {
++        return data[i] ;
++    }
++
++    protected void set(int i, long v) {
++        data[i] = v ;
++        super.setDirtyFlag() ;
++    }
++
++    @Override
++    protected ByteBuffer serialize(ByteBuffer bytes) {
++        for ( int i = 0 ; i < data.length ; i++ )
++            bytes.putLong(data[i]) ;
++        return bytes ;
++    }
++
++    @Override
++    protected void deserialize(ByteBuffer bytes) {
++        for ( int i = 0 ; i < data.length ; i++ )
++            data[i] = bytes.getLong() ;
++    }
++
++    @Override
++    protected void writeStateEvent() {}
++
++    @Override
++    protected void readStateEvent() {}
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/StateMgrDataIdx.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/StateMgrDataIdx.java
index 0000000,0000000..8f1c818
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/StateMgrDataIdx.java
@@@ -1,0 -1,0 +1,44 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction.txn;
++
++import org.seaborne.dboe.base.file.BufferChannel ;
++
++/** StateManagement for a number of long values, 
++ *  state accessed by index.
++ */
++public final class StateMgrDataIdx extends StateMgrData {
++    public StateMgrDataIdx(BufferChannel storage, long... initialData) {
++        super(storage, initialData) ;
++    }
++
++    // Expose these operations as public
++    
++    @Override
++    public long[] getData()              { return super.getData() ; }
++
++    @Override
++    public void setData(long... newData) { super.setData(newData); }
++
++    @Override
++    public long get(int i)           { return super.get(i) ; }
++
++    @Override
++    public void set(int i, long v)   { super.set(i, v); }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/SysTrans.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/SysTrans.java
index 0000000,0000000..091b635
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/SysTrans.java
@@@ -1,0 -1,0 +1,52 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction.txn;
++
++import java.nio.ByteBuffer ;
++
++/** Single component aspect of a transaction */  
++final 
++class SysTrans {
++    private final TransactionalComponent elt ;
++    private final Transaction transaction ;
++    private final TxnId txnId ;
++
++    public SysTrans(TransactionalComponent elt, Transaction transaction, TxnId txnId) { 
++        this.elt = elt ;
++        this.transaction = transaction ;
++        this.txnId = txnId ;
++    }
++
++    public void begin()                 { }
++    public boolean promote()            { return elt.promote(transaction) ; }
++
++    public ByteBuffer commitPrepare()   { return elt.commitPrepare(transaction) ; }
++
++    public void commit()                { elt.commit(transaction); }
++
++    public void commitEnd()             { elt.commitEnd(transaction); }
++
++    public void abort()                 { elt.abort(transaction); }
++
++    public void complete()              { elt.complete(transaction); }
++    
++    public Transaction getTransaction()             { return transaction ; } 
++    public TxnId getTxnId()                         { return txnId ; } 
++    public TransactionalComponent getComponent()    { return elt ; }
++    public ComponentId getComponentId()             { return elt.getComponentId() ; }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/SysTransState.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/SysTransState.java
index 0000000,0000000..8bf9e5c
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/SysTransState.java
@@@ -1,0 -1,0 +1,35 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction.txn;
++
++/** State for one detached components. */  
++public class SysTransState {
++    private final TransactionalComponent elt ;
++    private final Transaction transaction ;
++    private final Object state ;
++
++    public SysTransState(TransactionalComponent elt, Transaction transaction, Object state) { 
++        this.elt = elt ;
++        this.transaction = transaction ;
++        this.state = state ;
++    }
++
++    public TransactionalComponent getComponent()    { return elt ; }
++    public Transaction getTransaction()             { return transaction ; } 
++    public Object getState()                         { return state ; } 
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/Transaction.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/Transaction.java
index 0000000,0000000..8f17352
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/Transaction.java
@@@ -1,0 -1,0 +1,294 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction.txn;
++
++import static org.seaborne.dboe.transaction.txn.TxnState.ABORTED;
++import static org.seaborne.dboe.transaction.txn.TxnState.ACTIVE;
++import static org.seaborne.dboe.transaction.txn.TxnState.COMMIT;
++import static org.seaborne.dboe.transaction.txn.TxnState.COMMITTED;
++import static org.seaborne.dboe.transaction.txn.TxnState.DETACHED;
++import static org.seaborne.dboe.transaction.txn.TxnState.END_ABORTED;
++import static org.seaborne.dboe.transaction.txn.TxnState.END_COMMITTED;
++import static org.seaborne.dboe.transaction.txn.TxnState.INACTIVE;
++import static org.seaborne.dboe.transaction.txn.TxnState.PREPARE;
++
++import java.util.List ;
++import java.util.Objects ;
++import java.util.concurrent.atomic.AtomicReference ;
++
++import org.apache.jena.query.ReadWrite ;
++
++/** 
++ * A transaction as the composition of actions on components. 
++ * Works in conjunction with the TransactionCoordinator 
++ * to provide the transaction lifecycle.
++ * @see TransactionCoordinator
++ * @see TransactionalComponent
++ */
++final
++public class Transaction implements TransactionInfo {
++    // Using an AtomicReference<TxnState> requires that 
++    // TransactionalComponentLifecycle.internalComplete
++    // frees the thread local for the threadTxn, otherwise memory
++    // usage grows. If a plain member variable is used slow growth
++    // is still seen. 
++    // Nulling txnMgr and clearing components stops that slow growth.
++
++    private TransactionCoordinator txnMgr ;
++    private final TxnId txnId ;
++    private final List<SysTrans> components ;
++    
++    // Using an AtomicReference makes this observable from the outside.
++    // It also allow for multithreaded transactions (later). 
++    private final AtomicReference<TxnState> state = new AtomicReference<>() ;
++    //private TxnState state ;
++    private long dataVersion ;
++    private ReadWrite mode ;
++    
++    public Transaction(TransactionCoordinator txnMgr, TxnId txnId, ReadWrite readWrite, long dataVersion, List<SysTrans> components) {
++        Objects.requireNonNull(txnMgr) ;
++        Objects.requireNonNull(txnId) ;
++        Objects.requireNonNull(readWrite) ;
++        Objects.requireNonNull(components) ;
++        this.txnMgr = txnMgr ;
++        this.txnId = txnId ;
++        this.mode = readWrite ;
++        this.dataVersion = dataVersion ;
++        this.components = components ;
++        setState(INACTIVE) ;
++    }
++    
++    /*package*/ void resetDataVersion(long dataVersion) {
++        this.dataVersion = dataVersion;
++    }
++
++    /*package*/ void setState(TxnState newState) {
++        state.set(newState) ;
++    }
++
++    @Override
++    public TxnState getState() {
++        return state.get() ;
++    }
++
++    /**
++     * Each transaction is allocated a serialization point by the transaction
++     * coordinator. Normally, this is related to this number and it increases
++     * over time as the data changes. Two readers can have the same
++     * serialization point - they are working with the same view of the data.
++     */
++    @Override
++    public long getDataVersion() {
++        return dataVersion ;
++    }
++
++    public void begin() {
++        checkState(INACTIVE);
++        components.forEach((c) -> c.begin()) ;
++        setState(ACTIVE) ;
++    }
++    
++    public boolean promote() {
++        checkState(ACTIVE);
++        boolean b = txnMgr.promoteTxn(this) ;
++        if ( !b )
++            return false ;
++        mode = ReadWrite.WRITE;
++        return true ;
++    }
++    
++    /*package*/ void promoteComponents() {
++        // Call back from the Transaction coordinator during promote.
++        components.forEach((c) -> {
++            if ( ! c.promote() )
++                throw new TransactionException("Failed to promote") ;
++        }) ;
++        mode = ReadWrite.WRITE ;
++    }
++    
++    public void notifyUpdate() {
++        checkState(ACTIVE) ;
++        if ( mode == ReadWrite.READ ) {
++            System.err.println("notifyUpdate - promote needed") ;
++            promote() ;
++            mode = ReadWrite.WRITE ;
++        }
++    }
++    
++    public void prepare() {
++        checkState(ACTIVE) ;
++        if ( mode == ReadWrite.WRITE ) 
++            txnMgr.executePrepare(this) ;
++        setState(PREPARE);
++    }
++    
++    public void commit() { 
++        // Split into READ and WRITE forms.
++        TxnState s = getState();
++        if ( s == ACTIVE ) 
++            // Auto exec prepare().
++            prepare() ;
++        checkState(PREPARE) ;
++        setState(COMMIT) ;
++        switch(mode) {
++            case WRITE:
++                txnMgr.executeCommit(this,
++                                     ()->{components.forEach((c) -> c.commit()) ; } ,
++                                     ()->{components.forEach((c) -> c.commitEnd()) ; } ) ;
++                break ;
++            case READ:
++                // Different lifecycle?
++                txnMgr.executeCommit(this, 
++                                     ()->{components.forEach((c) -> c.commit()) ; } ,
++                                     ()->{components.forEach((c) -> c.commitEnd()) ; } ) ;
++                break ;
++        }
++        setState(COMMITTED) ;
++    }
++    
++    public void abort() {
++        abort$();
++        endInternal() ;
++    }
++
++    private void abort$() {
++        // Split into READ and WRITE forms.
++        checkState(ACTIVE, ABORTED) ;
++        // Includes notification start/finish
++        txnMgr.executeAbort(this, ()-> { components.forEach((c) -> c.abort()) ; }) ;
++        setState(ABORTED) ;
++    }
++
++    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$() ;
++        }
++        endInternal() ;
++        txnMgr.notifyEndFinish(this) ;
++        txnMgr = null ;
++        //components.clear() ;
++    }
++    
++    private void endInternal() {  
++        if ( hasFinalised() )
++            return ;
++        // Called once, at the first abort/commit/end.
++        txnMgr.notifyCompleteStart(this);
++        components.forEach((c) -> c.complete()) ;
++        txnMgr.completed(this) ;
++        if ( getState() == COMMITTED )
++            setState(END_COMMITTED);
++        else
++            setState(END_ABORTED);
++        txnMgr.notifyCompleteFinish(this);
++    }
++    
++    /*package*/ List<SysTrans> getComponents() {
++        return components ;
++    }
++    
++    /*package*/ void detach() {
++        checkState(ACTIVE,PREPARE) ;
++        setState(DETACHED) ;
++    }
++    
++    /*package*/ void attach() {
++        checkState(DETACHED) ;
++        setState(ACTIVE) ;
++    }
++    
++    public void requireWriteTxn() {
++        checkState(ACTIVE) ;
++        if ( mode != ReadWrite.WRITE )
++            throw new TransactionException("Not a write transaction") ;
++    }
++
++    @Override
++    public boolean hasStarted()   { 
++        TxnState x = getState() ;
++        return x == INACTIVE ;
++    }
++    
++    @Override
++    public boolean hasFinished() { 
++        TxnState x = getState() ;
++        return x == COMMITTED || x == ABORTED || x == END_COMMITTED || x == END_ABORTED ;
++    }
++
++    @Override
++    public boolean hasFinalised() { 
++        TxnState x = getState() ;
++        return x == END_COMMITTED || x == END_ABORTED ;
++    }
++
++    @Override
++    public TxnId getTxnId()     { return txnId ; } 
++
++    @Override
++    public ReadWrite getMode()  { return mode ; }
++    
++    /** Is this a READ transaction?
++     * Convenience operation equivalent to {@code (getMode() == READ)}
++     */
++    @Override
++    public boolean isReadTxn()  { return mode == ReadWrite.READ ; }
++
++    /** Is this a WRITE transaction?
++     * Convenience operation equivalent to {@code (getMode() == WRITE)}
++     */
++    @Override
++    public boolean isWriteTxn()  { return mode == ReadWrite.WRITE ; }
++    
++    // hashCode/equality
++    // These must be object equality.  No two transactions objects are .equals unless they are ==   
++    
++    private void checkWriteTxn() {
++        if ( ! isActiveTxn() || ! isWriteTxn() )
++            throw new TransactionException("Not in a write transaction") ;
++    }
++
++    // XXX Duplicate -- TransactionalComponentLifecycle
++    private void checkState(TxnState expected) {
++        TxnState s = getState();
++        if ( s != expected )
++            throw new TransactionException("Transaction is in state "+s+": expected state "+expected) ;
++    }
++
++    private void checkState(TxnState expected1, TxnState expected2) {
++        TxnState s = getState();
++        if ( s != expected1 && s != expected2 )
++            throw new TransactionException("Transaction is in state "+s+": expected state "+expected1+" or "+expected2) ;
++    }
++
++    // Avoid varargs ... undue worry?
++    private void checkState(TxnState expected1, TxnState expected2, TxnState expected3) {
++        TxnState s = getState();
++        if ( s != expected1 && s != expected2 && s != expected3 )
++            throw new TransactionException("Transaction is in state "+s+": expected state "+expected1+", "+expected2+" or "+expected3) ;
++    }
++    
++    @Override
++    public boolean isActiveTxn() {
++        return getState() != INACTIVE ;
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionCoordinator.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionCoordinator.java
index 0000000,0000000..cede020
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionCoordinator.java
@@@ -1,0 -1,0 +1,804 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction.txn;
++
++import static org.apache.jena.query.ReadWrite.WRITE ;
++import static org.seaborne.dboe.transaction.txn.journal.JournalEntryType.UNDO ;
++
++import java.nio.ByteBuffer ;
++import java.util.ArrayList ;
++import java.util.Iterator ;
++import java.util.List ;
++import java.util.Objects ;
++import java.util.concurrent.ConcurrentHashMap ;
++import java.util.concurrent.Semaphore ;
++import java.util.concurrent.atomic.AtomicLong ;
++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.seaborne.dboe.base.file.Location ;
++import org.seaborne.dboe.sys.Sys ;
++import org.seaborne.dboe.transaction.txn.journal.Journal ;
++import org.seaborne.dboe.transaction.txn.journal.JournalEntry ;
++import org.slf4j.Logger ;
++
++/**
++ * One {@code TransactionCoordinator} per group of {@link TransactionalComponent}s.
++ * {@link TransactionalComponent}s can not be shared across TransactionCoordinators.
++ * <p>
++ * This is a general engine although tested and most used for multiple-reader
++ * and single-writer (MR+SW). {@link TransactionalComponentLifecycle} provides the
++ * per-threadstyle.
++ * <p>
++ * Contrast to MRSW: multiple-reader or single-writer.
++ * <h3>Block writers</h3>
++ * Block until no writers are active.
++ * When this returns, this guarantees that the database is not changing
++ * and the journal is flushed to disk.
++ * <p>
++ * See {@link #blockWriters()}, {@link #enableWriters()}, {@link #execAsWriter(Runnable)}
++ * <h3>Excluisve mode</h3>
++ * Exclusive mode is when the current thread is the only active code : no readers, no writers.
++ * <p>
++ * See {@link #startExclusiveMode()}/{@link #tryExclusiveMode()} {@link #finishExclusiveMode()}, {@link #execExclusive(Runnable)}
++ *
++ * @see Transaction
++ * @see TransactionalComponent
++ * @see TransactionalSystem
++ */
++final
++public class TransactionCoordinator {
++    private static Logger log = Sys.syslog ;
++    
++    private final Journal journal ;
++    private boolean coordinatorStarted = false ;
++
++    private final ComponentGroup components = new ComponentGroup() ;
++    // Components 
++    private ComponentGroup txnComponents = null ;
++    private List<ShutdownHook> shutdownHooks ;
++    private TxnIdGenerator txnIdGenerator = TxnIdFactory.txnIdGenSimple ;
++    
++    private QuorumGenerator quorumGenerator = null ;
++    //private QuorumGenerator quorumGenerator = (m) -> components ;
++
++    // Semaphore to implement "Single Active Writer" - independent of readers
++    // This is not reentrant.
++    private Semaphore writersWaiting = new Semaphore(1, true) ;
++    
++    // All transaction need a "read" lock through out their lifetime. 
++    // Do not confuse with read/write transactions.  We need a 
++    // "one exclusive, or many other" lock which happens to be called ReadWriteLock
++    // See also {@code lock} which protects the datastructures during transaction management.  
++    private ReadWriteLock exclusivitylock = new ReentrantReadWriteLock() ;
++
++    // Coordinator wide lock object.
++    private Object coordinatorLock = new Object() ;
++
++    @FunctionalInterface
++    public interface ShutdownHook { void shutdown() ; }
++
++    /** Create a TransactionCoordinator, initially with no associated {@link TransactionalComponent}s */ 
++    public TransactionCoordinator(Location location) {
++        this(Journal.create(location)) ;
++    }
++    
++    /** Create a TransactionCoordinator, initially with no associated {@link TransactionalComponent}s */ 
++    public TransactionCoordinator(Journal journal) {
++        this(journal, null , new ArrayList<>()) ;
++    }
++
++    /** Create a TransactionCoordinator, initially with {@link TransactionalComponent} in the ComponentGroup */
++    public TransactionCoordinator(Journal journal, List<TransactionalComponent> components) {
++        this(journal, components , new ArrayList<>()) ;
++    }
++
++    //    /** Create a TransactionCoordinator, initially with no associated {@link TransactionalComponent}s */ 
++//    public TransactionCoordinator(Location journalLocation) {
++//        this(Journal.create(journalLocation), new ArrayList<>() , new ArrayList<>()) ;
++//    }
++
++    private TransactionCoordinator(Journal journal, List<TransactionalComponent> txnComp, List<ShutdownHook> shutdownHooks) { 
++        this.journal = journal ;
++        this.shutdownHooks = new ArrayList<>(shutdownHooks) ;
++        if ( txnComp != null ) {
++            //txnComp.forEach(x-> System.out.println(x.getComponentId().label()+" :: "+Bytes.asHex(x.getComponentId().bytes()) ) ) ;
++            txnComp.forEach(components::add);
++        }
++    }
++    
++    /** Add a {@link TransactionalComponent}.
++     * Safe to call at any time but it is good practice is to add all the
++     * compoents before any transactions start.
++     * Internally, the coordinator ensures the add will safely happen but it
++     * does not add the component to existing transactions.
++     * This must be setup before recovery is attempted. 
++     */
++    public TransactionCoordinator add(TransactionalComponent elt) {
++        checkSetup() ;
++        synchronized(coordinatorLock) {
++            components.add(elt) ;
++        }
++        return this ;
++    }
++
++    /** 
++     * Remove a {@link TransactionalComponent}.
++     * @see #add 
++     */
++    public TransactionCoordinator remove(TransactionalComponent elt) {
++        checkSetup() ;
++        synchronized(coordinatorLock) {
++            components.remove(elt.getComponentId()) ;
++        }
++        return this ;
++    }
++
++    /**
++     * Add a shutdown hook. Shutdown is not guaranteed to be called
++     * and hence hooks may not get called.
++     */
++    public void add(TransactionCoordinator.ShutdownHook hook) {
++        checkSetup() ;
++        synchronized(coordinatorLock) {
++            shutdownHooks.add(hook) ;
++        }
++    }
++
++    /** Remove a shutdown hook */
++    public void remove(TransactionCoordinator.ShutdownHook hook) {
++        checkSetup() ;
++        synchronized(coordinatorLock) {
++            shutdownHooks.remove(hook) ;
++        }
++    }
++    
++    public void setQuorumGenerator(QuorumGenerator qGen) {
++        checkSetup() ;
++        this.quorumGenerator = qGen ;
++    }
++
++    public void start() {
++        checkSetup() ;
++        recovery() ;
++        coordinatorStarted = true ;
++    }
++
++    private /*public*/ void recovery() {
++        
++        Iterator<JournalEntry> iter = journal.entries() ;
++        if ( ! iter.hasNext() ) {
++            components.forEachComponent(c -> c.cleanStart()) ;
++            return ;
++        }
++        
++        log.info("Journal recovery start") ;
++        components.forEachComponent(c -> c.startRecovery()) ;
++        
++        // Group to commit
++        
++        List<JournalEntry> entries = new ArrayList<>() ;
++        
++        iter.forEachRemaining( entry -> {
++            switch(entry.getType()) {
++                case ABORT :
++                    entries.clear() ;
++                    break ;
++                case COMMIT :
++                    recover(entries) ;
++                    entries.clear() ;
++                    break ;
++                case REDO : case UNDO :
++                    entries.add(entry) ;
++                    break ;
++            }
++        }) ;
++    
++        components.forEachComponent(c -> c.finishRecovery()) ;
++        journal.reset() ;
++        log.info("Journal recovery end") ;
++    }
++
++    private void recover(List<JournalEntry> entries) {
++        entries.forEach(e -> {
++            if ( e.getType() == UNDO ) {
++                Log.warn(TransactionCoordinator.this, "UNDO entry : not handled") ;  
++                return ;
++            }
++            ComponentId cid = e.getComponentId() ;
++            ByteBuffer bb = e.getByteBuffer() ;
++            // find component.
++            TransactionalComponent c = components.findComponent(cid) ;
++            if ( c == null ) {
++                Log.warn(TransactionCoordinator.this, "No component for "+cid) ;
++                return ;
++            }
++            c.recover(bb); 
++        }) ;
++    }
++
++    public void setTxnIdGenerator(TxnIdGenerator generator) {
++        this.txnIdGenerator = generator ;
++    }
++    
++    public Journal getJournal() {
++        return journal ;
++    }
++    
++    public TransactionCoordinatorState detach(Transaction txn) {
++        txn.detach();
++        TransactionCoordinatorState coordinatorState = new TransactionCoordinatorState(txn) ;
++        components.forEach((id, c) -> {
++            SysTransState s = c.detach() ;
++            coordinatorState.componentStates.put(id, s) ;
++        } ) ;
++        // The txn still counts as "active" for tracking purposes below.
++        return coordinatorState ;
++    }
++
++    public void attach(TransactionCoordinatorState coordinatorState) {
++        Transaction txn = coordinatorState.transaction ;
++        txn.attach() ;
++        coordinatorState.componentStates.forEach((id, obj) -> {
++            components.findComponent(id).attach(obj);
++        });
++    }
++
++    public void shutdown() {
++        if ( coordinatorLock == null )
++            return ;
++        components.forEach((id, c) -> c.shutdown()) ;
++        shutdownHooks.forEach((h)-> h.shutdown()) ;
++        coordinatorLock = null ;
++        journal.close(); 
++    }
++
++    // Are we in the initialization phase?
++    private void checkSetup() {
++        if ( coordinatorStarted )
++            throw new TransactionException("TransactionCoordinator has already been started") ;
++    }
++
++    // Are we up and ruuning?
++    private void checkActive() {
++        if ( ! coordinatorStarted )
++            throw new TransactionException("TransactionCoordinator has not been started") ;
++        checkNotShutdown();
++    }
++
++    // Check not wrapped up
++    private void checkNotShutdown() {
++        if ( coordinatorLock == null )
++            throw new TransactionException("TransactionCoordinator has been shutdown") ;
++    }
++
++    private void releaseWriterLock() {
++        int x = writersWaiting.availablePermits() ;
++        if ( x != 0 )
++            throw new TransactionException("TransactionCoordinator: Probably mismatch of enable/disableWriter calls") ;
++        writersWaiting.release() ;
++    }
++    
++    /** Acquire the writer lock - return true if succeeded */
++    private boolean acquireWriterLock(boolean canBlock) {
++        if ( ! canBlock )
++            return writersWaiting.tryAcquire() ;
++        try { 
++            writersWaiting.acquire() ; 
++            return true;
++        } catch (InterruptedException e) { throw new TransactionException(e) ; }
++    }
++    
++    /** Enter exclusive mode; block if necessary.
++     * There are no active transactions on return; new transactions will be held up in 'begin'.
++     * Return to normal (release waiting transactions, allow new transactions)
++     * with {@link #finishExclusiveMode}.
++     * <p>
++     * Do not call inside an existing transaction.
++     */
++    public void startExclusiveMode() {
++        startExclusiveMode(true);
++    }
++    
++    /** Try to enter exclusive mode. 
++     *  If return is true, then there are no active transactions on return and new transactions will be held up in 'begin'.
++     *  If false, there were in-progress transactions.
++     *  Return to normal (release waiting transactions, allow new transactions)
++     *  with {@link #finishExclusiveMode}.   
++     * <p>
++     * Do not call inside an existing transaction.
++     */
++    public boolean tryExclusiveMode() {
++        return tryExclusiveMode(false);
++    }
++    
++    /** Try to enter exclusive mode.  
++     *  If return is true, then there are no active transactions on return and new transactions will be held up in 'begin'.
++     *  If false, there were in-progress transactions.
++     *  Return to normal (release waiting transactions, allow new transactions)
++     *  with {@link #finishExclusiveMode}.   
++     * <p>
++     * Do not call inside an existing transaction.
++     * @param canBlock Allow the operation block and wait for the exclusive mode lock.
++     */
++    public boolean tryExclusiveMode(boolean canBlock) {
++        return startExclusiveMode(canBlock);
++    }
++
++    private boolean startExclusiveMode(boolean canBlock) {
++        if ( canBlock ) {
++            exclusivitylock.writeLock().lock() ;
++            return true ;
++        }
++        return exclusivitylock.writeLock().tryLock() ;
++    }
++
++    /** Return to normal (release waiting transactions, allow new transactions).
++     * Must be paired with an earlier {@link #startExclusiveMode}. 
++     */
++    public void finishExclusiveMode() {
++        exclusivitylock.writeLock().unlock() ;
++    }
++
++    /** Execute an action in exclusive mode.  This method can block.
++     * Equivalent to:
++     * <pre>
++     *  startExclusiveMode() ;
++     *  try { action.run(); }
++     *  finally { finishExclusiveMode(); }
++     * </pre>
++     * 
++     * @param action
++     */
++    public void execExclusive(Runnable action) {
++        startExclusiveMode() ;
++        try { action.run(); }
++        finally { finishExclusiveMode(); }
++    }
++    
++    /** Block until no writers are active.
++     *  When this returns, this guarantees that the database is not changing
++     *  and the journal is flushed to disk.
++     * <p> 
++     * The application must call {@link #enableWriters} later.
++     * <p> 
++     * This operation must not be nested (it will block).
++     * 
++     * @see #tryBlockWriters()
++     * @see #enableWriters()
++     * 
++     */
++    public void blockWriters() {
++        acquireWriterLock(true) ;
++    }
++
++    /** Try to block all writers, or return if can't at the moment.
++     * <p>
++     * Unlike a write transction, there is no associated transaction. 
++     * <p>
++     * If it returns true, the application must call {@link #enableWriters} later.
++     *  
++     * @see #blockWriters()
++     * @see #enableWriters()
++
++     * @return true if the operation succeeded and writers are blocked 
++     */
++    public boolean tryBlockWriters() {
++        return tryBlockWriters(false) ;
++    }
++
++    /**
++     * Block until no writers are active, optionally blocking or returning if can't at the moment.
++     * <p>
++     * Unlike a write transction, there is no associated transaction. 
++     * <p>
++     * If it returns true, the application must call {@link #enableWriters} later.
++     * @param canBlock
++     * @return true if the operation succeeded and writers are blocked
++     */
++    public boolean tryBlockWriters(boolean canBlock) {
++        return acquireWriterLock(canBlock) ;
++    }
++    /** Allow writers.  
++     * This must be used in conjunction with {@link #blockWriters()} or {@link #tryBlockWriters()}
++     * 
++     * @see #blockWriters()
++     * @see #tryBlockWriters()
++     */ 
++    public void enableWriters() {
++        releaseWriterLock();
++    }
++    
++    /** Execute an action in as if a Write but no write transaction started.
++     * This method can block.
++     * <p>
++     * Equivalent to:
++     * <pre>
++     *  blockWriters() ;
++     *  try { action.run(); }
++     *  finally { enableWriters(); }
++     * </pre>
++     * 
++     * @param action
++     */
++    public void execAsWriter(Runnable action) {
++        blockWriters() ;
++        try { action.run(); }
++        finally { enableWriters(); }
++    }
++    
++    /** Start a transaction. This may block. */
++    public Transaction begin(ReadWrite readWrite) {
++        return begin(readWrite, true) ;
++    }
++    
++    /** 
++     * Start a transaction.  Returns null if this operation would block.
++     * Readers can start at any time.
++     * A single writer policy is currently imposed so a "begin(WRITE)"
++     * may block.  
++     */
++    public Transaction begin(ReadWrite readWrite, boolean canBlock) {
++        Objects.nonNull(readWrite) ;
++        checkActive() ;
++        
++        // XXX Flag to bounce writers fpor long term "block writers"
++        if ( false /* bounceWritersAtTheMoment */) {
++            if ( readWrite == WRITE ) {
++                throw new TransactionException("Writers currently being rejected");
++            }
++        }
++        
++        if ( canBlock )
++            exclusivitylock.readLock().lock() ;
++        else {
++            if ( ! exclusivitylock.readLock().tryLock() )
++                return null ;
++        }
++        
++        // Readers never block.
++        if ( readWrite == 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 notifyCommitFinish/notifyAbortFinish
++            boolean b = acquireWriterLock(canBlock) ;
++            if ( !b ) {
++                exclusivitylock.readLock().unlock() ;
++                return null ;
++            }
++        }
++        Transaction transaction = begin$(readWrite) ;
++        startActiveTransaction(transaction) ;
++        transaction.begin();
++        return transaction;
++    }
++    
++    // The version is the serialization point for a transaction.
++    // All transactions on the same view of the data get the same serialization point.
++    
++    // A read transaction can be promoted if writer does not start
++    // This TransactionCoordinator provides Serializable, Read-lock-free
++    // execution.  With no item locking, a read can only be promoted
++    // if no writer started since the reader started.
++
++    /* The version of the data - incremented when transaction commits.
++     * This is the version with repest to the last commited transaction.
++     * Aborts do not cause the data version to advance. 
++     * This counterr never goes backwards.
++     */ 
++    private final AtomicLong dataVersion = new AtomicLong(0) ;
++    
++    private Transaction begin$(ReadWrite readWrite) {
++        synchronized(coordinatorLock) {
++            // Thread safe part of 'begin'
++            // Allocate the transaction serialization point.
++            TxnId txnId = txnIdGenerator.generate() ;
++            List<SysTrans> sysTransList = new ArrayList<>() ;
++            Transaction transaction = new Transaction(this, txnId, readWrite, dataVersion.get(), sysTransList) ;
++            
++            ComponentGroup txnComponents = chooseComponents(this.components, readWrite) ;
++            
++            try {
++                txnComponents.forEachComponent(elt -> {
++                    SysTrans sysTrans = new SysTrans(elt, transaction, txnId) ;
++                    sysTransList.add(sysTrans) ; }) ;
++                // Calling each component must be inside the lock
++                // so that a transaction does not commit overlapping with setup.
++                // If it did, different components might end up starting from
++                // different start states of the overall system.
++                txnComponents.forEachComponent(elt -> elt.begin(transaction)) ;
++            } catch(Throwable ex) {
++                // Careful about incomplete.
++                //abort() ;
++                //complete() ;
++                throw ex ;
++            }
++            return transaction ;
++        }
++    }
++    
++    private ComponentGroup chooseComponents(ComponentGroup components, ReadWrite readWrite) {
++        if ( quorumGenerator == null )
++            return components ;
++        ComponentGroup cg = quorumGenerator.genQuorum(readWrite) ;
++        if ( cg == null )
++            return components ;
++        cg.forEach((id, c) -> {
++            TransactionalComponent tcx = components.findComponent(id) ;
++            if ( ! tcx.equals(c) )
++                log.warn("TransactionalComponent not in TransactionCoordinator's ComponentGroup") ; 
++        }) ;
++        if ( log.isDebugEnabled() )
++            log.debug("Custom ComponentGroup for transaction "+readWrite+": size="+cg.size()+" of "+components.size()) ;
++        return cg ;
++    }
++
++    /** Is promotion of transactions enabled? */ 
++    /*private*/public/*for development*/ static boolean promotion               = true ;
++    
++    /** Control of whether a transaction promotion can see any commits that
++     *  happened between this transaction starting and it promoting.
++     *  A form of "ReadCommitted".   
++     */
++    /*private*/public/*for development*/ static boolean readCommittedPromotion  = false ;
++    
++    /** Whether to wait for writers when trying to promote */
++    private static final boolean promotionWaitForWriters = true;
++
++    /** Attempt to promote a tranasaction from READ to WRITE.
++     * No-op for a transaction already a writer.
++     * Throws {@link TransactionException} if the promotion
++     * can not be done.
++     */
++    /*package*/ boolean promoteTxn(Transaction transaction) {
++        if ( ! promotion )
++            return false;
++
++        if ( transaction.getMode() == WRITE )
++            return true ;
++        
++        // Has there been an writer active since the transaction started?
++        // Do a test outside the lock - only dataVaersion can change and that increases.
++        // If "read commited transactions" not allowed, the data has changed in a way we
++        // do no twish to expose.
++        // If this test fails outside the lock it will fail inside.
++        // If it passes, we have to test again in case there is an active writer.
++        
++        if ( ! readCommittedPromotion ) {
++            long txnEpoch = transaction.getDataVersion() ;      // The transaction-start point.
++            long currentEpoch = dataVersion.get() ;             // The data serialization point.
++            
++            if ( txnEpoch < currentEpoch )
++                // The data has changed and "read committed" not allowed.
++                // We can reject now.
++                return false ;
++        }
++        
++        // Once we have acquireWriterLock, we are single writer.
++        // We may have to discard writer status because eocne we can make the defintite
++        // decision on promotion, we find we can't promote after all.
++        if ( readCommittedPromotion ) {
++            /*
++             * acquireWriterLock(true) ;
++             * synchronized(coordinatorLock) {
++             * begin$ ==>
++             *    reset transaction.
++             *    promote components
++             *    reset dataVersion
++             */
++            acquireWriterLock(true) ;
++            synchronized(coordinatorLock) {
++                try { 
++                    transaction.promoteComponents() ;
++                    // Because we want to see the new state of the data.s
++                    //transaction.resetDataVersion(dataVersion.get());
++                } catch (TransactionException ex) {
++                    try { transaction.abort(); } catch(RuntimeException ex2) {}
++                    releaseWriterLock();
++                    return false ;
++                }
++            }
++            return true;
++        }
++        
++        if ( ! waitForWriters() )
++            // Failed to become a writer.
++            return false;
++        // Now a proto-writer.
++        
++        synchronized(coordinatorLock) {
++            // Not read commited.
++            // Need to check the data version once we are the writer and all previous
++            // writers have commited or aborted.
++            // Has there been an writer active since the transaction started?
++            long txnEpoch = transaction.getDataVersion() ;    // The transaction-start point.
++            long currentEpoch = dataVersion.get() ;         // The data serialization point.
++
++            if ( txnEpoch != currentEpoch ) {
++                // Failed to promote.
++                releaseWriterLock();
++                return false ;
++            }
++            
++            // ... we have now got the writer lock ...
++            try { 
++                transaction.promoteComponents() ;
++                // No need to reset the data version because strict isolation. 
++            } catch (TransactionException ex) {
++                try { transaction.abort(); } catch(RuntimeException ex2) {}
++                releaseWriterLock();
++                return false ;
++            }
++        }
++        return true ;
++    }
++        
++    private boolean waitForWriters() {
++        if ( promotionWaitForWriters )
++            return acquireWriterLock(true) ;
++        else
++            return acquireWriterLock(false) ;
++    }
++
++    // Called once by Transaction after the action of commit()/abort() or end()
++    /** Signal that the transaction has finished. */  
++    /*package*/ void completed(Transaction transaction) {
++        finishActiveTransaction(transaction);
++        journal.reset() ;
++    }
++
++    /*package*/ void executePrepare(Transaction transaction) {
++        // Do here because it needs access to the journal.
++        notifyPrepareStart(transaction);
++        transaction.getComponents().forEach(sysTrans -> {
++            TransactionalComponent c = sysTrans.getComponent() ;
++            ByteBuffer data = c.commitPrepare(transaction) ;
++            if ( data != null ) {
++                PrepareState s = new PrepareState(c.getComponentId(), data) ;
++                journal.write(s) ;
++            }
++        }) ;
++        notifyPrepareFinish(transaction);
++    }
++
++    /*package*/ void executeCommit(Transaction transaction,  Runnable commit, Runnable finish) {
++        // This is the commit point. 
++        synchronized(coordinatorLock) {
++            // *** COMMIT POINT
++            journal.sync() ;
++            // *** COMMIT POINT
++            // Now run the Transactions commit actions. 
++            commit.run() ;
++            journal.truncate(0) ;
++            // and tell the Transaction it's finished. 
++            finish.run() ;
++            // Bump global serialization point if necessary.
++            if ( transaction.getMode() == WRITE )
++                advanceDataVersion() ;
++            notifyCommitFinish(transaction) ;
++        }
++    }
++    
++    // Inside the global transaction start/commit lock.
++    private void advanceDataVersion() {
++        dataVersion.incrementAndGet();
++    }
++    
++    /*package*/ void executeAbort(Transaction transaction, Runnable abort) {
++        notifyAbortStart(transaction) ;
++        abort.run();
++        notifyAbortFinish(transaction) ;
++    }
++    
++    // Active transactions: this is (the missing) ConcurrentHashSet
++    private final static Object dummy                   = new Object() ;    
++    private ConcurrentHashMap<Transaction, Object> activeTransactions = new ConcurrentHashMap<>() ;
++    private AtomicLong activeTransactionCount = new AtomicLong(0) ;
++    private AtomicLong activeReadersCount = new AtomicLong(0) ;
++    private AtomicLong activeWritersCount = new AtomicLong(0) ;
++    
++    private void startActiveTransaction(Transaction transaction) {
++        synchronized(coordinatorLock) {
++            // Use lock to ensure all the counters move together.
++            // Thread safe - we have not let the Transaction object out yet.
++            countBegin.incrementAndGet() ;
++            switch(transaction.getMode()) {
++                case READ:  countBeginRead.incrementAndGet() ;  activeReadersCount.incrementAndGet() ; break ;
++                case WRITE: countBeginWrite.incrementAndGet() ; activeWritersCount.incrementAndGet() ; break ;
++            }
++            activeTransactionCount.incrementAndGet() ;
++            activeTransactions.put(transaction, dummy) ;
++        }
++    }
++    
++    private void finishActiveTransaction(Transaction transaction) {
++        synchronized(coordinatorLock) {
++            // Idempotent.
++            Object x = activeTransactions.remove(transaction) ;
++            if ( x == null )
++                return ;
++            countFinished.incrementAndGet() ;
++            activeTransactionCount.decrementAndGet() ;
++            switch(transaction.getMode()) {
++                case READ:  activeReadersCount.decrementAndGet() ; break ;
++                case WRITE: activeWritersCount.decrementAndGet() ; break ;
++            }
++        }
++        exclusivitylock.readLock().unlock() ; 
++    }
++    
++    public long countActiveReaders()    { return activeReadersCount.get() ; } 
++    public long countActiveWriter()     { return activeWritersCount.get() ; } 
++    public long countActive()           { return activeTransactionCount.get(); }
++    
++    // notify*Start/Finish called round each transaction lifecycle step
++    // Called in cooperation between Transaction and TransactionCoordinator
++    // depending on who is actually do the work of each step.
++
++    /*package*/ void notifyPrepareStart(Transaction transaction) {}
++
++    /*package*/ void notifyPrepareFinish(Transaction transaction) {}
++
++    // Writers released here - can happen because of commit() or abort(). 
++
++    private void notifyCommitStart(Transaction transaction) {}
++    
++    private void notifyCommitFinish(Transaction transaction) {
++        if ( transaction.getMode() == WRITE )
++            releaseWriterLock();
++    }
++    
++    private void notifyAbortStart(Transaction transaction) { }
++    
++    private void notifyAbortFinish(Transaction transaction) {
++        if ( transaction.getMode() == WRITE )
++            releaseWriterLock();
++    }
++
++    /*package*/ void notifyEndStart(Transaction transaction) { }
++
++    /*package*/ void notifyEndFinish(Transaction transaction) {}
++
++    // Called by Transaction once at the end of first commit()/abort() or end()
++    
++    /*package*/ void notifyCompleteStart(Transaction transaction) { }
++
++    /*package*/ void notifyCompleteFinish(Transaction transaction) { }
++
++    // Coordinator state.
++    private final AtomicLong countBegin         = new AtomicLong(0) ;
++
++    private final AtomicLong countBeginRead     = new AtomicLong(0) ;
++
++    private final AtomicLong countBeginWrite    = new AtomicLong(0) ;
++
++    private final AtomicLong countFinished      = new AtomicLong(0) ;
++
++    // Access counters
++    public long countBegin()        { return countBegin.get() ; }
++
++    public long countBeginRead()    { return countBeginRead.get() ; }
++
++    public long countBeginWrite()   { return countBeginWrite.get() ; }
++
++    public long countFinished()     { return countFinished.get() ; }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionCoordinatorState.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionCoordinatorState.java
index 0000000,0000000..def1f6e
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionCoordinatorState.java
@@@ -1,0 -1,0 +1,33 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction.txn;
++
++import java.util.HashMap ;
++import java.util.Map ;
++
++public class TransactionCoordinatorState {
++    /*package*/final Transaction transaction ;
++    /*package*/Map<ComponentId, SysTransState> componentStates = new HashMap<>();
++    /*package*/ TransactionCoordinatorState(Transaction transaction) {
++        this.transaction = transaction ;
++    }
++    
++    public Transaction getTransaction() {
++        return transaction ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionException.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionException.java
index 0000000,0000000..8e45811
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionException.java
@@@ -1,0 -1,0 +1,29 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction.txn;
++
++import org.apache.jena.sparql.JenaTransactionException ;
++
++public class TransactionException extends JenaTransactionException {
++    public TransactionException()                                  { super(); }
++    public TransactionException(String message)                    { super(message); }
++    public TransactionException(Throwable cause)                   { super(cause) ; }
++    public TransactionException(String message, Throwable cause)   { super(message, cause) ; }
++
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionInfo.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionInfo.java
index 0000000,0000000..34a39f2
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionInfo.java
@@@ -1,0 -1,0 +1,75 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction.txn;
++
++import static org.seaborne.dboe.transaction.txn.TxnState.INACTIVE ;
++import org.apache.jena.query.ReadWrite ;
++
++/** 
++ * A view that provides information about a transaction
++ * @see Transaction
++ */
++public interface TransactionInfo {
++
++    /** The transaction lifecycle state */
++    public TxnState getState() ;
++
++    /**
++     * Each transaction is allocated a serialization point by the transaction
++     * coordinator. Normally, this is related to this number and it increases
++     * over time as the data changes. Two readers can have the same
++     * serialization point - they are working with the same view of the data.
++     */
++    public long getDataVersion() ;
++
++    /** Has the transaction started? */ 
++    public boolean hasStarted() ;
++    
++    /** Has the transaction finished (has commit/abort/end been called)? */ 
++    public boolean hasFinished() ; 
++
++    /** Has the transaction gone through all lifecycle states? */ 
++    public boolean hasFinalised() ; 
++
++    /** Get the trasnaction id for this transaction. Unique within this OS process (JVM) at least . */
++    public TxnId getTxnId() ; 
++
++    /** What mode is this transaction?
++     *  This may change from {@code READ} to {@code WRITE} in a transactions lifetime.  
++     */
++    public ReadWrite getMode() ;
++    
++    /** Is this a view of a READ transaction?
++     * Convenience operation equivalent to {@code (getMode() == READ)}
++     */
++    public default boolean isReadTxn()  { return getMode() == ReadWrite.READ ; }
++    
++    /** Is this a view of a WRITE transaction?
++     * Convenience operation equivalent to {@code (getMode() == WRITE)}
++     */
++    public default boolean isWriteTxn()  { return getMode() == ReadWrite.WRITE ; }
++    
++    /** Is this a view of a transaction that is active?
++     * Equivalent to {@code getState() != INACTIVE} 
++     */
++    public default boolean isActiveTxn() { 
++        return getState() != INACTIVE ;
++    }
++
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionalBase.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionalBase.java
index 0000000,0000000..13b6683
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionalBase.java
@@@ -1,0 -1,0 +1,249 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction.txn;
++
++import java.util.Objects ;
++
++import org.apache.jena.atlas.logging.Log ;
++import org.apache.jena.query.ReadWrite ;
++
++/**
++ * Framework for implementing a Transactional.
++ */
++
++public class TransactionalBase implements TransactionalSystem {
++    // Help debugging by generating names for Transactionals.
++    private final String label ; 
++    protected boolean isShutdown = false ; 
++    protected final TransactionCoordinator txnMgr ;
++    
++    // Per thread transaction.
++    private final ThreadLocal<Transaction> theTxn = new ThreadLocal<>() ;
++    
++    public TransactionalBase(String label, TransactionCoordinator txnMgr) {
++        this.label = label ;
++        this.txnMgr = txnMgr ;
++    }
++    
++    public TransactionalBase(TransactionCoordinator txnMgr) {
++        this(null, txnMgr) ;
++    }
++
++    @Override
++    public TransactionCoordinator getTxnMgr() {
++        return txnMgr ;
++    }
++
++    // Development
++    private static final boolean trackAttachDetach = false ;
++    
++    @Override
++    public TransactionCoordinatorState detach() {
++        if ( trackAttachDetach )
++            Log.info(this,  ">> detach");
++        checkRunning() ;
++        // Not if it just commited but before end.
++        //checkActive() ;
++        Transaction txn = theTxn.get() ;
++        TransactionCoordinatorState coordinatorState = null ;
++        if ( txn != null )
++            // We are not ending.
++            coordinatorState = txnMgr.detach(txn) ;
++        if ( trackAttachDetach )
++            Log.info(this,  "  theTxn = "+txn) ;
++        theTxn.remove() ; ///??????
++        if ( trackAttachDetach )
++            Log.info(this,  "<< detach");
++        if ( coordinatorState == null )
++            throw new TransactionException("Not attached") ;
++        return coordinatorState ;
++    }
++    
++    @Override
++    public void attach(TransactionCoordinatorState coordinatorState) {
++        if ( trackAttachDetach )
++            Log.info(this,  ">> attach");
++        Objects.nonNull(coordinatorState) ;
++        checkRunning() ;
++        checkNotActive() ;
++        TxnState txnState = coordinatorState.transaction.getState() ;
++        if ( txnState != TxnState.DETACHED )
++            throw new TransactionException("Not a detached transaction") ;
++        txnMgr.attach(coordinatorState) ;
++        if ( trackAttachDetach )
++            Log.info(this,  "  theTxn = "+coordinatorState.transaction) ;
++        theTxn.set(coordinatorState.transaction);
++        if ( trackAttachDetach )
++            Log.info(this,  "<< attach");
++    } 
++    
++    @Override
++    public final void begin(ReadWrite readWrite) {
++        Objects.nonNull(readWrite) ;
++        checkRunning() ;
++        checkNotActive() ;
++        Transaction transaction = txnMgr.begin(readWrite) ;
++        theTxn.set(transaction) ;
++    }
++    
++    @Override
++    public boolean promote() {
++        checkActive() ;
++        Transaction txn = getValidTransaction() ;
++        return txn.promote() ;
++    }
++
++    @Override
++    public final void commit() {
++        checkRunning() ;
++        TransactionalSystem.super.commit() ;
++    }
++
++    @Override
++    public void commitPrepare() {
++        Transaction txn = getValidTransaction() ;
++        txn.prepare() ;
++    }
++
++    @Override
++    public void commitExec() {
++        Transaction txn = getValidTransaction() ;
++        txn.commit() ;
++        _end() ;
++    }
++
++//    /** Signal end of commit phase */
++//    @Override
++//    public void commitEnd() {
++//        _end() ;
++//    }
++    
++    @Override
++    public final void abort() {
++        checkRunning() ;
++        checkActive() ;
++        Transaction txn = getValidTransaction() ;
++        try { txn.abort() ; }
++        finally { _end() ; }
++    }
++
++    @Override
++    public final void end() {
++        checkRunning() ;
++        // Don't check if active or if any thread locals exist
++        // because this may have already been called.
++        // txn.get() ; -- may be null -- test repeat calls.
++        _end() ;
++    }
++
++    /**
++     * Return the Read/write state (or null when not in a transaction)
++     */
++    @Override
++    final
++    public ReadWrite getState() {
++        checkRunning() ;
++        // tricky - touching theTxn causes it to initialize.
++        Transaction txn = theTxn.get() ;
++        if ( txn != null )
++            return txn.getMode() ;
++        theTxn.remove() ;
++        return null ; 
++    }
++    
++    @Override
++    final
++    public TransactionInfo getTransactionInfo() {
++        return getThreadTransaction() ;
++    }
++    
++    @Override
++    final
++    public Transaction getThreadTransaction() {
++        Transaction txn = theTxn.get() ;
++        // Touched the thread local so it is defined now.
++//        if ( txn == null )
++//            theTxn.remove() ;
++        return txn ;
++    }
++
++    /** Get the transaction, checking there is one */  
++    private Transaction getValidTransaction() {
++        Transaction txn = theTxn.get() ;
++        if ( txn == null )
++            throw new TransactionException("Not in a transaction") ;
++        return txn ;
++    }
++
++    private void checkRunning() {
++//        if ( ! hasStarted )
++//            throw new TransactionException("Not started") ;
++        
++        if ( isShutdown )
++            throw new TransactionException("Shutdown") ;
++    }
++    
++    /**
++     * Shutdown component, aborting any in-progress transactions. This operation
++     * is not guaranteed to be called.
++     */
++    public void shutdown() {
++        txnMgr.shutdown() ;
++        isShutdown = true ;
++    }
++
++    protected String label(String msg) {
++        if ( label == null )
++            return msg ;
++        return label+": "+msg ;
++    }
++    
++    final
++    protected void checkActive() {
++        checkNotShutdown() ;
++        if ( ! isInTransaction() )
++            throw new TransactionException(label("Not in an active transaction")) ;
++    }
++
++    final
++    protected void checkNotActive() {
++        checkNotShutdown() ;
++        if ( isInTransaction() )
++            throw new TransactionException(label("Currently in an active transaction")) ;
++    }
++
++    final
++    protected void checkNotShutdown() {
++        if ( isShutdown )
++            throw new TransactionException(label("Already shutdown")) ;
++    }
++
++    private final void _end() {
++        Transaction txn = theTxn.get() ;
++        if ( txn != null ) {
++            try {
++                // Can throw an exception on begin(W)...end().
++                txn.end() ;
++            } finally {
++                theTxn.set(null) ;
++                theTxn.remove() ;
++            }
++        }
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionalComponent.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionalComponent.java
index 0000000,0000000..dce526b
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionalComponent.java
@@@ -1,0 -1,0 +1,175 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction.txn;
++
++import java.nio.ByteBuffer ;
++
++/** Interface that for components of a transaction system.
++* <p><br/>
++* The {@link TransactionCoordinator} manages a number of components
++* which provide the {@link TransactionalComponent} interface.
++* <p><br/>
++* When a new coordinator starts, typically being when the in-process system starts,
++* there is a recovery phase when work from a previous coordinator is recovered.
++* Transactions were either were properly committed by the previous coordinator,
++* and hence redo actions (finalization) should be done,
++* or they were not, in which case undo actions may be needed.
++* Transctions to discard are not notified, only fully commited transaction are
++* notified during recovery. The component may need to keepit's own record of
++* undo actions needed across restarts.
++* <p><br/>
++* Lifecycle of startup:
++* <ul>
++* <li>{@link #startRecovery}
++* <li>{@link #recover} for each commited/durable transaction (redo actions)
++* <li>{@link #finishRecovery}, discarding any othe transactions (undo actions).
++* </ul>
++* <p><br/>
++* Lifecycle of a read transaction:
++* <ul>
++* <li>{@link #begin}
++* <li>{@link #complete}
++* </ul>
++* <br/>
++* A read transaction may also include {@code commit} or {@code abort} lifecycles.
++* {@link #commitPrepare} and {@link #commitEnd} are not called.
++*<p><br/>
++* Lifecycle of a write transaction:
++* <li>{@link #begin}
++* <li>{@link #commitPrepare}
++* <li>{@link #commit} or {@link #abort}
++* <li>{@link #commitEnd}
++* <li>{@link #complete} including abort
++* </ul>
++* <br/>
++* or if the application aborts the transaction:
++* <ul>
++* <li>{@link #begin}
++* <li>{@link #abort}
++* <li>{@link #complete}
++* </ul>
++* <p>
++* {@link #complete} may be called out of sequence and it forces an abort if before 
++* {@link #commitPrepare}. Once {@link #commitPrepare} has been called, the component
++* can not decide whether to commit finally or to cause a system abort; it must wait 
++* for the coordinator. After {@link #commitEnd}, the coordinator has definitely 
++* commited the overall transaction and local prepared state can be released, and changes
++* made to the permanent state of the component.
++*
++* @see Transaction
++* @see TransactionCoordinator
++*/
++
++public interface TransactionalComponent
++{
++    /**
++     * Every component <i>instance</i> must supplied a unique number.
++     * It is used to route journal entries to subsystems, including across restarts/recovery. 
++     * Uniqueness scope is within the same {@link TransactionCoordinator},
++     * and the same across restarts.  
++     * <p>
++     * If a component imposes the rule of one-per-{@link TransactionCoordinator},
++     * the same number can be used (if different from all other component type instances).
++     * <p>
++     * If a component can have multiple instances per {@link TransactionCoordinator},
++     * for example indexes, each must have a unique instance id. 
++     */
++    public ComponentId getComponentId() ;
++
++    // ---- Recovery phase
++    public void startRecovery() ;
++    
++    /** Notification that {@code ref} was really committed and is being recovered.
++     *  
++     * @param ref Same bytes as were written during prepare originally.
++     */
++    public void recover(ByteBuffer ref) ;
++    
++    /** End of the receovery phase */
++    public void finishRecovery() ;
++
++    /** Indicate that no recovery is being done (the journal thinks everything was completed last time) */
++    public void cleanStart() ;
++
++    // ---- Normal operation
++    
++    /** Start a transaction; return an identifier for this components use. */ 
++    public void begin(Transaction transaction) ;
++    
++    /** Promote a component in a transaction.
++     * <p>
++     *  May return "false" for "can't do that" if the transaction can not be promoted.
++     *  <p>
++     *  May throw {@link UnsupportedOperationException} if promotion is not supported.
++     */
++    public boolean promote(Transaction transaction) ;
++
++    /** Prepare for a commit.
++     *  Returns some bytes that will be written to the journal.
++     *  The journal remains valid until {@link #commitEnd} is called.
++     */
++    public ByteBuffer commitPrepare(Transaction transaction) ;
++
++    /** Commit a transaction (make durable).
++     * Other components not have been commited yet and recovery may occur still.
++     * Permanent state should not be finalised until {@link #commitEnd}.
++     */
++    public void commit(Transaction transaction) ;
++    
++    /** Signal all commits on all components are done (the component can clearup now) */  
++    public void commitEnd(Transaction transaction) ;
++
++    /** Abort a transaction (undo the effect of a transaction) */   
++    public void abort(Transaction transaction) ;
++
++    /** Finalization - the coordinator will not mention the transaction again
++     *  although recovery after a crash may do so.
++     */
++    public void complete(Transaction transaction) ;
++    
++    // ---- End of operations
++    
++    /** Detach this component from the transaction of the current thread
++     * and return some internal state that can be used in a future call of 
++     * {@link #attach(SysTransState)}
++     * <p>
++     * After this call, the component is not in a transaction but the
++     * existing transaction still exists. The thread may start a new
++     * transaction; that transaction is completely independent of the
++     * detached transaction.
++     * <p>
++     * Returns {@code null} if the current thread not in a transaction.
++     * The component may return null to indicate it has no state. 
++     * The return system state should be used in a call to {@link #attach(SysTransState)}
++     * and the transaction ended in the usual way. 
++     *   
++     */
++    public SysTransState detach() ;
++    
++    /** Set the current thread to be in the transaction.  The {@code systemState}
++     * must be obtained from a call of {@link #detach()}.
++     * This method can only be called once per {@code systemState}.
++     */
++    public void attach(SysTransState systemState) ;
++    
++    /** Shutdown component, aborting any in-progress transactions.
++     * This operation is not guaranteed to be called.
++     */
++    public void shutdown() ;
++    
++}


[20/50] [abbrv] jena git commit: Reformat

Posted by an...@apache.org.
Reformat


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

Branch: refs/heads/jena-tdb2
Commit: 9b987b002235c87f58e9a419025679e66bb83d25
Parents: 4d02f57
Author: Andy Seaborne <an...@seaborne.org>
Authored: Mon Sep 4 23:14:04 2017 +0100
Committer: Andy Seaborne <an...@seaborne.org>
Committed: Mon Sep 4 23:14:04 2017 +0100

----------------------------------------------------------------------
 tdb2-cmds/src/main/java/tdb2/tdbcompact.java |  3 +++
 tdb2-cmds/src/main/java/tdb2/tdbdump.java    | 16 ++++++----------
 2 files changed, 9 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/9b987b00/tdb2-cmds/src/main/java/tdb2/tdbcompact.java
----------------------------------------------------------------------
diff --git a/tdb2-cmds/src/main/java/tdb2/tdbcompact.java b/tdb2-cmds/src/main/java/tdb2/tdbcompact.java
index 373e822..ea2e275 100644
--- a/tdb2-cmds/src/main/java/tdb2/tdbcompact.java
+++ b/tdb2-cmds/src/main/java/tdb2/tdbcompact.java
@@ -40,6 +40,9 @@ public class tdbcompact extends CmdTDB {
     @Override
     protected void exec() {
         DatasetGraphSwitchable dsg = getDatabaseContainer() ;
+        long start = System.currentTimeMillis();
         DatabaseOps.compact(dsg) ;
+        long finish = System.currentTimeMillis();
+        System.out.printf("Compacted in %.3fs", (finish-start)/1000.0);
     }
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/9b987b00/tdb2-cmds/src/main/java/tdb2/tdbdump.java
----------------------------------------------------------------------
diff --git a/tdb2-cmds/src/main/java/tdb2/tdbdump.java b/tdb2-cmds/src/main/java/tdb2/tdbdump.java
index f54220c..9c50e87 100644
--- a/tdb2-cmds/src/main/java/tdb2/tdbdump.java
+++ b/tdb2-cmds/src/main/java/tdb2/tdbdump.java
@@ -31,27 +31,23 @@ public class tdbdump extends CmdTDB
 {
     static ModLangOutput modLangOutput = new ModLangOutput() ;
     
-    static public void main(String... argv)
-    { 
+    static public void main(String... argv) {
         CmdTDB.init() ;
         new tdbdump(argv).mainRun() ;
     }
 
-    protected tdbdump(String[] argv)
-    {
+    protected tdbdump(String[] argv) {
         super(argv) ;
         addModule(modLangOutput) ;
     }
-    
+
     @Override
-    protected String getSummary()
-    {
-        return getCommandName()+" : Write a dataset to stdout (defaults to N-Quads)" ;
+    protected String getSummary() {
+        return getCommandName() + " : Write a dataset to stdout (defaults to N-Quads)" ;
     }
 
     @Override
-    protected void exec()
-    {
+    protected void exec() {
         DatasetGraph dsg = getDatasetGraph() ;
         // Prefer stream over fully pretty output formats.
         RDFFormat fmt = modLangOutput.getOutputStreamFormat() ;


[36/50] [abbrv] jena git commit: JENA-1396: Merge //github/afs/mantis as subdirectory jena-db/

Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/pom.xml
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/pom.xml
index 0000000,0000000..d55b4c8
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/pom.xml
@@@ -1,0 -1,0 +1,122 @@@
++<?xml version="1.0" encoding="utf-8"?>
++<!--
++   Licensed 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.
++ 
++   See the NOTICE file distributed with this work for additional
++   information regarding copyright ownership.
++-->
++
++<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
++
++  <modelVersion>4.0.0</modelVersion>
++
++  <artifactId>tdb2-cmds</artifactId>
++  <packaging>jar</packaging>
++  <name>Mantis (${project.artifactId})</name>
++
++  <parent>
++    <groupId>org.seaborne.mantis</groupId>
++    <artifactId>mantis</artifactId>
++    <version>0.3.1-SNAPSHOT</version>
++  </parent> 
++
++  <dependencies>
++
++    <dependency>
++      <groupId>org.seaborne.mantis</groupId>
++      <artifactId>tdb2</artifactId>
++      <version>0.3.1-SNAPSHOT</version>
++    </dependency>
++
++    <dependency>
++      <groupId>org.apache.jena</groupId>
++      <artifactId>jena-cmds</artifactId>
++      <exclusions>
++        <exclusion>
++           <groupId>org.apache.jena</groupId>
++           <artifactId>jena-tdb</artifactId>
++        </exclusion>
++      </exclusions>
++    </dependency>
++
++    <!-- Require a logging implementation -->
++    <dependency>
++      <groupId>org.slf4j</groupId>
++      <artifactId>slf4j-log4j12</artifactId>
++    </dependency>
++    
++   <dependency>
++     <groupId>log4j</groupId>
++     <artifactId>log4j</artifactId>
++   </dependency> 
++
++  </dependencies>
++
++  <build>
++    <plugins>
++      <plugin>
++        <groupId>org.apache.maven.plugins</groupId>
++        <artifactId>maven-source-plugin</artifactId>
++        <executions>
++          <execution>
++            <id>attach-sources</id>
++            <!-- <phase>package</phase> package is the default -->
++            <goals>
++              <goal>jar-no-fork</goal>
++            </goals>
++          </execution>
++        </executions>
++      </plugin>
++
++      <plugin>
++        <groupId>org.apache.maven.plugins</groupId>
++        <artifactId>maven-shade-plugin</artifactId>
++        <version>2.4.3</version>
++        <configuration>
++          <shadedArtifactAttached>false</shadedArtifactAttached>
++          <transformers>
++            <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
++              <mainClass>tdb2.Intro</mainClass>
++            </transformer>
++            <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
++            <transformer implementation="org.apache.maven.plugins.shade.resource.ApacheLicenseResourceTransformer" />
++            <transformer implementation="org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformer">
++              <addHeader>false</addHeader>
++            </transformer>
++          </transformers>
++          <filters>
++            <filter>
++              <artifact>*:*</artifact>
++              <excludes>
++                <exclude>META-INF/*.SF</exclude>
++                <exclude>META-INF/*.DSA</exclude>
++                <exclude>META-INF/*.RSA</exclude>
++              </excludes>
++            </filter>
++          </filters>
++        </configuration>
++        <executions>
++          <execution>
++            <phase>package</phase>
++            <!--<phase /><!- - Switch off -->
++            <goals>
++              <goal>shade</goal>
++            </goals>
++          </execution>
++        </executions>
++      </plugin>
++
++    </plugins>
++  </build>
++  
++</project>

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/src/main/java/tdb2/Intro.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/src/main/java/tdb2/Intro.java
index 0000000,0000000..f8fd6e0
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/src/main/java/tdb2/Intro.java
@@@ -1,0 -1,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 tdb2;
++
++public class Intro {
++    public static void main(String...a) {
++        System.err.println("Commands available:");
++        System.err.println("  tdb2.tdbloader");
++        System.err.println("  tdb2.tdbquery");
++        System.err.println("  tdb2.tdbupdate");
++        System.err.println("  tdb2.tdbdump");
++        System.err.println("  tdb2.tdbstats");
++        
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/src/main/java/tdb2/cmdline/CmdSub.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/src/main/java/tdb2/cmdline/CmdSub.java
index 0000000,0000000..bda20c5
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/src/main/java/tdb2/cmdline/CmdSub.java
@@@ -1,0 -1,0 +1,71 @@@
++/*
++ * 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 tdb2.cmdline;
++
++import java.util.Collection;
++import java.util.HashMap;
++import java.util.Map;
++
++import jena.cmd.CmdException;
++
++public class CmdSub {
++    public interface Exec {
++        public void exec(String[] argv) ;
++    }
++    private Map<String, Exec> dispatch = new HashMap<>() ;
++
++    private String    subCmd ;
++    private String    args[] ;
++
++    public CmdSub(String... argv) {
++        subCmd = subCommand(argv) ;
++        args = cmdline(argv) ;
++    }
++
++    protected void exec() {
++        Exec exec = dispatch.get(subCmd) ;
++        if ( exec == null )
++            throw new CmdException("No subcommand: " + subCmd) ;
++        exec.exec(args) ;
++    }
++
++    protected static String[] cmdline(String... argv) {
++        String[] a = new String[argv.length - 1] ;
++        System.arraycopy(argv, 1, a, 0, argv.length - 1) ;
++        return a ;
++    }
++
++    protected static String subCommand(String... argv) {
++        if ( argv.length == 0 )
++            throw new CmdException("Missing subcommand") ;
++
++        String subCmd = argv[0] ;
++        if ( subCmd.startsWith("-") )
++            throw new CmdException("Argument found where subcommand expected") ;
++        return subCmd ;
++    }
++
++    protected void addSubCommand(String subCmdName, Exec exec) {
++        dispatch.put(subCmdName, exec) ;
++    }
++
++    protected Collection<String> subCommandNames() {
++        return dispatch.keySet() ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/src/main/java/tdb2/cmdline/CmdTDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/src/main/java/tdb2/cmdline/CmdTDB.java
index 0000000,0000000..da799fd
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/src/main/java/tdb2/cmdline/CmdTDB.java
@@@ -1,0 -1,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 tdb2.cmdline;
++
++import arq.cmdline.CmdARQ ;
++import org.apache.jena.Jena ;
++import org.apache.jena.atlas.lib.Lib ;
++import org.apache.jena.atlas.logging.LogCtl ;
++import org.apache.jena.query.ARQ ;
++import org.apache.jena.query.Dataset ;
++import org.apache.jena.sparql.core.DatasetGraph ;
++import org.apache.jena.system.JenaSystem ;
++import org.seaborne.dboe.base.file.Location;
++import org.seaborne.tdb2.TDB2;
++import org.seaborne.tdb2.store.DatasetGraphSwitchable ;
++import org.seaborne.tdb2.sys.TDBInternal ;
++
++public abstract class CmdTDB extends CmdARQ
++{
++    protected final ModTDBDataset tdbDatasetAssembler   = new ModTDBDataset() ;
++
++    private static boolean initialized = false ;
++    
++    protected CmdTDB(String[] argv) {
++        super(argv) ;
++        init() ;
++        super.addModule(tdbDatasetAssembler) ;
++        super.modVersion.addClass(Jena.class) ;
++        super.modVersion.addClass(ARQ.class) ;
++        super.modVersion.addClass(TDB2.class) ;
++    }
++
++    public static synchronized void init() {
++        // In case called from elsewhere.
++        JenaSystem.init() ;
++        if (initialized)
++            return ;
++        // attempt once.
++        initialized = true ;
++        LogCtl.setCmdLogging() ;
++    }
++
++    @Override
++    protected void processModulesAndArgs() {
++        super.processModulesAndArgs() ;
++    }
++
++    protected Location getLocation() {
++        return tdbDatasetAssembler.getLocation() ;
++    }
++
++    protected DatasetGraph getDatasetGraph() {
++        return getDataset().asDatasetGraph() ;
++    }
++
++    protected DatasetGraphSwitchable getDatabaseContainer() {
++        return TDBInternal.getDatabaseContainer(getDatasetGraph());
++    }
++
++    protected Dataset getDataset() {
++        return tdbDatasetAssembler.getDataset() ;
++    }
++
++    @Override
++    protected String getCommandName() {
++        return Lib.className(this) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/src/main/java/tdb2/cmdline/CmdTDBGraph.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/src/main/java/tdb2/cmdline/CmdTDBGraph.java
index 0000000,0000000..60b7982
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/src/main/java/tdb2/cmdline/CmdTDBGraph.java
@@@ -1,0 -1,0 +1,81 @@@
++/*
++ * 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 tdb2.cmdline;
++
++import jena.cmd.ArgDecl;
++import jena.cmd.CmdException;
++import org.apache.jena.atlas.lib.Lib ;
++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.seaborne.tdb2.store.GraphTDB;
++import tdb2.cmdline.CmdTDB;
++
++public abstract class CmdTDBGraph extends CmdTDB
++{
++    private static final ArgDecl argNamedGraph          = new ArgDecl(ArgDecl.HasValue, "graph") ;
++    protected String graphName = null ;
++    
++    protected CmdTDBGraph(String[] argv)
++    {
++        super(argv) ;
++        super.add(argNamedGraph, "--graph=IRI", "Act on a named graph") ;
++    }
++    
++    @Override
++    protected void processModulesAndArgs()
++    {
++        super.processModulesAndArgs() ;
++        if ( contains(argNamedGraph) )
++            graphName = getValue(argNamedGraph) ; 
++    }
++    
++    protected Model getModel()
++    {
++        Dataset ds = tdbDatasetAssembler.getDataset() ;
++        
++        if ( graphName != null )
++        {
++            Model m = ds.getNamedModel(graphName) ;
++            if ( m == null )
++                throw new CmdException("No such named graph (is this a TDB dataset?)") ;
++            return m ;
++        }
++        else
++            return ds.getDefaultModel() ;
++    }
++    
++    public Node getGraphName()  { return graphName == null ? null : NodeFactory.createURI(graphName) ; } 
++    
++    protected GraphTDB getGraph()
++    {
++        if ( graphName != null )
++            return (GraphTDB)tdbDatasetAssembler.getDataset().getNamedModel(graphName).getGraph() ;
++        else
++            return (GraphTDB)tdbDatasetAssembler.getDataset().getDefaultModel().getGraph() ;
++    }
++    
++    @Override
++    protected String getCommandName()
++    {
++        return Lib.className(this) ;
++    }
++    
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/src/main/java/tdb2/cmdline/ModLocation.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/src/main/java/tdb2/cmdline/ModLocation.java
index 0000000,0000000..1d6b37a
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/src/main/java/tdb2/cmdline/ModLocation.java
@@@ -1,0 -1,0 +1,55 @@@
++/*
++ * 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 tdb2.cmdline;
++
++import jena.cmd.ArgDecl;
++import jena.cmd.CmdArgModule;
++import jena.cmd.CmdGeneral;
++import jena.cmd.ModBase;
++import org.seaborne.dboe.base.file.Location;
++
++public class ModLocation extends ModBase
++{
++    public ModLocation() {}
++    
++    protected final ArgDecl locationDecl = new ArgDecl(ArgDecl.HasValue, "location", "loc") ;
++    protected Location location = null ;
++    
++    @Override
++    public void registerWith(CmdGeneral cmdLine)
++    {
++        cmdLine.getUsage().startCategory("Location") ;
++        cmdLine.add(locationDecl, "--loc=DIR", "Location (a directory)") ;
++    }
++    
++    public void checkCommandLine(CmdArgModule cmdLine)
++    {}
++
++    @Override
++    public void processArgs(CmdArgModule cmdLine)
++    {
++        if ( cmdLine.contains(locationDecl) )
++        {
++            String dir = cmdLine.getValue(locationDecl) ;
++            location = Location.create(dir) ;
++        }
++    }
++    
++    public Location getLocation() { return location ; }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/src/main/java/tdb2/cmdline/ModModel.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/src/main/java/tdb2/cmdline/ModModel.java
index 0000000,0000000..eca9612
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/src/main/java/tdb2/cmdline/ModModel.java
@@@ -1,0 -1,0 +1,66 @@@
++/*
++ * 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 tdb2.cmdline;
++
++import jena.cmd.ArgDecl;
++import jena.cmd.CmdArgModule;
++import jena.cmd.CmdGeneral;
++import jena.cmd.ModBase;
++
++import org.apache.jena.rdf.model.Model ;
++import org.apache.jena.util.FileManager ;
++
++/** Name a model */
++public class ModModel extends ModBase
++{
++    protected ArgDecl modelArgDecl = null ;
++    private Model model = null ;
++    
++    //public ModModel() { this("model") ; }
++    public ModModel(String argName, String ... altNames)
++    {
++        modelArgDecl = new ArgDecl(ArgDecl.HasValue, argName) ;
++        for ( String x : altNames )
++            modelArgDecl.addName(x) ;
++    }
++
++    public ArgDecl getArg() 
++    {
++        return modelArgDecl ;
++    }
++    
++    @Override
++    public void registerWith(CmdGeneral cmdLine)
++    {
++        cmdLine.add(modelArgDecl, "--"+modelArgDecl.getKeyName()+"=filename", "Filename for a model") ;
++    }
++
++    @Override
++    public void processArgs(CmdArgModule cmdLine)
++    {
++        if ( cmdLine.contains(modelArgDecl) )
++        {
++            String filename = cmdLine.getValue(modelArgDecl) ;
++            model = FileManager.get().loadModel(filename) ;
++        }
++    }
++    
++    public Model getModel() { return model ; }
++    
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/src/main/java/tdb2/cmdline/ModTDBAssembler.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/src/main/java/tdb2/cmdline/ModTDBAssembler.java
index 0000000,0000000..6fbb345
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/src/main/java/tdb2/cmdline/ModTDBAssembler.java
@@@ -1,0 -1,0 +1,89 @@@
++/*
++ * 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 tdb2.cmdline;
++
++import java.io.File;
++
++import jena.cmd.CmdArgModule;
++import jena.cmd.CmdException;
++import jena.cmd.CmdGeneral;
++import org.seaborne.dboe.base.file.Location;
++import tdb2.cmdline.ModLocation;
++import arq.cmdline.ModAssembler;
++
++/**  Extends ModAssembler to include --tdb.
++ *   Defaulting to "tdb.ttl" is done in ModTDBDataset because it interacts
++ *   with --location
++ */  
++public class ModTDBAssembler extends ModAssembler
++{
++    private ModLocation modLocation     =  new ModLocation() ;
++
++    public static final String defaultAssemblerFile = "tdb.ttl" ;
++    protected boolean useDefaultAssemblerFile = false ;
++    
++    public ModTDBAssembler()
++    { 
++        super() ;
++        ModAssembler.assemblerDescDecl.addName("tdb") ;
++    }
++    
++    @Override
++    public void processArgs(CmdArgModule cmdLine)
++    {
++        int count = 0 ;
++
++        modLocation.processArgs(cmdLine) ;
++        super.processArgs(cmdLine) ;
++        if ( super.getAssemblerFile() != null ) count++ ;
++        if ( modLocation.getLocation() != null ) count++ ;    
++        
++        if ( count == 0 )
++        {
++            useDefaultAssemblerFile = true ;
++            // throw new CmdException("No assembler file and no location") ;
++        }
++            
++        if ( count > 1 )
++            throw new CmdException("Only one of an assembler file and a location") ;
++    }
++   
++    @Override
++    public void registerWith(CmdGeneral cmdLine)
++    {
++        super.registerWith(cmdLine) ;
++        cmdLine.addModule(modLocation) ;
++        //cmdLine.getUsage().startCategory("Dataset") ;
++        cmdLine.getUsage().addUsage("--tdb=", "Assembler description file") ;
++    }
++ 
++    public Location getLocation() { return modLocation.getLocation() ; }
++    
++    @Override
++    public String getAssemblerFile()
++    {
++        if ( useDefaultAssemblerFile )
++        {
++            File f = new File(defaultAssemblerFile) ;
++            if ( f.exists() )
++                return defaultAssemblerFile ; 
++        }
++        return super.getAssemblerFile() ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/src/main/java/tdb2/cmdline/ModTDBDataset.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/src/main/java/tdb2/cmdline/ModTDBDataset.java
index 0000000,0000000..199268a
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/src/main/java/tdb2/cmdline/ModTDBDataset.java
@@@ -1,0 -1,0 +1,138 @@@
++/*
++ * 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 tdb2.cmdline;
++
++import java.util.ArrayList ;
++import java.util.List ;
++
++import arq.cmdline.ModDataset ;
++import jena.cmd.ArgDecl;
++import jena.cmd.CmdArgModule;
++import jena.cmd.CmdException;
++import jena.cmd.CmdGeneral;
++import org.apache.jena.atlas.logging.Log ;
++import org.apache.jena.query.* ;
++import org.apache.jena.rdf.model.Model ;
++import org.apache.jena.riot.RDFDataMgr ;
++import org.apache.jena.shared.JenaException ;
++import org.apache.jena.sparql.core.assembler.AssemblerUtils ;
++import org.apache.jena.sparql.core.assembler.DatasetAssemblerVocab ;
++import org.apache.jena.util.FileManager ;
++import org.seaborne.dboe.base.file.Location;
++import org.seaborne.tdb2.TDB2Factory;
++import org.seaborne.tdb2.assembler.VocabTDB2;
++import org.seaborne.tdb2.store.DatasetGraphTDB;
++
++public class ModTDBDataset extends ModDataset
++{
++    // Mixes assembler, location and "tdb"
++    // Can make a single model or a dataset
++    
++    private ArgDecl argMem                  = new ArgDecl(ArgDecl.HasValue, "mem", "data") ;
++    private ModTDBAssembler modAssembler    = new ModTDBAssembler() ;
++    private String inMemFile                = null ;
++    
++    public ModTDBDataset() {}
++    
++    @Override
++    public void registerWith(CmdGeneral cmdLine)
++    {
++        cmdLine.add(argMem, "--mem=FILE", "Execute on an in-memory TDB database (for testing)") ;
++        cmdLine.addModule(modAssembler) ;
++    }
++
++    @Override
++    public void processArgs(CmdArgModule cmdLine)
++    {
++        inMemFile = cmdLine.getValue(argMem) ;
++        modAssembler.processArgs(cmdLine) ;
++    }        
++
++    @Override
++    public Dataset createDataset() {
++        if ( inMemFile != null ) {
++            Dataset ds = TDB2Factory.createDataset();
++            RDFDataMgr.read(ds, inMemFile);
++            return ds;
++        }
++
++        if ( modAssembler.getAssemblerFile() != null ) {
++            Dataset thing = null;
++            // Two variants: plain dataset with TDB2 dataset or plain building
++            // (which may go wrong later if TDB2 directly is needed).
++            try {
++                thing = (Dataset)AssemblerUtils.build(modAssembler.getAssemblerFile(), VocabTDB2.tDatasetTDB);
++                if ( thing != null && !(thing.asDatasetGraph() instanceof DatasetGraphTDB) )
++                    Log.warn(this, "Unexpected: Not a TDB2 dataset for type DatasetTDB2");
++
++                if ( thing == null )
++                    // Should use assembler inheritance but how do we assert
++                    // the subclass relationship in a program?
++                    thing = (Dataset)AssemblerUtils.build(modAssembler.getAssemblerFile(), DatasetAssemblerVocab.tDataset);
++            }
++            catch (JenaException ex) {
++                throw ex;
++            }
++            catch (Exception ex) {
++                throw new CmdException("Error creating", ex);
++            }
++            return thing;
++        }
++
++        if ( modAssembler.getLocation() == null )
++            throw new CmdException("No assembler file nor location provided");
++
++        // No assembler - use location to find a database.
++        Dataset ds = TDB2Factory.connectDataset(modAssembler.getLocation());
++        return ds;
++    }
++    
++    public Location getLocation()
++    {
++        List<String> x = locations() ;
++        if ( x.size() == 0 )
++            return null ;
++        return Location.create(x.get(0)) ;
++    }
++    
++    public List<String> locations()
++    {
++        List<String> locations = new ArrayList<>() ;
++        
++        if ( modAssembler.getLocation() != null )
++            locations.add(modAssembler.getLocation().getDirectoryPath()) ;
++
++        // Extract the location from the assember file.
++        if ( modAssembler.getAssemblerFile() != null )
++        {
++            // Find and clear all locations
++            Model m = FileManager.get().loadModel(modAssembler.getAssemblerFile()) ;
++            Query query = QueryFactory.create("PREFIX tdb:     <http://jena.hpl.hp.com/2008/tdb#> SELECT ?dir { [] tdb:location ?dir FILTER (isURI(?dir)) }") ;
++            try(QueryExecution qExec = QueryExecutionFactory.create(query, m)) {
++                for (ResultSet rs = qExec.execSelect() ; rs.hasNext() ; )
++                {
++                    String x = rs.nextSolution().getResource("dir").getURI() ;
++                    locations.add(x) ;
++                }
++            }
++        }
++        
++        return locations ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/src/main/java/tdb2/tdbbackup.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/src/main/java/tdb2/tdbbackup.java
index 0000000,0000000..8291514
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/src/main/java/tdb2/tdbbackup.java
@@@ -1,0 -1,0 +1,55 @@@
++/*
++ * 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 tdb2;
++
++import arq.cmdline.ModLangOutput ;
++import org.seaborne.tdb2.store.DatasetGraphSwitchable ;
++import org.seaborne.tdb2.sys.DatabaseOps ;
++import tdb2.cmdline.CmdTDB;
++
++public class tdbbackup extends CmdTDB
++{
++    static ModLangOutput modLangOutput = new ModLangOutput() ;
++    
++    static public void main(String... argv)
++    { 
++        CmdTDB.init() ;
++        new tdbbackup(argv).mainRun() ;
++    }
++
++    protected tdbbackup(String[] argv)
++    {
++        super(argv) ;
++        addModule(modLangOutput) ;
++    }
++    
++    @Override
++    protected String getSummary()
++    {
++        return getCommandName()+" : Backup a TDB dataset" ;
++    }
++
++    @Override
++    protected void exec()
++    {
++        DatasetGraphSwitchable dsg = getDatabaseContainer();
++        String fn = DatabaseOps.backup(dsg);
++        System.out.println("Backup written to "+fn);
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/src/main/java/tdb2/tdbcompact.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/src/main/java/tdb2/tdbcompact.java
index 0000000,0000000..ea2e275
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/src/main/java/tdb2/tdbcompact.java
@@@ -1,0 -1,0 +1,48 @@@
++/*
++ * 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 tdb2;
++
++import org.seaborne.tdb2.store.DatasetGraphSwitchable ;
++import org.seaborne.tdb2.sys.DatabaseOps ;
++import tdb2.cmdline.CmdTDB;
++
++public class tdbcompact extends CmdTDB {
++    static public void main(String... argv) {
++        CmdTDB.init() ;
++        new tdbcompact(argv).mainRun() ;
++    }
++
++    protected tdbcompact(String[] argv) {
++        super(argv) ;
++    }
++
++    @Override
++    protected String getSummary() {
++        return getCommandName() + " : Compact a TDB2 dataset" ;
++    }
++
++    @Override
++    protected void exec() {
++        DatasetGraphSwitchable dsg = getDatabaseContainer() ;
++        long start = System.currentTimeMillis();
++        DatabaseOps.compact(dsg) ;
++        long finish = System.currentTimeMillis();
++        System.out.printf("Compacted in %.3fs", (finish-start)/1000.0);
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/src/main/java/tdb2/tdbdump.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/src/main/java/tdb2/tdbdump.java
index 0000000,0000000..9c50e87
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/src/main/java/tdb2/tdbdump.java
@@@ -1,0 -1,0 +1,66 @@@
++/*
++ * 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 tdb2;
++
++import arq.cmdline.ModLangOutput ;
++import jena.cmd.CmdException ;
++import org.apache.jena.riot.RDFDataMgr ;
++import org.apache.jena.riot.RDFFormat ;
++import org.apache.jena.riot.RDFLanguages ;
++import org.apache.jena.sparql.core.DatasetGraph ;
++import org.seaborne.dboe.jenax.Txn;
++import tdb2.cmdline.CmdTDB;
++
++public class tdbdump extends CmdTDB
++{
++    static ModLangOutput modLangOutput = new ModLangOutput() ;
++    
++    static public void main(String... argv) {
++        CmdTDB.init() ;
++        new tdbdump(argv).mainRun() ;
++    }
++
++    protected tdbdump(String[] argv) {
++        super(argv) ;
++        addModule(modLangOutput) ;
++    }
++
++    @Override
++    protected String getSummary() {
++        return getCommandName() + " : Write a dataset to stdout (defaults to N-Quads)" ;
++    }
++
++    @Override
++    protected void exec() {
++        DatasetGraph dsg = getDatasetGraph() ;
++        // Prefer stream over fully pretty output formats.
++        RDFFormat fmt = modLangOutput.getOutputStreamFormat() ;
++        // Stream writing happens naturally - no need to call StreamRDFWriter.
++        //if ( fmt != null && StreamRDFWriter.registered(fmt) )
++        if ( fmt == null )
++            fmt = modLangOutput.getOutputFormatted() ;
++        if ( fmt == null )
++            // Default.
++            fmt = RDFFormat.NQUADS ;
++        if ( ! RDFLanguages.isQuads(fmt.getLang() ))
++            throw new CmdException("Databases can be dumped only in quad formats (e.g. Trig, N-Quads), not "+fmt.getLang()) ;
++        RDFFormat fmtFinal = fmt ;
++        Txn.executeRead(dsg, ()->RDFDataMgr.write(System.out, dsg, fmtFinal));
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/src/main/java/tdb2/tdbloader.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/src/main/java/tdb2/tdbloader.java
index 0000000,0000000..f31cb70
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/src/main/java/tdb2/tdbloader.java
@@@ -1,0 -1,0 +1,175 @@@
++/*
++ * 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 tdb2 ;
++
++import java.util.List ;
++
++import jena.cmd.ArgDecl;
++import jena.cmd.CmdException;
++import org.apache.jena.atlas.lib.FileOps;
++import org.apache.jena.atlas.lib.NotImplemented;
++import org.apache.jena.atlas.lib.ProgressMonitor;
++import org.apache.jena.graph.Graph;
++import org.apache.jena.query.ARQ ;
++import org.apache.jena.riot.Lang ;
++import org.apache.jena.riot.RDFDataMgr;
++import org.apache.jena.riot.RDFLanguages ;
++import org.apache.jena.riot.system.ProgressStreamRDF;
++import org.apache.jena.riot.system.StreamRDF;
++import org.apache.jena.riot.system.StreamRDFLib;
++import org.apache.jena.sparql.core.DatasetGraph;
++import org.apache.jena.system.Txn;
++import org.slf4j.Logger;
++import org.slf4j.LoggerFactory;
++import tdb2.cmdline.CmdTDB;
++import tdb2.cmdline.CmdTDBGraph;
++
++public class tdbloader extends CmdTDBGraph {
++    // private static final ArgDecl argParallel = new ArgDecl(ArgDecl.NoValue, "parallel") ;
++    // private static final ArgDecl argIncremental = new ArgDecl(ArgDecl.NoValue, "incr", "incremental") ;
++    private static final ArgDecl argNoStats = new ArgDecl(ArgDecl.NoValue, "nostats") ;
++    private static final ArgDecl argStats = new ArgDecl(ArgDecl.HasValue,  "stats") ;
++
++    private boolean showProgress  = true ;
++    private boolean generateStats  = true ;
++
++    static public void main(String... argv) {
++        CmdTDB.init() ;
++        new tdbloader(argv).mainRun() ;
++    }
++
++    protected tdbloader(String[] argv) {
++        super(argv) ;
++        super.add(argNoStats, "--nostats", "Switch off statistics gathering") ;
++        super.add(argStats) ;   // Hidden argument
++    }
++
++    @Override
++    protected void processModulesAndArgs() {
++        super.processModulesAndArgs() ;
++    }
++
++    @Override
++    protected String getSummary() {
++        return getCommandName() + " [--desc DATASET | -loc DIR] FILE ..." ;
++    }
++
++    @Override
++    protected void exec() {
++        if ( isVerbose() ) {
++            System.out.println("Java maximum memory: " + Runtime.getRuntime().maxMemory()) ;
++            System.out.println(ARQ.getContext()) ;
++        }
++        if ( isVerbose() )
++            showProgress = true ;
++        if ( isQuiet() )
++            showProgress = false ;
++        if ( super.contains(argStats) ) {
++            if ( ! hasValueOfTrue(argStats) && ! hasValueOfFalse(argStats) )
++                throw new CmdException("Not a boolean value: "+getValue(argStats)) ;
++            generateStats = super.hasValueOfTrue(argStats) ;
++        }
++
++        if ( super.contains(argNoStats))
++            generateStats = false ;
++        
++        List<String> urls = getPositional() ;
++        if ( urls.size() == 0 )
++            urls.add("-") ;
++
++        if ( graphName == null ) {
++            loadQuads(urls) ;
++            return ;
++        }
++        
++        // There's a --graph.
++        // Check/warn that there are no quads formats mentioned
++        // (RIOT will take the default graph from quads).  
++        
++        for ( String url : urls ) {
++            Lang lang = RDFLanguages.filenameToLang(url) ;
++            if ( lang != null && RDFLanguages.isQuads(lang) ) {
++                System.err.println("Warning: Quads format given - only the default graph is loaded into the graph for --graph") ;
++                break ;
++            }
++        }
++        
++        loadNamedGraph(urls) ;
++    }
++
++//    void loadDefaultGraph(List<String> urls) {
++//        GraphTDB graph = getGraph() ;
++//        TDBLoader.load(graph, urls, showProgress) ;
++//        return ;
++//    }
++
++    void loadNamedGraph(List<String> urls) {
++        Graph graph = getGraph() ;
++        TDBLoader.load(graph, urls, showProgress) ;
++        return ;
++    }
++
++    void loadQuads(List<String> urls) {
++        TDBLoader.load(getDatasetGraph(), urls, showProgress, generateStats) ;
++        return ;
++    }
++    
++    /** Tick point for messages during loading of data */
++    public static int       DataTickPoint         = 50 * 1000 ;
++    /** Tick point for messages during secondary index creation */
++    public static long      IndexTickPoint        = 100 * 1000 ;
++
++    /** Number of ticks per super tick */
++    public static int       superTick             = 10 ;
++    
++    private static Logger LOG = LoggerFactory.getLogger("TDB2");
++    
++    static class TDBLoader {
++
++        public static void load(DatasetGraph dsg, List<String> urls, boolean showProgress, boolean generateStats) {
++            Txn.executeWrite(dsg, ()->{
++                urls.forEach((x)->loadOne(dsg, x, showProgress));
++            });
++        }
++
++        private static void loadOne(DatasetGraph dsg, String x, boolean showProgress) {
++            StreamRDF dest = StreamRDFLib.dataset(dsg);
++            StreamRDF sink = dest;
++            ProgressMonitor monitor = null;
++            if ( showProgress ) { 
++                String basename = FileOps.splitDirFile(x).get(1);
++                monitor = ProgressMonitor.create(LOG, basename, DataTickPoint, superTick); 
++                sink = new ProgressStreamRDF(sink, monitor);
++            }
++            if ( monitor!= null )
++                monitor.start();
++            sink.start();
++            RDFDataMgr.parse(sink, x);
++            sink.finish();
++            if ( monitor!= null ) {
++                monitor.finish();
++                monitor.finishMessage();
++            }
++        }
++
++        public static void load(Graph graph, List<String> urls, boolean showProgress) {
++            throw new NotImplemented();
++        }
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/src/main/java/tdb2/tdbquery.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/src/main/java/tdb2/tdbquery.java
index 0000000,0000000..fffe597
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/src/main/java/tdb2/tdbquery.java
@@@ -1,0 -1,0 +1,51 @@@
++/*
++ * 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 tdb2;
++
++import arq.query;
++import arq.cmdline.ModDataset;
++import tdb2.cmdline.CmdTDB;
++import tdb2.cmdline.ModTDBDataset;
++
++public class tdbquery extends query {
++    // Inherits from arq.query so is not a CmdTDB. Mixins for Java!
++    public static void main(String... argv) {
++        CmdTDB.init();
++        new tdbquery(argv).mainRun();
++    }
++
++    public tdbquery(String[] argv) {
++        super(argv);
++    }
++
++    @Override
++    protected String getSummary() {
++        return getCommandName() + " --loc=<path> --query=<query>";
++    }
++
++//    @Override
++//    protected void processModulesAndArgs() {
++//        super.processModulesAndArgs();
++//    }
++
++    @Override
++    protected ModDataset setModDataset() {
++        return new ModTDBDataset();
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/src/main/java/tdb2/tdbstats.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/src/main/java/tdb2/tdbstats.java
index 0000000,0000000..7f53199
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/src/main/java/tdb2/tdbstats.java
@@@ -1,0 -1,0 +1,99 @@@
++/*
++ * 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 tdb2;
++
++import java.util.Iterator ;
++
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.atlas.logging.Log ;
++import org.apache.jena.graph.Node ;
++import org.apache.jena.sparql.core.Quad ;
++import org.seaborne.dboe.jenax.Txn;
++import org.seaborne.tdb2.solver.SolverLib;
++import org.seaborne.tdb2.solver.stats.Stats;
++import org.seaborne.tdb2.solver.stats.StatsCollectorNodeId;
++import org.seaborne.tdb2.solver.stats.StatsResults;
++import org.seaborne.tdb2.store.DatasetGraphTDB;
++import org.seaborne.tdb2.store.NodeId;
++import org.seaborne.tdb2.store.nodetable.NodeTable;
++import org.seaborne.tdb2.store.nodetupletable.NodeTupleTable;
++import org.seaborne.tdb2.sys.TDBInternal ;
++import tdb2.cmdline.CmdTDB;
++import tdb2.cmdline.CmdTDBGraph;
++
++public class tdbstats extends CmdTDBGraph {
++    static public void main(String... argv) {
++        CmdTDB.init();
++        new tdbstats(argv).mainRun();
++    }
++
++    protected tdbstats(String[] argv) {
++        super(argv);
++    }
++
++    @Override
++    protected String getSummary() {
++        return null;
++    }
++
++    public static StatsResults stats(DatasetGraphTDB dsg, Node gn) {
++        return Txn.calculateRead(dsg, ()->stats$(dsg, gn));
++    }
++    
++    private static StatsResults stats$(DatasetGraphTDB dsg, Node gn) {
++                            
++        NodeTable nt = dsg.getTripleTable().getNodeTupleTable().getNodeTable();
++        StatsCollectorNodeId stats = new StatsCollectorNodeId(nt);
++
++        if ( gn == null ) {
++            Iterator<Tuple<NodeId>> iter = dsg.getTripleTable().getNodeTupleTable().findAll();
++            for ( ; iter.hasNext() ; ) {
++                Tuple<NodeId> t = iter.next();
++                stats.record(null, t.get(0), t.get(1), t.get(2));
++            }
++        } else {
++            // If the union graph, then we need to scan all quads but with uniqueness.
++            boolean unionGraph = Quad.isUnionGraph(gn) ;
++            NodeId gnid = null ;
++            if ( !unionGraph ) {
++                gnid = nt.getNodeIdForNode(gn);
++                if ( NodeId.isDoesNotExist(gnid) )
++                    Log.warn(tdbstats.class, "No such graph: " + gn);
++            }
++
++            NodeTupleTable ntt = dsg.getQuadTable().getNodeTupleTable();
++            Iterator<Tuple<NodeId>> iter = unionGraph
++                ? SolverLib.unionGraph(ntt)
++                : ntt.find(gnid, null, null, null) ;
++            for ( ; iter.hasNext() ; ) {
++                Tuple<NodeId> t = iter.next();
++                stats.record(t.get(0), t.get(1), t.get(2), t.get(3));
++            }
++        }
++        return stats.results();
++    }
++
++    @Override
++    protected void exec() {
++        DatasetGraphTDB dsg = TDBInternal.getDatasetGraphTDB(getDatasetGraph());
++        Node gn = getGraphName();
++        StatsResults results = stats(dsg, gn);
++        Stats.write(System.out, results);
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/src/main/java/tdb2/tdbupdate.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/src/main/java/tdb2/tdbupdate.java
index 0000000,0000000..cbe0245
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/src/main/java/tdb2/tdbupdate.java
@@@ -1,0 -1,0 +1,56 @@@
++/*
++ * 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 tdb2;
++
++import arq.cmdline.ModDataset;
++import jena.cmd.CmdException;
++import org.apache.jena.sparql.core.DatasetGraph;
++import org.seaborne.tdb2.TDB2;
++import tdb2.cmdline.CmdTDB;
++import tdb2.cmdline.ModTDBDataset;
++
++public class tdbupdate extends arq.update {
++    // Inherits from arq.update so is not a CmdTDB. Mixins for Java!
++    public static void main(String... argv) {
++        CmdTDB.init();
++        new tdbupdate(argv).mainRun();
++    }
++
++    public tdbupdate(String[] argv) {
++        super(argv);
++        // Because this inherits from an ARQ command
++        CmdTDB.init();
++        super.modVersion.addClass(TDB2.class);
++    }
++
++    @Override
++    protected void processModulesAndArgs() {
++        super.processModulesAndArgs();
++    }
++
++    @Override
++    protected ModDataset setModeDataset() {
++        return new ModTDBDataset();
++    }
++
++    @Override
++    protected DatasetGraph dealWithNoDataset() {
++        throw new CmdException("No dataset provided");
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/src/main/resources/META-INF/LICENSE
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/src/main/resources/META-INF/LICENSE
index 0000000,0000000..67db858
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/src/main/resources/META-INF/LICENSE
@@@ -1,0 -1,0 +1,175 @@@
++
++                                 Apache License
++                           Version 2.0, January 2004
++                        http://www.apache.org/licenses/
++
++   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
++
++   1. Definitions.
++
++      "License" shall mean the terms and conditions for use, reproduction,
++      and distribution as defined by Sections 1 through 9 of this document.
++
++      "Licensor" shall mean the copyright owner or entity authorized by
++      the copyright owner that is granting the License.
++
++      "Legal Entity" shall mean the union of the acting entity and all
++      other entities that control, are controlled by, or are under common
++      control with that entity. For the purposes of this definition,
++      "control" means (i) the power, direct or indirect, to cause the
++      direction or management of such entity, whether by contract or
++      otherwise, or (ii) ownership of fifty percent (50%) or more of the
++      outstanding shares, or (iii) beneficial ownership of such entity.
++
++      "You" (or "Your") shall mean an individual or Legal Entity
++      exercising permissions granted by this License.
++
++      "Source" form shall mean the preferred form for making modifications,
++      including but not limited to software source code, documentation
++      source, and configuration files.
++
++      "Object" form shall mean any form resulting from mechanical
++      transformation or translation of a Source form, including but
++      not limited to compiled object code, generated documentation,
++      and conversions to other media types.
++
++      "Work" shall mean the work of authorship, whether in Source or
++      Object form, made available under the License, as indicated by a
++      copyright notice that is included in or attached to the work
++      (an example is provided in the Appendix below).
++
++      "Derivative Works" shall mean any work, whether in Source or Object
++      form, that is based on (or derived from) the Work and for which the
++      editorial revisions, annotations, elaborations, or other modifications
++      represent, as a whole, an original work of authorship. For the purposes
++      of this License, Derivative Works shall not include works that remain
++      separable from, or merely link (or bind by name) to the interfaces of,
++      the Work and Derivative Works thereof.
++
++      "Contribution" shall mean any work of authorship, including
++      the original version of the Work and any modifications or additions
++      to that Work or Derivative Works thereof, that is intentionally
++      submitted to Licensor for inclusion in the Work by the copyright owner
++      or by an individual or Legal Entity authorized to submit on behalf of
++      the copyright owner. For the purposes of this definition, "submitted"
++      means any form of electronic, verbal, or written communication sent
++      to the Licensor or its representatives, including but not limited to
++      communication on electronic mailing lists, source code control systems,
++      and issue tracking systems that are managed by, or on behalf of, the
++      Licensor for the purpose of discussing and improving the Work, but
++      excluding communication that is conspicuously marked or otherwise
++      designated in writing by the copyright owner as "Not a Contribution."
++
++      "Contributor" shall mean Licensor and any individual or Legal Entity
++      on behalf of whom a Contribution has been received by Licensor and
++      subsequently incorporated within the Work.
++
++   2. Grant of Copyright License. Subject to the terms and conditions of
++      this License, each Contributor hereby grants to You a perpetual,
++      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
++      copyright license to reproduce, prepare Derivative Works of,
++      publicly display, publicly perform, sublicense, and distribute the
++      Work and such Derivative Works in Source or Object form.
++
++   3. Grant of Patent License. Subject to the terms and conditions of
++      this License, each Contributor hereby grants to You a perpetual,
++      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
++      (except as stated in this section) patent license to make, have made,
++      use, offer to sell, sell, import, and otherwise transfer the Work,
++      where such license applies only to those patent claims licensable
++      by such Contributor that are necessarily infringed by their
++      Contribution(s) alone or by combination of their Contribution(s)
++      with the Work to which such Contribution(s) was submitted. If You
++      institute patent litigation against any entity (including a
++      cross-claim or counterclaim in a lawsuit) alleging that the Work
++      or a Contribution incorporated within the Work constitutes direct
++      or contributory patent infringement, then any patent licenses
++      granted to You under this License for that Work shall terminate
++      as of the date such litigation is filed.
++
++   4. Redistribution. You may reproduce and distribute copies of the
++      Work or Derivative Works thereof in any medium, with or without
++      modifications, and in Source or Object form, provided that You
++      meet the following conditions:
++
++      (a) You must give any other recipients of the Work or
++          Derivative Works a copy of this License; and
++
++      (b) You must cause any modified files to carry prominent notices
++          stating that You changed the files; and
++
++      (c) You must retain, in the Source form of any Derivative Works
++          that You distribute, all copyright, patent, trademark, and
++          attribution notices from the Source form of the Work,
++          excluding those notices that do not pertain to any part of
++          the Derivative Works; and
++
++      (d) If the Work includes a "NOTICE" text file as part of its
++          distribution, then any Derivative Works that You distribute must
++          include a readable copy of the attribution notices contained
++          within such NOTICE file, excluding those notices that do not
++          pertain to any part of the Derivative Works, in at least one
++          of the following places: within a NOTICE text file distributed
++          as part of the Derivative Works; within the Source form or
++          documentation, if provided along with the Derivative Works; or,
++          within a display generated by the Derivative Works, if and
++          wherever such third-party notices normally appear. The contents
++          of the NOTICE file are for informational purposes only and
++          do not modify the License. You may add Your own attribution
++          notices within Derivative Works that You distribute, alongside
++          or as an addendum to the NOTICE text from the Work, provided
++          that such additional attribution notices cannot be construed
++          as modifying the License.
++
++      You may add Your own copyright statement to Your modifications and
++      may provide additional or different license terms and conditions
++      for use, reproduction, or distribution of Your modifications, or
++      for any such Derivative Works as a whole, provided Your use,
++      reproduction, and distribution of the Work otherwise complies with
++      the conditions stated in this License.
++
++   5. Submission of Contributions. Unless You explicitly state otherwise,
++      any Contribution intentionally submitted for inclusion in the Work
++      by You to the Licensor shall be under the terms and conditions of
++      this License, without any additional terms or conditions.
++      Notwithstanding the above, nothing herein shall supersede or modify
++      the terms of any separate license agreement you may have executed
++      with Licensor regarding such Contributions.
++
++   6. Trademarks. This License does not grant permission to use the trade
++      names, trademarks, service marks, or product names of the Licensor,
++      except as required for reasonable and customary use in describing the
++      origin of the Work and reproducing the content of the NOTICE file.
++
++   7. Disclaimer of Warranty. Unless required by applicable law or
++      agreed to in writing, Licensor provides the Work (and each
++      Contributor provides its Contributions) on an "AS IS" BASIS,
++      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
++      implied, including, without limitation, any warranties or conditions
++      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
++      PARTICULAR PURPOSE. You are solely responsible for determining the
++      appropriateness of using or redistributing the Work and assume any
++      risks associated with Your exercise of permissions under this License.
++
++   8. Limitation of Liability. In no event and under no legal theory,
++      whether in tort (including negligence), contract, or otherwise,
++      unless required by applicable law (such as deliberate and grossly
++      negligent acts) or agreed to in writing, shall any Contributor be
++      liable to You for damages, including any direct, indirect, special,
++      incidental, or consequential damages of any character arising as a
++      result of this License or out of the use or inability to use the
++      Work (including but not limited to damages for loss of goodwill,
++      work stoppage, computer failure or malfunction, or any and all
++      other commercial damages or losses), even if such Contributor
++      has been advised of the possibility of such damages.
++
++   9. Accepting Warranty or Additional Liability. While redistributing
++      the Work or Derivative Works thereof, You may choose to offer,
++      and charge a fee for, acceptance of support, warranty, indemnity,
++      or other liability obligations and/or rights consistent with this
++      License. However, in accepting such obligations, You may act only
++      on Your own behalf and on Your sole responsibility, not on behalf
++      of any other Contributor, and only if You agree to indemnify,
++      defend, and hold each Contributor harmless for any liability
++      incurred by, or claims asserted against, such Contributor by reason
++      of your accepting any such warranty or additional liability.

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/src/main/resources/META-INF/NOTICE
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/src/main/resources/META-INF/NOTICE
index 0000000,0000000..5ba7ec0
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/src/main/resources/META-INF/NOTICE
@@@ -1,0 -1,0 +1,10 @@@
++Mantis Database Operating Environment
++
++Copyright 2013-2017 Andy Seaborne
++Copyright 2014, 2015 Epimorphics Ltd.
++
++Portions of this software derive from software from Apache Jena:
++  - Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP
++  - Copyright 2010, 2011 Epimorphics Ltd.
++  - Copyright 2010, 2011 Talis Systems Ltd.
++These have been licensed to the Apache Software Foundation under a software grant.

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/src/main/resources/META-INF/services/org.apache.jena.system.JenaSubsystemLifecycle
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/src/main/resources/META-INF/services/org.apache.jena.system.JenaSubsystemLifecycle
index 0000000,0000000..21f02ea
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/src/main/resources/META-INF/services/org.apache.jena.system.JenaSubsystemLifecycle
@@@ -1,0 -1,0 +1,1 @@@
++org.seaborne.tdb2.sys.InitTDB2

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/src/main/resources/org/apache/jena/tdb/tdb-properties.xml
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/src/main/resources/org/apache/jena/tdb/tdb-properties.xml
index 0000000,0000000..67d8255
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/src/main/resources/org/apache/jena/tdb/tdb-properties.xml
@@@ -1,0 -1,0 +1,8 @@@
++<?xml version="1.0" encoding="UTF-8"?>
++<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
++<!-- Licensed under the terms of http://www.apache.org/licenses/LICENSE-2.0 -->
++<properties version="1.0">
++  <comment>TDB System Properties</comment>
++  <entry key="org.apache.jena.tdb.version">${project.version}</entry>
++  <entry key="org.apache.jena.tdb.build.datetime">${build.time.xsd}</entry>
++</properties>

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/tdb2
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/tdb2
index 0000000,0000000..d2772db
new file mode 100755
--- /dev/null
+++ b/jena-db/tdb2-cmds/tdb2
@@@ -1,0 -1,0 +1,99 @@@
++#!/usr/bin/env bash
++
++## Usage: tdb2
++## Sets up the java enviroment to run Jena-based commands.
++
++CP=
++
++#JENA_CP="$(jena)"
++## If Jena development, this needs to be different.
++JENA_CP="$HOME/jlib/apache-jena/lib/"'*'
++
++## Log4j
++LOG4J_CP="/home/afs/.m2/repo/org/slf4j/slf4j-log4j12/1.7.25/slf4j-log4j12-1.7.25.jar:/home/afs/.m2/repo/log4j/log4j/1.2.17/log4j-1.2.17.jar"
++## JUL
++JUL_CP="/home/afs/.m2/repo/org/slf4j/slf4j-jdk14/1.7.25/slf4j-jdk14-1.7.25.jar"
++
++H="$HOME/Mantis"
++D=
++
++# modules
++# Not dboe-index-test
++read -r -d '' MODULES << EOM
++dboe-base 
++dboe-index 
++dboe-transaction
++dboe-trans-data
++tdb2
++tdb2-cmds
++EOM
++
++if true # if in development area ./
++then
++    for X in $MODULES
++    do
++	[[ $D = "" ]] || D="$D:"
++	D="${D}$H/$X/target/classes"
++    done
++else
++    ## @@ By artifacts ...
++    echo "Not implemented: classpath by artifacts" 2>&1
++    exit 9
++fi
++
++
++TDB2_CP="$D"
++
++if [[ -e logging.properties ]]
++then
++    LOGGING_JUL=-Djava.util.logging.config.file=logging.properties
++fi
++if [[ -e log4j.properties ]]
++then
++    LOGGING_LOG4J=-Dlog4j.configuration=file:log4j.properties
++fi
++
++if [[ $# = 0 ]]
++then
++    echo "Class path: $TDB2_CP:${JENA_CP}"
++    exit
++    #echo "No class to run" 1>&2
++    #exit 1
++else
++    CMD="$1"
++    shift
++fi
++
++# Map CMD to class name
++TDB2_PKG="tdb2"
++
++# Alternative names.
++## case $CMD in
++##     ???
++## 	CMD=tdb2.$CMD
++## 	;;
++##     *) ;;
++## esac
++
++# Map to class name.
++case $CMD in
++    "")
++    ;;
++    tdbbackup | tdbdump | tdbloader | tdbquery | tdbstats | tdbupdate | tdbcompact)
++	CMD_CLS="$TDB2_PKG"."$CMD"
++	;;
++##     tools.*)
++## 	CMD_CLS="${BASE_PKG}"."$CMD"
++## 	;;
++    *)
++	echo "Unknown command $CMD" 2>&1
++	exit 1
++    ;;
++
++esac
++
++## echo "TDB2_CP=$TDB2_CP"
++## echo "JENA_CP=$JENA_CP"
++#echo java $JVM_ARGS $LOGGING_LOG4J $LOGGING_JUL -cp "$TDB2_CP:${JENA_CP}" "$CMD_CLS" "$@"
++
++java $JVM_ARGS $LOGGING_LOG4J $LOGGING_JUL -cp "$TDB2_CP:${JENA_CP}" "$CMD_CLS" "$@"

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Assembler/tdb-dataset-embed.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Assembler/tdb-dataset-embed.ttl
index 0000000,0000000..9edd5a3
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Assembler/tdb-dataset-embed.ttl
@@@ -1,0 -1,0 +1,23 @@@
++#  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.
++
++@prefix tdb2:    <http://jena.apache.org/2016/tdb#> .
++@prefix rdfs:	 <http://www.w3.org/2000/01/rdf-schema#> .
++@prefix rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
++@prefix ja:      <http://jena.hpl.hp.com/2005/11/Assembler#> .
++
++<#dataset> rdf:type      tdb2:DatasetTDB2 ;
++    tdb2:location "--mem--" ;
++    .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Assembler/tdb-dataset.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Assembler/tdb-dataset.ttl
index 0000000,0000000..c9d8d7b
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Assembler/tdb-dataset.ttl
@@@ -1,0 -1,0 +1,23 @@@
++#  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.
++
++@prefix tdb2:    <http://jena.apache.org/2016/tdb#> .
++@prefix rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
++@prefix ja:      <http://jena.hpl.hp.com/2005/11/Assembler#> .
++
++<#dataset> rdf:type      tdb2:DatasetTDB2 ;
++    # Do at least one the long way.
++    tdb2:location "target/tdb-testing/DB" ;
++    .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Assembler/tdb-graph-embed.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Assembler/tdb-graph-embed.ttl
index 0000000,0000000..af3daab
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Assembler/tdb-graph-embed.ttl
@@@ -1,0 -1,0 +1,27 @@@
++#  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.
++
++@prefix tdb2:    <http://jena.apache.org/2016/tdb#> .
++@prefix rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
++@prefix rdfs:	 <http://www.w3.org/2000/01/rdf-schema#> .
++@prefix ja:      <http://jena.hpl.hp.com/2005/11/Assembler#> .
++
++<#dataset> rdf:type      ja:RDFDataset ;
++    ja:defaultGraph <#graph> ;
++    . 
++
++<#graph> rdf:type tdb2:GraphTDB2 ;
++    tdb2:location "--mem--" ;
++    .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Assembler/tdb-graph-ref-dataset.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Assembler/tdb-graph-ref-dataset.ttl
index 0000000,0000000..fe256bd
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Assembler/tdb-graph-ref-dataset.ttl
@@@ -1,0 -1,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.
++
++@prefix tdb2:    <http://jena.apache.org/2016/tdb#> .
++@prefix rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
++@prefix rdfs:	 <http://www.w3.org/2000/01/rdf-schema#> .
++@prefix ja:      <http://jena.hpl.hp.com/2005/11/Assembler#> .
++
++<#dataset> rdf:type      ja:RDFDataset ;
++    ja:defaultGraph <#graph> ;
++    . 
++
++<#graph> rdf:type tdb2:GraphTDB2 ;
++    tdb2:dataset <#dataset> ;
++    .
++
++<#dataset> rdf:type tdb2:DatasetTDB2 ;
++    tdb2:location "--mem--" ;
++    .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Assembler/tdb-graph.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Assembler/tdb-graph.ttl
index 0000000,0000000..2ae8396
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Assembler/tdb-graph.ttl
@@@ -1,0 -1,0 +1,27 @@@
++#  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.
++
++@prefix tdb2:    <http://jena.apache.org/2016/tdb#> .
++@prefix rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
++@prefix ja:      <http://jena.hpl.hp.com/2005/11/Assembler#> .
++
++<#dataset> rdf:type      ja:RDFDataset ;
++    ja:defaultGraph <#graph> ;
++    . 
++
++<#graph> rdf:type tdb2:GraphTDB2 ;
++    tdb2:location "target/tdb-testing/DB" ;
++    .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Assembler/tdb-named-graph-1.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Assembler/tdb-named-graph-1.ttl
index 0000000,0000000..78e40cf
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Assembler/tdb-named-graph-1.ttl
@@@ -1,0 -1,0 +1,29 @@@
++
++#  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.
++
++@prefix tdb2:    <http://jena.apache.org/2016/tdb#> .
++@prefix rdfs:	 <http://www.w3.org/2000/01/rdf-schema#> .
++@prefix rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
++@prefix ja:      <http://jena.hpl.hp.com/2005/11/Assembler#> .
++
++<#dataset> rdf:type      ja:RDFDataset ;
++    ja:defaultGraph <#graph> ;
++    . 
++
++<#graph> rdf:type tdb2:GraphTDB2 ;
++    tdb2:location "--mem--" ;
++    tdb2:graphName "http://example.com/graph" ;
++    .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Assembler/tdb-named-graph-2.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Assembler/tdb-named-graph-2.ttl
index 0000000,0000000..ee2feab
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Assembler/tdb-named-graph-2.ttl
@@@ -1,0 -1,0 +1,28 @@@
++#  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.
++
++@prefix tdb2:    <http://jena.apache.org/2016/tdb#> .
++@prefix rdfs:	 <http://www.w3.org/2000/01/rdf-schema#> .
++@prefix rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
++@prefix ja:      <http://jena.hpl.hp.com/2005/11/Assembler#> .
++
++<#dataset> rdf:type      ja:RDFDataset ;
++    ja:defaultGraph <#graph> ;
++    . 
++
++<#graph> rdf:type tdb2:GraphTDB2 ;
++    tdb2:location "--mem--" ;
++    tdb2:graphName "http://example.com/graph" ;
++    .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Basic/basic-00.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Basic/basic-00.rq
index 0000000,0000000..f2341a0
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Basic/basic-00.rq
@@@ -1,0 -1,0 +1,4 @@@
++PREFIX :  <http://example>
++
++SELECT * 
++{ ?x ?p ?z }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Basic/basic-1-O.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Basic/basic-1-O.rq
index 0000000,0000000..e1d1da7
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Basic/basic-1-O.rq
@@@ -1,0 -1,0 +1,4 @@@
++PREFIX :  <http://example/>
++
++SELECT * 
++{ ?x ?p :z1 }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Basic/basic-1-P.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Basic/basic-1-P.rq
index 0000000,0000000..d64b9f9
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Basic/basic-1-P.rq
@@@ -1,0 -1,0 +1,4 @@@
++PREFIX :  <http://example/>
++
++SELECT * 
++{ ?x :p1 ?z }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Basic/basic-1-PO.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Basic/basic-1-PO.rq
index 0000000,0000000..01fa14a
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Basic/basic-1-PO.rq
@@@ -1,0 -1,0 +1,4 @@@
++PREFIX :  <http://example/>
++
++SELECT * 
++{ ?x :p1 :z1 }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Basic/basic-1-S.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Basic/basic-1-S.rq
index 0000000,0000000..180188e
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Basic/basic-1-S.rq
@@@ -1,0 -1,0 +1,4 @@@
++PREFIX :  <http://example/>
++
++SELECT * 
++{ :x1 ?p ?z }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Basic/basic-1-SO.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Basic/basic-1-SO.rq
index 0000000,0000000..a4242fb
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Basic/basic-1-SO.rq
@@@ -1,0 -1,0 +1,4 @@@
++PREFIX :  <http://example/>
++
++SELECT * 
++{ :x1 ?p :z1 }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Basic/basic-1-SP.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Basic/basic-1-SP.rq
index 0000000,0000000..37ddfe6
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Basic/basic-1-SP.rq
@@@ -1,0 -1,0 +1,4 @@@
++PREFIX :  <http://example/>
++
++SELECT * 
++{ :x1 :p1 ?z }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Basic/basic-1-SPO.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Basic/basic-1-SPO.rq
index 0000000,0000000..41aa20f
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Basic/basic-1-SPO.rq
@@@ -1,0 -1,0 +1,4 @@@
++PREFIX :  <http://example/>
++
++SELECT ?v
++{ :x1 :p1 :z1 }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Basic/basic-2-SO.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Basic/basic-2-SO.rq
index 0000000,0000000..49c5711
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Basic/basic-2-SO.rq
@@@ -1,0 -1,0 +1,4 @@@
++PREFIX :  <http://example/>
++
++SELECT *
++{ ?x ?p ?x }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Basic/basic-3-O.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Basic/basic-3-O.rq
index 0000000,0000000..f7f3f6b
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Basic/basic-3-O.rq
@@@ -1,0 -1,0 +1,4 @@@
++PREFIX :  <http://example/>
++
++SELECT * 
++{ ?x ?p :z1a }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Basic/basic-3-P.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Basic/basic-3-P.rq
index 0000000,0000000..8c79fa5
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Basic/basic-3-P.rq
@@@ -1,0 -1,0 +1,4 @@@
++PREFIX :  <http://example/>
++
++SELECT * 
++{ ?x :p1a ?z }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Basic/basic-3-PO.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Basic/basic-3-PO.rq
index 0000000,0000000..a011a1c
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Basic/basic-3-PO.rq
@@@ -1,0 -1,0 +1,4 @@@
++PREFIX :  <http://example/>
++
++SELECT * 
++{ ?x :p1 :z1a }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Basic/basic-3-S.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Basic/basic-3-S.rq
index 0000000,0000000..8b62930
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Basic/basic-3-S.rq
@@@ -1,0 -1,0 +1,4 @@@
++PREFIX :  <http://example/>
++
++SELECT * 
++{ :x1a ?p ?z }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Basic/basic-3-SO.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Basic/basic-3-SO.rq
index 0000000,0000000..e71f0b3
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Basic/basic-3-SO.rq
@@@ -1,0 -1,0 +1,4 @@@
++PREFIX :  <http://example/>
++
++SELECT * 
++{ :x1 ?p :z1a }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Basic/basic-3-SP.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Basic/basic-3-SP.rq
index 0000000,0000000..3b7ff81
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Basic/basic-3-SP.rq
@@@ -1,0 -1,0 +1,4 @@@
++PREFIX :  <http://example/>
++
++SELECT * 
++{ :x1a :p1 ?z }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Basic/data-1.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Basic/data-1.ttl
index 0000000,0000000..d3a3d80
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Basic/data-1.ttl
@@@ -1,0 -1,0 +1,17 @@@
++@prefix : <http://example/> .
++
++:x1 :p1 :z1 .
++:x1 :p1 :z2 .
++
++:x1 :p2 :z1 .
++:x1 :p2 :z2 .
++
++:x2 :p1 :z1 .
++:x2 :p1 :z2 .
++
++:x2 :p2 :z1 .
++:x2 :p2 :z2 .
++
++:x :p :x .
++:x :x :x .
++


[13/50] [abbrv] jena git commit: Remove DatasetControl - no longer applies

Posted by an...@apache.org.
Remove DatasetControl - no longer applies


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

Branch: refs/heads/jena-tdb2
Commit: 982c0c7f6321bb280006624554e36669a9e98348
Parents: 4e8fc3b
Author: Andy Seaborne <an...@seaborne.org>
Authored: Mon Sep 4 10:36:48 2017 +0100
Committer: Andy Seaborne <an...@seaborne.org>
Committed: Mon Sep 4 10:36:48 2017 +0100

----------------------------------------------------------------------
 .../org/seaborne/tdb2/setup/TDBBuilder.java     |   8 +-
 .../org/seaborne/tdb2/store/DatasetControl.java |  44 ------
 .../seaborne/tdb2/store/DatasetControlMRSW.java | 144 -------------------
 .../seaborne/tdb2/store/DatasetControlNone.java |  48 -------
 .../tdb2/store/DatasetControlReadOnly.java      |  59 --------
 .../java/org/seaborne/tdb2/store/QuadTable.java |   4 +-
 .../java/org/seaborne/tdb2/store/TableBase.java |   5 +-
 .../org/seaborne/tdb2/store/TripleTable.java    |   5 +-
 .../store/nodetupletable/NodeTupleTable.java    |   4 -
 .../nodetupletable/NodeTupleTableConcrete.java  |  66 ++++-----
 .../nodetupletable/NodeTupleTableWrapper.java   |   5 -
 11 files changed, 33 insertions(+), 359 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/982c0c7f/tdb2/src/main/java/org/seaborne/tdb2/setup/TDBBuilder.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/setup/TDBBuilder.java b/tdb2/src/main/java/org/seaborne/tdb2/setup/TDBBuilder.java
index 67ee708..88e18fc 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/setup/TDBBuilder.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/setup/TDBBuilder.java
@@ -143,8 +143,6 @@ public class TDBBuilder {
         return new TDBBuilder(txnCoord, location, params, new ComponentIdMgr(UUID.randomUUID())) ;
     }
 
-    private DatasetControl createPolicy() { return new DatasetControlNone() ; }
-
     /** Look at a directory and see if it is a new area */
     private static boolean isNewDatabaseArea(Location location) {
         if ( location.isMem() )
@@ -188,7 +186,7 @@ public class TDBBuilder {
 
         if ( tripleIndexes.length != indexes.length )
             error(log, "Wrong number of triple table tuples indexes: "+tripleIndexes.length) ;
-        TripleTable tripleTable = new TripleTable(tripleIndexes, nodeTable, createPolicy()) ;
+        TripleTable tripleTable = new TripleTable(tripleIndexes, nodeTable) ;
         return tripleTable ;
     }
 
@@ -205,7 +203,7 @@ public class TDBBuilder {
 
         if ( tripleIndexes.length != indexes.length )
             error(log, "Wrong number of triple table tuples indexes: "+tripleIndexes.length) ;
-        QuadTable tripleTable = new QuadTable(tripleIndexes, nodeTable, createPolicy()) ;
+        QuadTable tripleTable = new QuadTable(tripleIndexes, nodeTable) ;
         return tripleTable ;
     }
 
@@ -221,7 +219,7 @@ public class TDBBuilder {
         //NodeTable prefixNodes = makeNodeTable(location, pnNode2Id, pnId2Node, -1, -1, -1)  ;
         NodeTupleTable prefixTable = new NodeTupleTableConcrete(primary.length(),
                                                                 prefixIndexes,
-                                                                prefixNodes, createPolicy()) ;
+                                                                prefixNodes) ;
         DatasetPrefixesTDB prefixes = new DatasetPrefixesTDB(prefixTable) ; 
         log.debug("Prefixes: "+primary+" :: "+String.join(",", indexes)) ;
         return prefixes ;

http://git-wip-us.apache.org/repos/asf/jena/blob/982c0c7f/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetControl.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetControl.java b/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetControl.java
deleted file mode 100644
index b283ed4..0000000
--- a/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetControl.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- *  Licensed 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.
- *
- *  See the NOTICE file distributed with this work for additional
- *  information regarding copyright ownership.
- */
-
-package org.seaborne.tdb2.store;
-
-import java.util.Iterator ;
-
-// Used in NodeTupleTables.
-
-/** A DatasetControl is an encapsulation of what to do on reads and writes.
- *  In addition, iterators returned can be tied back to the original request
- *  to check they are still valid.  
- */
-public interface DatasetControl
-{
-    /** Fine grained, internal update - start */  
-    public void startUpdate() ;
-    
-    /** Fine grained, internal update - finish */
-    public void finishUpdate();
-
-    /** Signal the start of an internal read operation */
-    public void startRead();
-    
-    /** Signal the completion of an internal read operation */
-    public void finishRead();
-    
-    /** An iterator being created. */
-    public <T> Iterator<T> iteratorControl(Iterator<T> iter) ;
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/982c0c7f/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetControlMRSW.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetControlMRSW.java b/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetControlMRSW.java
deleted file mode 100644
index d4c9f57..0000000
--- a/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetControlMRSW.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- *  Licensed 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.
- *
- *  See the NOTICE file distributed with this work for additional
- *  information regarding copyright ownership.
- */
-
-package org.seaborne.tdb2.store;
-
-import static java.lang.String.format ;
-
-import java.util.ConcurrentModificationException ;
-import java.util.Iterator ;
-import java.util.NoSuchElementException ;
-import java.util.concurrent.atomic.AtomicLong ;
-
-import org.apache.jena.atlas.iterator.Iter ;
-import org.apache.jena.atlas.lib.Closeable ;
-
-/** A policy that checks, but does not enforce, single writer or multiple writer locking policy */ 
-public class DatasetControlMRSW implements DatasetControl
-{
-    private final boolean concurrencyChecking = true ;
-    private final AtomicLong epoch = new AtomicLong(5) ;                // Update counters, used to check iterators. No need to start at 0.
-    private final AtomicLong readCounter = new AtomicLong(0) ;
-    private final AtomicLong writeCounter = new AtomicLong(0) ;
-    
-    public DatasetControlMRSW()
-    { }
-
-    @Override
-    public void startRead() {
-        readCounter.getAndIncrement() ;
-        checkConcurrency() ;
-    }
-
-    @Override
-    public void finishRead() {
-        readCounter.decrementAndGet() ;
-    }
-
-    @Override
-    public void startUpdate() {
-        epoch.getAndIncrement() ;
-        writeCounter.getAndIncrement() ;
-        checkConcurrency() ;
-    }
-
-    @Override
-    public void finishUpdate() {
-        writeCounter.decrementAndGet() ;
-    }
-
-    private void checkConcurrency() {
-        long R, W ;
-        synchronized (this) {
-            R = readCounter.get() ;
-            W = writeCounter.get() ;
-        }
-
-        if ( R > 0 && W > 0 )
-            policyError(R, W) ;
-        if ( W > 1 )
-            policyError(R, W) ;
-    }
-    
-    @Override
-    public <T> Iterator<T> iteratorControl(Iterator<T> iter) { return new IteratorCheckNotConcurrent<>(iter, epoch) ; }
-    
-    private static class IteratorCheckNotConcurrent<T> implements Iterator<T>, Closeable
-    {
-        private Iterator<T> iter ;
-        private AtomicLong eCount ;
-        private boolean finished = false ;
-        private long startEpoch ; 
-
-        IteratorCheckNotConcurrent(Iterator<T> iter, AtomicLong eCount) {
-            // Assumes correct locking to set up, i.e. eCount not changing
-            // (writer on separate thread).
-            this.iter = iter ;
-            this.eCount = eCount ;
-            this.startEpoch = eCount.get() ;
-        }
-
-        private void checkConcurrentModification() {
-            if ( finished )
-                return ;
-
-            long now = eCount.get() ;
-            if ( now != startEpoch ) {
-                policyError(format("Iterator: started at %d, now %d", startEpoch, now)) ;
-            }
-        }
-
-        @Override
-        public boolean hasNext() {
-            checkConcurrentModification() ;
-            boolean b = iter.hasNext() ;
-            if ( !b )
-                close() ;
-            return b ;
-        }
-
-        @Override
-        public T next() {
-            checkConcurrentModification() ;
-            try { return iter.next() ; }
-            catch (NoSuchElementException ex) {
-                close() ;
-                throw ex ;
-            }
-        }
-
-        @Override
-        public void remove() {
-            checkConcurrentModification() ;
-            iter.remove() ;
-        }
-
-        @Override
-        public void close() {
-            finished = true ;
-            Iter.close(iter) ;
-        }
-    }
-    
-    private static void policyError(long R, long W) {
-        policyError(format("Reader = %d, Writer = %d", R, W)) ;
-    }
-
-    private static void policyError(String message) {
-        throw new ConcurrentModificationException(message) ;
-    }    
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/982c0c7f/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetControlNone.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetControlNone.java b/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetControlNone.java
deleted file mode 100644
index b1bf51b..0000000
--- a/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetControlNone.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- *  Licensed 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.
- *
- *  See the NOTICE file distributed with this work for additional
- *  information regarding copyright ownership.
- */
-
-package org.seaborne.tdb2.store;
-
-import java.util.Iterator ;
-
-/** Concurrency policy  that is a "free for all".
- *  No checking, no locking.
- */
-public final class DatasetControlNone implements DatasetControl
-{
-    @Override
-    public void startUpdate()
-    {}
-
-    @Override
-    public void finishUpdate()
-    {}
-
-    @Override
-    public void startRead()
-    {}
-
-    @Override
-    public void finishRead()
-    {}
-
-    @Override
-    public <T> Iterator<T> iteratorControl(Iterator<T> iter)
-    {
-        return iter ;
-    }
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/982c0c7f/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetControlReadOnly.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetControlReadOnly.java b/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetControlReadOnly.java
deleted file mode 100644
index 85b31be..0000000
--- a/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetControlReadOnly.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- *  Licensed 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.
- *
- *  See the NOTICE file distributed with this work for additional
- *  information regarding copyright ownership.
- */
-
-package org.seaborne.tdb2.store;
-
-import java.util.Iterator ;
-import java.util.concurrent.atomic.AtomicLong ;
-
-import org.seaborne.tdb2.TDBException ;
-
-/** A policy that provide read-only access */ 
-public class DatasetControlReadOnly implements DatasetControl
-{
-    private final AtomicLong readCounter = new AtomicLong(0) ;
-    
-    public DatasetControlReadOnly()
-    { }
-
-    @Override
-    public void startRead()
-    {
-        readCounter.getAndIncrement() ;
-    }
-
-    @Override
-    public void finishRead()
-    {
-        readCounter.decrementAndGet() ;
-    }
-
-    @Override
-    public void startUpdate()
-    {
-        throw new TDBException("Read-only") ;
-    }
-
-    @Override
-    public void finishUpdate()
-    {
-        throw new TDBException("Read-only") ;
-    }
-
-    @Override
-    public <T> Iterator<T> iteratorControl(Iterator<T> iter) { return iter ; }
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/982c0c7f/tdb2/src/main/java/org/seaborne/tdb2/store/QuadTable.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/QuadTable.java b/tdb2/src/main/java/org/seaborne/tdb2/store/QuadTable.java
index 15a14ff..6d4b366 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/store/QuadTable.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/store/QuadTable.java
@@ -34,8 +34,8 @@ import org.seaborne.tdb2.store.tupletable.TupleIndex ;
  */
 
 public class QuadTable extends TableBase {
-    public QuadTable(TupleIndex[] indexes, NodeTable nodeTable, DatasetControl policy) {
-        super(4, indexes, nodeTable, policy) ;
+    public QuadTable(TupleIndex[] indexes, NodeTable nodeTable) {
+        super(4, indexes, nodeTable) ;
     }
 
     /** Add a quad - return true if it was added, false if it already existed */

http://git-wip-us.apache.org/repos/asf/jena/blob/982c0c7f/tdb2/src/main/java/org/seaborne/tdb2/store/TableBase.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/TableBase.java b/tdb2/src/main/java/org/seaborne/tdb2/store/TableBase.java
index 05b80ab..1241a0b 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/store/TableBase.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/store/TableBase.java
@@ -28,12 +28,11 @@ public class TableBase implements Sync, Closeable
 {
     final protected NodeTupleTable table ;
     
-    protected TableBase(int N, TupleIndex[] indexes, NodeTable nodeTable, DatasetControl policy) {
-        table = new NodeTupleTableConcrete(N, indexes, nodeTable, policy) ;
+    protected TableBase(int N, TupleIndex[] indexes, NodeTable nodeTable) {
+        table = new NodeTupleTableConcrete(N, indexes, nodeTable) ;
     }
 
     public NodeTupleTable getNodeTupleTable()   { return table ; }
-    public DatasetControl getPolicy()           { return table.getPolicy() ; }
     
     @Override
     public void sync() {

http://git-wip-us.apache.org/repos/asf/jena/blob/982c0c7f/tdb2/src/main/java/org/seaborne/tdb2/store/TripleTable.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/TripleTable.java b/tdb2/src/main/java/org/seaborne/tdb2/store/TripleTable.java
index 8e5aeb9..a85494b 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/store/TripleTable.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/store/TripleTable.java
@@ -35,9 +35,8 @@ import org.seaborne.tdb2.store.tupletable.TupleIndex ;
  */
 
 public class TripleTable extends TableBase {
-    public TripleTable(TupleIndex[] indexes, NodeTable nodeTable, DatasetControl policy) {
-        super(3, indexes, nodeTable, policy) ;
-        // table = new NodeTupleTableConcrete(3, indexes, nodeTable, policy) ;
+    public TripleTable(TupleIndex[] indexes, NodeTable nodeTable) {
+        super(3, indexes, nodeTable) ;
     }
 
     /** Add triple */

http://git-wip-us.apache.org/repos/asf/jena/blob/982c0c7f/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTable.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTable.java b/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTable.java
index 89139c9..4d48189 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTable.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTable.java
@@ -23,7 +23,6 @@ import org.apache.jena.atlas.lib.Closeable ;
 import org.apache.jena.atlas.lib.Sync ;
 import org.apache.jena.atlas.lib.tuple.Tuple ;
 import org.apache.jena.graph.Node ;
-import org.seaborne.tdb2.store.DatasetControl ;
 import org.seaborne.tdb2.store.NodeId ;
 import org.seaborne.tdb2.store.nodetable.NodeTable ;
 import org.seaborne.tdb2.store.tupletable.TupleTable ;
@@ -69,7 +68,4 @@ public interface NodeTupleTable extends Sync, Closeable
     // and node tables separately.
     
     public long size() ;
-
-    /** Return the current policy, if any, for this NodeTupleTable */
-    public DatasetControl getPolicy() ;
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/982c0c7f/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableConcrete.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableConcrete.java b/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableConcrete.java
index 0d967f0..e2fc5a9 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableConcrete.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableConcrete.java
@@ -27,7 +27,6 @@ import org.apache.jena.atlas.lib.tuple.TupleFactory ;
 import org.apache.jena.graph.Node ;
 import org.seaborne.tdb2.TDBException ;
 import org.seaborne.tdb2.lib.TupleLib ;
-import org.seaborne.tdb2.store.DatasetControl ;
 import org.seaborne.tdb2.store.NodeId ;
 import org.seaborne.tdb2.store.nodetable.NodeTable ;
 import org.seaborne.tdb2.store.tupletable.TupleIndex ;
@@ -38,14 +37,13 @@ public class NodeTupleTableConcrete implements NodeTupleTable
 {
     protected final NodeTable  nodeTable ;
     protected final TupleTable tupleTable ;
-    private final DatasetControl dsPolicy ;
 
     /*
      * Concurrency checking: Everything goes through one of 
      * addRow, deleteRow or find*
      */
 
-    public NodeTupleTableConcrete(int N, TupleIndex[] indexes, NodeTable nodeTable, DatasetControl dsControl)
+    public NodeTupleTableConcrete(int N, TupleIndex[] indexes, NodeTable nodeTable)
     {
         if (indexes.length == 0 || indexes[0] == null) throw new TDBException("A primary index is required") ;
         for (TupleIndex index : indexes)
@@ -55,24 +53,19 @@ public class NodeTupleTableConcrete implements NodeTupleTable
                                               N, index.getMappingStr(), index.getTupleLength())) ;
         }
 
-        this.dsPolicy = dsControl ;
         this.tupleTable = new TupleTable(N, indexes) ;
         this.nodeTable = nodeTable ;
     }
 
-    private void startWrite()   { dsPolicy.startUpdate() ; }
+    private void startWrite()   { }
 
-    private void finishWrite()  { dsPolicy.finishUpdate() ; }
+    private void finishWrite()  { }
 
-    private void startRead()    { dsPolicy.startRead() ; }
+    private void startRead()    { }
 
-    private void finishRead()   { dsPolicy.finishRead() ; }
+    private void finishRead()   { }
 
     @Override
-    public DatasetControl getPolicy()
-    { return dsPolicy ; }
-    
-    @Override
     public void addRow(Node... nodes)
     {
         try  {
@@ -180,73 +173,61 @@ public class NodeTupleTableConcrete implements NodeTupleTable
 
     // ==== Node
 
-    protected final NodeId idForNode(Node node)
-    {
-        if (node == null || node == Node.ANY) return NodeId.NodeIdAny ;
-        if (node.isVariable()) throw new TDBException("Can't pass variables to NodeTupleTable.find*") ;
+    protected final NodeId idForNode(Node node) {
+        if ( node == null || node == Node.ANY )
+            return NodeId.NodeIdAny ;
+        if ( node.isVariable() )
+            throw new TDBException("Can't pass variables to NodeTupleTable.find*") ;
         return nodeTable.getNodeIdForNode(node) ;
     }
 
     // ==== Accessors
 
     /**
-     * Return the undelying tuple table - used with great care by tools that
-     * directly manipulate internal structures.
+     * Return the undelying tuple table - used with great care by tools that directly
+     * manipulate internal structures.
      */
     @Override
-    public final TupleTable getTupleTable()
-    {
+    public final TupleTable getTupleTable() {
         return tupleTable ;
     }
 
     /** Return the node table */
     @Override
-    public final NodeTable getNodeTable()
-    {
+    public final NodeTable getNodeTable() {
         return nodeTable ;
     }
 
     @Override
-    public boolean isEmpty()
-    {
+    public boolean isEmpty() {
         return tupleTable.isEmpty() ;
     }
 
     /** Clear the tuple table - does not clear the node table */
     @Override
-    public void clear()
-    {
+    public void clear() {
         try {
             startWrite() ;
             tupleTable.clear() ;
-        } finally {
-            finishWrite() ;
-        }
+        } finally { finishWrite() ; }
     }
 
     @Override
-    public long size()
-    {
+    public long size() {
         return tupleTable.size() ;
     }
 
-    // @Override
     @Override
-    public final void close()
-    {
-        try
-        {
+    public final void close() {
+        try {
             startWrite() ;
             tupleTable.close() ;
             nodeTable.close() ;
-        } 
-        finally { finishWrite() ; }
+        } finally { finishWrite() ; }
     }
 
-    // @Override
     @Override
-    public final void sync()
-    {
+    public final void sync() {
         try {
             startWrite() ;
             tupleTable.sync() ;
@@ -254,5 +235,6 @@ public class NodeTupleTableConcrete implements NodeTupleTable
         } finally { finishWrite() ; }
     }
 
-    private <T> Iterator<T> iteratorControl(Iterator<T> iter) { return dsPolicy.iteratorControl(iter) ; }
+    // Add any iterator checking.
+    private <T> Iterator<T> iteratorControl(Iterator<T> iter) { return iter; }
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/982c0c7f/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableWrapper.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableWrapper.java b/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableWrapper.java
index e54f964..952dda9 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableWrapper.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/store/nodetupletable/NodeTupleTableWrapper.java
@@ -21,7 +21,6 @@ import java.util.Iterator ;
 
 import org.apache.jena.atlas.lib.tuple.Tuple ;
 import org.apache.jena.graph.Node ;
-import org.seaborne.tdb2.store.DatasetControl ;
 import org.seaborne.tdb2.store.NodeId ;
 import org.seaborne.tdb2.store.nodetable.NodeTable ;
 import org.seaborne.tdb2.store.tupletable.TupleTable ;
@@ -77,10 +76,6 @@ public class NodeTupleTableWrapper implements NodeTupleTable
     { return nodeTupleTable.getTupleTable() ; }
 
     @Override
-    public DatasetControl getPolicy()
-    { return nodeTupleTable.getPolicy() ; }
-    
-    @Override
     public boolean isEmpty()
     { return nodeTupleTable.isEmpty() ; }
     


[04/50] [abbrv] jena git commit: Switch to switchable

Posted by an...@apache.org.
Switch to switchable


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

Branch: refs/heads/jena-tdb2
Commit: 780d67167bf4018f579ccbc56f8606962d3b665a
Parents: d72f696
Author: Andy Seaborne <an...@seaborne.org>
Authored: Thu Aug 31 12:55:23 2017 +0100
Committer: Andy Seaborne <an...@seaborne.org>
Committed: Thu Aug 31 12:55:46 2017 +0100

----------------------------------------------------------------------
 .../java/org/seaborne/tdb2/DatabaseMgr.java     |  15 +-
 .../seaborne/tdb2/sys/DatabaseConnection.java   |  34 ++--
 .../java/org/seaborne/tdb2/sys/DatabaseOps.java |  26 +--
 .../org/seaborne/tdb2/sys/StoreConnection.java  |   6 +-
 .../test/java/org/seaborne/tdb2/TS_Factory.java |  11 +-
 .../seaborne/tdb2/TestDatabaseConnection.java   |  68 ++++++++
 .../java/org/seaborne/tdb2/TestDatabaseMgr.java |  89 +++++++++++
 .../java/org/seaborne/tdb2/TestDatabaseOps.java | 114 +++++++++++++
 .../java/org/seaborne/tdb2/TestTDBFactory.java  | 159 ++++++++++---------
 .../test/java/org/seaborne/tdb2/junit/TL.java   |  10 +-
 .../AbstractTestStoreConnectionBasics.java      |   3 +-
 11 files changed, 422 insertions(+), 113 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/780d6716/tdb2/src/main/java/org/seaborne/tdb2/DatabaseMgr.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/DatabaseMgr.java b/tdb2/src/main/java/org/seaborne/tdb2/DatabaseMgr.java
index 33a4a6b..7b3ff17 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/DatabaseMgr.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/DatabaseMgr.java
@@ -18,14 +18,13 @@
 
 package org.seaborne.tdb2;
 
-import org.apache.commons.lang3.NotImplementedException ;
 import org.apache.jena.query.Dataset ;
 import org.apache.jena.sparql.core.DatasetGraph ;
 import org.seaborne.dboe.base.file.Location ;
 import org.seaborne.tdb2.repack.DatasetGraphSwitchable ;
 import org.seaborne.tdb2.store.DatasetGraphTDB ;
+import org.seaborne.tdb2.sys.DatabaseConnection ;
 import org.seaborne.tdb2.sys.DatabaseOps ;
-import org.seaborne.tdb2.sys.StoreConnection ;
 
 /** Operations for TDBS DatasetGraph, including admin operations 
  * See {@link TDB2Factory} for creating API-level {@link Dataset Datasets}.
@@ -36,10 +35,9 @@ public class DatabaseMgr {
 
     // All creation of DatasetGraph for TDB2 goes through this method.
     private static DatasetGraph DB_ConnectCreate(Location location) {
-        //return DatabaseOps.connectOrCreate(location);
-        // One level.
-        StoreConnection sConn = StoreConnection.connectCreate(location) ;
-        return sConn.getDatasetGraph() ; 
+        return DatabaseConnection.connectCreate(location).getDatasetGraph();
+//        // One level.
+//        return StoreConnection.connectCreate(location).getDatasetGraph() ; 
     }
 
     /** Create or connect to a TDB2-backed dataset */
@@ -73,10 +71,11 @@ public class DatabaseMgr {
      * and does not lock out other use of the dataset.
      * 
      * @param container
+     * @return File name of the backup.
      */
-    public static void backup(DatasetGraph container) {
+    public static String backup(DatasetGraph container) {
         DatasetGraphSwitchable dsg = requireSwitchable(container);
-        throw new NotImplementedException("DatasetGraph backup not implemented yet");
+        return DatabaseOps.backup(dsg);
     }
 
 

http://git-wip-us.apache.org/repos/asf/jena/blob/780d6716/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseConnection.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseConnection.java b/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseConnection.java
index 3fa4983..62c5949 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseConnection.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseConnection.java
@@ -21,7 +21,9 @@ package org.seaborne.tdb2.sys;
 import java.io.IOException ;
 import java.nio.file.Path ;
 import java.nio.file.Paths ;
+import java.util.HashSet ;
 import java.util.Map ;
+import java.util.Set ;
 import java.util.concurrent.ConcurrentHashMap ;
 
 import org.apache.jena.atlas.io.IO ;
@@ -68,21 +70,20 @@ public class DatabaseConnection {
             return dbConn;
         }
         // Cached by Location. Named in-memory or on-disk.
-        ProcessFileLock lock = null;
-        if (SystemTDB.DiskLocationMultiJvmUsagePrevention && ! location.isMem() ) {
-            lock = lockForLocation(location);
-            // Take the lock.  This is atomic.
-            lock.lockEx();
-        }
-        final ProcessFileLock lock2 = lock;
-        DatabaseConnection dbConn = cache.computeIfAbsent(location, (loc)->buildForCache(loc, lock2, params));
+        DatabaseConnection dbConn = cache.computeIfAbsent(location, (loc)->buildForCache(loc, params));
         return dbConn ;
     }
     
-    private static DatabaseConnection buildForCache(Location location,  ProcessFileLock lock, StoreParams params) {
+    private static DatabaseConnection buildForCache(Location location, StoreParams params) {
         if ( location.isMemUnique() ) {
             throw new TDBException("Can't buildForCache a memory-unique location");
         }
+        ProcessFileLock lock = null;
+        if (SystemTDB.DiskLocationMultiJvmUsagePrevention && ! location.isMem() ) {
+            lock = lockForLocation(location);
+            // Take the lock.  This is atomic.
+            lock.lockEx();
+        }
         DatasetGraph dsg = DatabaseOps.create(location);
         return new DatabaseConnection(dsg, location, lock) ;
     }
@@ -130,6 +131,21 @@ public class DatabaseConnection {
         }
     }
     
+    /** 
+     * Stop managing all locations. 
+     * Use with extreme care.
+     * This is intended to support internal testing.
+     */
+    public static synchronized void reset() {
+        // Copy to avoid potential CME.
+        Set<Location> x = new HashSet<>(cache.keySet()) ;
+        for (Location loc : x)
+            expel(loc, true) ;
+        cache.clear() ;
+        StoreConnection.reset();
+    }
+    
+    
     // One of the other.
     private final DatasetGraphSwitchable   datasetGraphSwitchable;
     private final DatasetGraph             datasetGraph;

http://git-wip-us.apache.org/repos/asf/jena/blob/780d6716/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseOps.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseOps.java b/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseOps.java
index 8a375b9..82dc579 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseOps.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseOps.java
@@ -27,6 +27,7 @@ import java.util.zip.GZIPOutputStream ;
 
 import org.apache.jena.atlas.RuntimeIOException ;
 import org.apache.jena.atlas.lib.DateTimeUtils ;
+import org.apache.jena.atlas.lib.Pair ;
 import org.apache.jena.atlas.logging.Log;
 import org.apache.jena.riot.Lang ;
 import org.apache.jena.riot.RDFDataMgr ;
@@ -42,6 +43,8 @@ import org.seaborne.tdb2.repack.sys.FilenameUtils;
 import org.seaborne.tdb2.repack.sys.Util;
 import org.seaborne.tdb2.setup.StoreParams;
 import org.seaborne.tdb2.store.DatasetGraphTDB;
+import org.slf4j.Logger ;
+import org.slf4j.LoggerFactory ;
 
 /** Operations on and about TDB2 databases. 
  * <p>
@@ -60,6 +63,7 @@ import org.seaborne.tdb2.store.DatasetGraphTDB;
  * </ul>
  */
 public class DatabaseOps {
+    private static Logger LOG = LoggerFactory.getLogger(DatabaseOps.class); 
     private static final String dbPrefix     = "Data";
     private static final String SEP          = "-";
     private static final String startCount   = "0001";
@@ -68,15 +72,15 @@ public class DatabaseOps {
     // Basename of the backup file. "backup_{DateTime}.nq.gz
     private static final String BACKUPS_FN   = "backup";
     
+    /** Create a fresh database - called by {@code DatabaseMgr}.
+     * It is important to go via {@code DatabaseConnection} to avoid
+     * duplicate {@code DatasetGraphSwitchable}s for the same location.
+     */
     /*package*/ static DatasetGraph create(Location location) {
         // Hide implementation class.
         return createSwitchable(location);
     }
     
-    /** Create a fresh database - called by {@code DatabaseMgr}.
-     * It is important to go via {@code DatabaseMgr} to avoid
-     * duplicate {@code DatasetGraphSwitchable}s for the same location.
-     */
     private static DatasetGraphSwitchable createSwitchable(Location location) {
         if ( location.isMem() ) {
             DatasetGraph dsg = StoreConnection.connectCreate(location).getDatasetGraph();
@@ -100,7 +104,7 @@ public class DatabaseOps {
        return appDSG;
     }
     
-    public static void backup(DatasetGraphSwitchable container) {
+    public static String backup(DatasetGraphSwitchable container) {
         checkSupportsAdmin(container);
         Path dbPath = container.getContainerPath();
         Path backupDir = dbPath.resolve(BACKUPS_DIR);
@@ -117,14 +121,15 @@ public class DatabaseOps {
 //      activeBackups.add(dsg) ;
 //  }
 
-        try (OutputStream out2 = openUniqueFileForWriting(backupDir, BACKUPS_FN, "nq.gz");
+        Pair<OutputStream, Path> x = openUniqueFileForWriting(backupDir, BACKUPS_FN, "nq.gz");
+        try (OutputStream out2 = x.getLeft();
              OutputStream out1 = new GZIPOutputStream(out2, 8 * 1024) ;
              OutputStream out = new BufferedOutputStream(out1)) {
             Txn.executeRead(dsg, ()->RDFDataMgr.write(out, dsg, Lang.NQUADS));
         } catch (IOException e) {
             throw IOX.exception(e) ;
-        } finally {
         }
+        return x.getRight().toString();
     }
     
     private static void checkSupportsAdmin(DatasetGraphSwitchable container) {
@@ -136,7 +141,7 @@ public class DatabaseOps {
     
     
     // --> IOX
-    private static OutputStream openUniqueFileForWriting(Path dirPath, String basename, String ext) {
+    private static Pair<OutputStream, Path> openUniqueFileForWriting(Path dirPath, String basename, String ext) {
         if ( ! Files.isDirectory(dirPath) )
             throw new IllegalArgumentException("Not a directory: "+dirPath);
         if ( basename.contains("/") || basename.contains("\\") )
@@ -148,7 +153,8 @@ public class DatabaseOps {
         int x = 0 ;
         for(;;) {
             try {
-                return Files.newOutputStream(p, StandardOpenOption.CREATE_NEW);
+                OutputStream out = Files.newOutputStream(p, StandardOpenOption.CREATE_NEW); 
+                return Pair.create(out, p);
             } catch (AccessDeniedException ex)  { 
                 throw IOX.exception("Access denied", ex);
             } catch (FileAlreadyExistsException ex) {
@@ -193,7 +199,7 @@ public class DatabaseOps {
             Path db2 = db1.getParent().resolve(next);
             IOX.createDirectory(db2);
             Location loc2 = IOX.asLocation(db2);
-            System.out.printf("Compact %s -> %s\n", db1.getFileName(), db2.getFileName());
+            LOG.debug(String.format("Compact %s -> %s\n", db1.getFileName(), db2.getFileName()));
             
             compact(container, loc1, loc2);
         }

http://git-wip-us.apache.org/repos/asf/jena/blob/780d6716/tdb2/src/main/java/org/seaborne/tdb2/sys/StoreConnection.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/sys/StoreConnection.java b/tdb2/src/main/java/org/seaborne/tdb2/sys/StoreConnection.java
index f73032d..3ef596f 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/sys/StoreConnection.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/sys/StoreConnection.java
@@ -96,7 +96,11 @@ public class StoreConnection
         return sConn ;
     }
     
-    /** Stop managing all locations. Use with great care. */
+    /** 
+     * Stop managing all locations. 
+     * Use with extreme care.
+     * This is intended to support internal testing.
+     */
     public static synchronized void reset() {
         // Copy to avoid potential CME.
         Set<Location> x = new HashSet<>(cache.keySet()) ;

http://git-wip-us.apache.org/repos/asf/jena/blob/780d6716/tdb2/src/test/java/org/seaborne/tdb2/TS_Factory.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/TS_Factory.java b/tdb2/src/test/java/org/seaborne/tdb2/TS_Factory.java
index eb8fb1d..d963ad3 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/TS_Factory.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/TS_Factory.java
@@ -23,10 +23,13 @@ import org.junit.runners.Suite;
 
 @RunWith(Suite.class)
 @Suite.SuiteClasses( {
-    TestTDBFactory.class
+    TestDatabaseMgr.class
+    , TestTDBFactory.class
+
+    // Move to TS_Sys
+    , TestDatabaseOps.class
+    , TestDatabaseConnection.class
 })
 
 public class TS_Factory
-{
-
-}
+{}

http://git-wip-us.apache.org/repos/asf/jena/blob/780d6716/tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseConnection.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseConnection.java b/tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseConnection.java
new file mode 100644
index 0000000..a720b11
--- /dev/null
+++ b/tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseConnection.java
@@ -0,0 +1,68 @@
+/*
+ * 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.seaborne.tdb2;
+
+import static org.junit.Assert.assertNotSame ;
+import static org.junit.Assert.assertSame ;
+
+import org.apache.jena.atlas.lib.FileOps ;
+import org.apache.jena.sparql.core.DatasetGraph ;
+import org.junit.Test ;
+import org.seaborne.dboe.base.file.Location ;
+import org.seaborne.tdb2.sys.DatabaseConnection ;
+
+public class TestDatabaseConnection {
+    
+    @Test
+    public void testStoreConnectionTxn1() {
+        DatabaseConnection.reset() ;
+        // Only do disk things for test that need them (disk takes time!).
+        String DIRx = ConfigTest.getCleanDir() ;
+        Location DIR = Location.create(DIRx);
+        FileOps.clearDirectory(DIRx) ;
+        try {
+            DatasetGraph dg1 = DatabaseConnection.connectCreate(DIR).getDatasetGraph() ;
+            DatasetGraph dg2 = DatabaseConnection.connectCreate(DIR).getDatasetGraph() ;
+            assertSame(dg1, dg2) ;
+        }
+        finally {
+            FileOps.clearDirectory(DIRx) ;
+        }
+    }
+    
+    @Test
+    public void testStoreConnectionTxn2() {
+        // Named memory locations
+        DatabaseConnection.reset() ;
+        DatasetGraph dg1 = DatabaseConnection.connectCreate(Location.mem("FOO")).getDatasetGraph() ;
+        DatasetGraph dg2 = DatabaseConnection.connectCreate(Location.mem("FOO")).getDatasetGraph() ;
+        assertSame(dg1, dg2) ;
+    }
+    
+    @Test
+    public void testStoreConnectionTxn3() {
+        // Un-named memory locations
+        DatabaseConnection.reset() ;
+        DatasetGraph dg1 = DatabaseConnection.connectCreate(Location.mem()).getDatasetGraph() ;
+        DatasetGraph dg2 = DatabaseConnection.connectCreate(Location.mem()).getDatasetGraph() ;
+        assertNotSame(dg1, dg2) ;
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/780d6716/tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseMgr.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseMgr.java b/tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseMgr.java
new file mode 100644
index 0000000..5251b8b
--- /dev/null
+++ b/tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseMgr.java
@@ -0,0 +1,89 @@
+/*
+ *  Licensed 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.
+ *
+ *  See the NOTICE file distributed with this work for additional
+ *  information regarding copyright ownership.
+ */
+
+package org.seaborne.tdb2;
+
+import org.apache.jena.atlas.junit.BaseTest ;
+import org.apache.jena.atlas.lib.FileOps ;
+import org.apache.jena.sparql.core.DatasetGraph ;
+import org.apache.jena.sparql.core.Quad ;
+import org.apache.jena.sparql.sse.SSE ;
+import org.junit.Test ;
+import org.seaborne.dboe.base.file.Location ;
+import org.seaborne.dboe.jenax.Txn ;
+import org.seaborne.tdb2.sys.DatabaseConnection ;
+
+/** Test of DatabaseMgr - the DatasetGraph level API to TDB2 **/
+public class TestDatabaseMgr extends BaseTest
+{
+//    String DIRx = ConfigTest.getCleanDir() ;
+//    Location DIR = Location.create(DIRx);
+    
+    static Quad quad1 = SSE.parseQuad("(_ _:a <p> 1)") ;
+    static Quad quad2 = SSE.parseQuad("(_ <s> <p> 1)") ;
+    
+    @Test
+    public void testDatabaseMgr1() {
+        DatabaseConnection.reset() ;
+        DatasetGraph dg1 = DatabaseMgr.connectDatasetGraph(Location.mem("FOO")) ;
+        DatasetGraph dg2 = DatabaseMgr.connectDatasetGraph(Location.mem("FOO")) ;
+        Txn.executeWrite(dg1, ()->{
+            dg1.add(quad1) ;
+        }) ;
+        Txn.executeRead(dg2, ()->{
+            assertTrue(dg2.contains(quad1)) ;
+        }) ;
+    }
+    
+    @Test
+    public void testDatabaseMgr2() {
+        DatabaseConnection.reset() ;
+        // The unnamed location is unique each time.
+        DatasetGraph dg1 = DatabaseMgr.connectDatasetGraph(Location.mem()) ;
+        DatasetGraph dg2 = DatabaseMgr.connectDatasetGraph(Location.mem()) ;
+        Txn.executeWrite(dg1, ()->{
+            dg1.add(quad1) ;    
+        }) ;
+        Txn.executeRead(dg2, ()->{
+            assertFalse(dg2.contains(quad1)) ;
+        }) ;
+    }
+
+    @Test
+    public void testDatabaseMgrDisk() {
+        DatabaseConnection.reset() ;
+        // The named disk location
+        String DIRx = ConfigTest.getCleanDir() ;
+        Location LOC = Location.create(DIRx) ;
+        FileOps.clearDirectory(DIRx) ;
+        try {
+            DatasetGraph dg1 = DatabaseMgr.connectDatasetGraph(LOC) ;
+            DatasetGraph dg2 = DatabaseMgr.connectDatasetGraph(Location.create(LOC.getDirectoryPath())) ;
+            assertSame(dg1, dg2) ;
+            Txn.executeWrite(dg1, ()-> {
+                dg1.add(quad1) ;
+            }) ;
+            Txn.executeRead(dg2, ()-> {
+                assertTrue(dg2.contains(quad1)) ;
+            }) ;
+        }
+        finally {
+            FileOps.clearDirectory(DIRx) ;
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/780d6716/tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseOps.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseOps.java b/tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseOps.java
new file mode 100644
index 0000000..406758b
--- /dev/null
+++ b/tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseOps.java
@@ -0,0 +1,114 @@
+/*
+ *  Licensed 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.
+ *
+ *  See the NOTICE file distributed with this work for additional
+ *  information regarding copyright ownership.
+ */
+
+package org.seaborne.tdb2;
+
+import static org.junit.Assert.* ;
+
+import org.apache.commons.io.FileUtils ;
+import org.apache.jena.atlas.lib.FileOps ;
+import org.apache.jena.riot.RDFDataMgr ;
+import org.apache.jena.sparql.core.DatasetGraph ;
+import org.apache.jena.sparql.core.Quad ;
+import org.apache.jena.sparql.sse.SSE ;
+import org.junit.After ;
+import org.junit.Before ;
+import org.junit.Test ;
+import org.seaborne.dboe.base.file.Location ;
+import org.seaborne.dboe.jenax.Txn ;
+import org.seaborne.tdb2.repack.DatasetGraphSwitchable ;
+import org.seaborne.tdb2.store.DatasetGraphTDB ;
+import org.seaborne.tdb2.sys.DatabaseConnection ;
+import org.seaborne.tdb2.sys.IOX ;
+import org.seaborne.tdb2.sys.StoreConnection ;
+
+public class TestDatabaseOps
+{
+    static String DIRx = ConfigTest.getCleanDir() ;
+    static Location DIR = Location.create(DIRx);
+    
+    static Quad quad1 = SSE.parseQuad("(_ _:a <p> 1)") ;
+    static Quad quad2 = SSE.parseQuad("(_ <s> <p> 1)") ;
+    
+    @Before
+    public void before() {
+        FileUtils.deleteQuietly(IOX.asFile(DIR));
+        FileOps.ensureDir(DIR.getDirectoryPath());
+    }
+
+    @After  
+    public void after() {
+        DatabaseConnection.reset();
+        FileUtils.deleteQuietly(IOX.asFile(DIR));
+    }
+
+    @Test public void compact_1() {
+        DatasetGraph dsg = DatabaseMgr.connectDatasetGraph(DIR);
+        DatasetGraphSwitchable dsgs = (DatasetGraphSwitchable)dsg;
+        DatasetGraph dsg1 = dsgs.get();
+        Location loc1 = ((DatasetGraphTDB)dsg1).getLocation();
+        
+        Txn.executeWrite(dsg, ()-> {
+            dsg.add(quad1) ;
+            dsg.add(quad2) ;
+        }) ;
+        DatabaseMgr.compact(dsg);
+        DatasetGraph dsg2 = dsgs.get();
+        Location loc2 = ((DatasetGraphTDB)dsg2).getLocation();
+
+        assertNotEquals(dsg1, dsg2);
+        assertNotEquals(loc1, loc2);
+
+        Txn.executeRead(dsg, ()-> {
+            assertTrue(dsg.contains(quad1)) ;
+            assertTrue(dsg.contains(quad2)) ;
+        }) ;
+        
+        // Naughty!
+        // dsg1 was closed and expelled. We must reopen its storage only.
+        DatasetGraph dsgOld = StoreConnection.connectCreate(loc1).getDatasetGraph();
+        
+        Txn.executeWrite(dsgOld, ()->dsgOld.delete(quad1));
+
+        Txn.executeWrite(dsg, ()->assertTrue(dsg.contains(quad1)) );
+
+        Txn.executeWrite(dsg2, ()->assertTrue(dsg2.contains(quad1)) ) ;
+        
+        StoreConnection.expel(loc1, true);
+    }
+
+//    @Test public void compact_2() {
+//    }
+
+    @Test public void backup_1() {
+        DatasetGraph dsg = DatabaseMgr.connectDatasetGraph(DIR);
+        Txn.executeWrite(dsg, ()-> {
+            dsg.add(quad1) ;
+            dsg.add(quad2) ;
+        }) ;
+        String file1 = DatabaseMgr.backup(dsg);
+        DatasetGraph dsg2 = RDFDataMgr.loadDatasetGraph(file1);
+        Txn.executeRead(dsg, ()-> {
+            assertTrue(dsg.contains(quad2)) ;
+            assertEquals(2, dsg.getDefaultGraph().size());
+            assertTrue(dsg2.getDefaultGraph().isIsomorphicWith(dsg.getDefaultGraph()));
+        }) ;
+        String file2 = DatabaseMgr.backup(dsg);
+        assertNotEquals(file1, file2);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/780d6716/tdb2/src/test/java/org/seaborne/tdb2/TestTDBFactory.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/TestTDBFactory.java b/tdb2/src/test/java/org/seaborne/tdb2/TestTDBFactory.java
index fd6ba1c..7d1c780 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/TestTDBFactory.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/TestTDBFactory.java
@@ -19,103 +19,112 @@ package org.seaborne.tdb2;
 
 import org.apache.jena.atlas.junit.BaseTest ;
 import org.apache.jena.atlas.lib.FileOps ;
-import org.apache.jena.sparql.core.DatasetGraph ;
-import org.apache.jena.sparql.core.Quad ;
-import org.apache.jena.sparql.sse.SSE ;
-import org.junit.After ;
-import org.junit.Before ;
+import org.apache.jena.datatypes.xsd.XSDDatatype ;
+import org.apache.jena.query.Dataset ;
+import org.apache.jena.rdf.model.Literal ;
+import org.apache.jena.rdf.model.Property ;
+import org.apache.jena.rdf.model.Resource ;
+import org.apache.jena.rdf.model.ResourceFactory ;
 import org.junit.Test ;
 import org.seaborne.dboe.base.file.Location ;
 import org.seaborne.dboe.jenax.Txn ;
-import org.seaborne.tdb2.sys.StoreConnection ;
+import org.seaborne.tdb2.sys.DatabaseConnection ;
 
+/** Test of TDB2Factory - the Dataset level API to TDB2 **/
 public class TestTDBFactory extends BaseTest
 {
-    String DIRx = ConfigTest.getCleanDir() ;
-    Location DIR = Location.create(DIRx);
+    static Resource s1 = ResourceFactory.createResource("http://example/s2");
+    static Resource s2 = ResourceFactory.createResource();
+    static Property p  =  ResourceFactory.createProperty("http://example/ns#", "p");
+    static Literal  o1 =  ResourceFactory.createPlainLiteral("object");
+    static Literal  o2 =  ResourceFactory.createTypedLiteral("1", XSDDatatype.XSDinteger);
     
-    static Quad quad1 = SSE.parseQuad("(_ <s> <p> 1)") ;
-    static Quad quad2 = SSE.parseQuad("(_ <s> <p> 1)") ;
-    
-    @Before
-    public void before() {
-        FileOps.clearDirectory(DIRx) ;
-    }
-
-    @After
-    public void after() {
-        FileOps.clearDirectory(DIRx) ;
-    }
-
     @Test
     public void testTDBFactory1() {
-        StoreConnection.reset() ;
-        DatasetGraph dg1 = DatabaseMgr.connectDatasetGraph(Location.mem("FOO")) ;
-        DatasetGraph dg2 = DatabaseMgr.connectDatasetGraph(Location.mem("FOO")) ;
-        Txn.executeWrite(dg1, ()->{
-            dg1.add(quad1) ;
-        }) ;
-        Txn.executeRead(dg2, ()->{
-            assertTrue(dg2.contains(quad1)) ;
-        }) ;
+        DatabaseConnection.reset() ;
+        Dataset ds = TDB2Factory.connectDataset(Location.mem()) ;
+        test(ds);
     }
     
     @Test
     public void testTDBFactory2() {
-        StoreConnection.reset() ;
-        // The unnamed location is unique each time.
-        DatasetGraph dg1 = DatabaseMgr.connectDatasetGraph(Location.mem()) ;
-        DatasetGraph dg2 = DatabaseMgr.connectDatasetGraph(Location.mem()) ;
-        Txn.executeWrite(dg1, ()->{
-            dg1.add(quad1) ;    
-        }) ;
-        Txn.executeRead(dg2, ()->{
-            assertFalse(dg2.contains(quad1)) ;
-        }) ;
+        DatabaseConnection.reset() ;
+        Dataset ds = TDB2Factory.connectDataset(Location.mem("MEMORY")) ;
+        test(ds);
     }
-
-
+    
     @Test
-    public void testStoreConnectionTxn1() {
-        StoreConnection.reset() ;
-        DatasetGraph dg1 = StoreConnection.connectCreate(DIR).getDatasetGraph() ;
-        DatasetGraph dg2 = StoreConnection.connectCreate(DIR).getDatasetGraph() ;
-        assertSame(dg1, dg2) ;
+    public void testTDBFactory3() {
+        DatabaseConnection.reset() ;
+        // Only do disk things for tests that need them (disk takes time!).
+        String DIRx = ConfigTest.getCleanDir() ;
+        Location DIR = Location.create(DIRx);
+        try { 
+            FileOps.clearDirectory(DIRx) ;
+            Dataset ds = TDB2Factory.connectDataset(DIR) ;
+            test(ds);
+        } finally { FileOps.clearDirectory(DIRx) ; }
     }
     
     @Test
-    public void testStoreConnectionTxn2() {
-        // Named memory locations
-        StoreConnection.reset() ;
-        DatasetGraph dg1 = StoreConnection.connectCreate(Location.mem("FOO")).getDatasetGraph() ;
-        DatasetGraph dg2 = StoreConnection.connectCreate(Location.mem("FOO")).getDatasetGraph() ;
-        
-        assertSame(dg1, dg2) ;
+    public void testTDBFactory2DS_1() {
+        DatabaseConnection.reset() ;
+        Dataset ds1 = TDB2Factory.connectDataset(Location.mem("FOO")) ;
+        Dataset ds2 = TDB2Factory.connectDataset(Location.mem("FOO")) ;
+        Txn.executeWrite(ds1, ()->{
+            ds1.getDefaultModel().add(s1, p, o1);
+        });
+        Txn.executeRead(ds2, ()->{
+            assertTrue(ds2.getDefaultModel().contains(s1, p, o1));
+        });
     }
     
     @Test
-    public void testStoreConnectionTxn3() {
-        // Un-named memory locations
-        StoreConnection.reset() ;
-        DatasetGraph dg1 = StoreConnection.connectCreate(Location.mem()).getDatasetGraph() ;
-        DatasetGraph dg2 = StoreConnection.connectCreate(Location.mem()).getDatasetGraph() ;
-        
-        assertNotSame(dg1, dg2) ;
+    public void testTDBFactory2DS_2() {
+        DatabaseConnection.reset() ;
+        // The unnamed location is unique each time.
+        Dataset ds1 = TDB2Factory.connectDataset(Location.mem()) ;
+        Dataset ds2 = TDB2Factory.connectDataset(Location.mem()) ;
+        Txn.executeWrite(ds1, ()->{
+            ds1.getDefaultModel().add(s1, p, o1);
+        });
+        Txn.executeRead(ds2, ()->{
+            assertFalse(ds2.getDefaultModel().contains(s1, p, o1));
+        });
     }
-
+    
     @Test
-    public void testTDBFactoryDisk() {
-        StoreConnection.reset() ;
-        // The named disk location
-        Location loc = DIR;
-        DatasetGraph dg1 = DatabaseMgr.connectDatasetGraph(loc) ;
-        DatasetGraph dg2 = DatabaseMgr.connectDatasetGraph(Location.create(loc.getDirectoryPath()));
-        assertSame(dg1, dg2) ;
-        Txn.executeWrite(dg1, ()->{
-            dg1.add(quad1) ;    
-        }) ;
-        Txn.executeRead(dg2, ()->{
-            assertTrue(dg2.contains(quad1)) ;
-        }) ;
+    public void testTDBFactory2DS_3() {
+        DatabaseConnection.reset() ;
+        DatabaseConnection.reset() ;
+        String DIRx = ConfigTest.getCleanDir() ;
+        Location DIR = Location.create(DIRx);
+        try {
+            Dataset ds1 = TDB2Factory.connectDataset(DIR) ;
+            Dataset ds2 = TDB2Factory.connectDataset(DIR) ;
+            Txn.executeWrite(ds1, ()->{
+                ds1.getDefaultModel().add(s1, p, o1);
+            });
+            Txn.executeRead(ds2, ()->{
+                assertTrue(ds2.getDefaultModel().contains(s1, p, o1));
+            });
+        } finally { FileOps.clearDirectory(DIRx) ; }
+    }
+
+    private static void test(Dataset ds) {
+        Txn.executeWrite(ds, ()->{
+            ds.getDefaultModel().add(s1, p, o1);
+        });
+        Txn.executeRead(ds, ()->{
+            assertTrue(ds.getDefaultModel().contains(s1, p, o1));
+        });
+        Txn.executeWrite(ds, ()->{
+            ds.getDefaultModel().remove(s1, p, o1);
+            ds.getDefaultModel().add(s2, p, o2);
+        });
+        Txn.executeRead(ds, ()->{
+            assertFalse(ds.getDefaultModel().contains(s1, p, o1));
+            assertTrue(ds.getDefaultModel().contains(s2, p, o2));
+        });
     }
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/780d6716/tdb2/src/test/java/org/seaborne/tdb2/junit/TL.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/junit/TL.java b/tdb2/src/test/java/org/seaborne/tdb2/junit/TL.java
index 47ee5d0..cd58c36 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/junit/TL.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/junit/TL.java
@@ -25,7 +25,7 @@ import org.seaborne.dboe.base.file.Location ;
 import org.seaborne.tdb2.ConfigTest ;
 import org.seaborne.tdb2.DatabaseMgr ;
 import org.seaborne.tdb2.TDB2Factory ;
-import org.seaborne.tdb2.sys.StoreConnection ;
+import org.seaborne.tdb2.sys.DatabaseConnection ;
 import org.seaborne.tdb2.sys.SystemTDB ;
 
 /** Execute a test with a fresh dataset in a write transaction */
@@ -35,14 +35,14 @@ public class TL {
         Dataset dataset = createTestDataset() ;
         action.accept(dataset);
         releaseDataset(dataset) ;
-        StoreConnection.reset() ;
+        DatabaseConnection.reset() ;
     }
     
     public static void execMem(Consumer<Dataset> action) {
         Dataset dataset = createTestDatasetMem() ;
         action.accept(dataset);
-        releaseDataset(dataset) ;
-        StoreConnection.reset() ;
+        releaseDataset(dataset);
+        DatabaseConnection.reset();
     }
 
     // Or use these for @Before, @After style.
@@ -58,7 +58,7 @@ public class TL {
     
     public static void releaseDataset(Dataset dataset) {
         dataset.abort() ;
-        StoreConnection.reset() ;
+        DatabaseConnection.reset() ;
     }
 
     public static Dataset createTestDataset() {

http://git-wip-us.apache.org/repos/asf/jena/blob/780d6716/tdb2/src/test/java/org/seaborne/tdb2/store/AbstractTestStoreConnectionBasics.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/store/AbstractTestStoreConnectionBasics.java b/tdb2/src/test/java/org/seaborne/tdb2/store/AbstractTestStoreConnectionBasics.java
index a78ab67..854402b 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/store/AbstractTestStoreConnectionBasics.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/store/AbstractTestStoreConnectionBasics.java
@@ -26,6 +26,7 @@ import org.junit.Before ;
 import org.junit.Test ;
 import org.seaborne.dboe.base.file.Location ;
 import org.seaborne.dboe.jenax.Txn ;
+import org.seaborne.tdb2.sys.DatabaseConnection ;
 import org.seaborne.tdb2.sys.StoreConnection ;
 
 /** StoreConnection and transactions - basic wiring.
@@ -52,7 +53,7 @@ public abstract class AbstractTestStoreConnectionBasics extends BaseTest
     
     @Before public void before()
     {
-        StoreConnection.reset() ;
+        DatabaseConnection.reset() ;
         location = getLocation() ;
     }
 


[41/50] [abbrv] jena git commit: JENA-1396: Merge //github/afs/mantis as subdirectory jena-db/

Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/rewriter/BPlusTreeRewriter.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/rewriter/BPlusTreeRewriter.java
index 0000000,0000000..16a535b
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/rewriter/BPlusTreeRewriter.java
@@@ -1,0 -1,0 +1,361 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.bplustree.rewriter ;
++
++import static org.seaborne.dboe.trans.bplustree.rewriter.BPlusTreeRewriterUtils.divider ;
++import static org.seaborne.dboe.trans.bplustree.rewriter.BPlusTreeRewriterUtils.printIndexBlocks ;
++import static org.seaborne.dboe.trans.bplustree.rewriter.BPlusTreeRewriterUtils.summarizeDataBlocks ;
++
++import java.util.Iterator ;
++import java.util.List ;
++
++import org.apache.jena.atlas.iterator.Iter ;
++import org.apache.jena.atlas.iterator.IteratorWithBuffer ;
++import org.apache.jena.atlas.lib.Pair ;
++import org.seaborne.dboe.base.block.BlockMgr ;
++import org.seaborne.dboe.base.buffer.PtrBuffer ;
++import org.seaborne.dboe.base.buffer.RecordBuffer ;
++import org.seaborne.dboe.base.file.BufferChannel ;
++import org.seaborne.dboe.base.record.Record ;
++import org.seaborne.dboe.base.record.RecordFactory ;
++import org.seaborne.dboe.base.recordbuffer.RecordBufferPage ;
++import org.seaborne.dboe.base.recordbuffer.RecordBufferPageMgr ;
++import org.seaborne.dboe.trans.bplustree.* ;
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++public class BPlusTreeRewriter {
++    static private Logger log         = LoggerFactory.getLogger(BPlusTreeRewriter.class) ;
++
++    static boolean        rebalance   = true ;
++    static boolean        debug       = false ;
++    static boolean        materialize = debug ;
++
++    // Process:
++    // 1/ Take a stream of records and create leaves.
++    // Emit the RecordBufferPage (B+Tree leaves).
++    // 2/ Take a stream of RecordBufferPage and create the first level of
++    // branches.
++    // 3/ Take each branch level and create upper branches until root hit.
++    // 4/ Copy root block to root real location.
++
++    // --------------------------------
++
++    /**
++     * Given a stream of records and details of the B+Tree to build, go and
++     * build it.
++     * 
++     * @return A newly built BPlusTree
++     */
++    public static BPlusTree packIntoBPlusTree(Iterator<Record> iterRecords, BPlusTreeParams bptParams, RecordFactory recordFactory,
++                                              BufferChannel bptState, BlockMgr blkMgrNodes, BlockMgr blkMgrRecords) {
++        // **** Attach to storage.
++        // Small caches as we mostly work on a block then move on.
++        // Only read behind and look ahead actually do any work on an existing
++        // block.
++        // (check this by rerunning with different cache sizes).
++
++        if ( !iterRecords.hasNext() )
++            // No records. Just return a B+Tree.
++            return BPlusTreeFactory.createNonTxn(bptParams, bptState, blkMgrNodes, blkMgrRecords) ;
++
++        
++        // Initial B+tree needed to carry parameters around - not legal at this point.
++        BPlusTree bpt2 = BPT.createRootOnlyBPTree(bptParams, bptState, blkMgrNodes, blkMgrRecords) ;
++        // Get the root node.
++        // We will use this slot later and write in the correct root.
++        // The root has to be block zero currently.
++        BPTreeNode root = bpt2.getNodeManager().getWrite(BPlusTreeParams.RootId, BPlusTreeParams.RootParent) ;
++        // ******** Pack data blocks.
++        Iterator<Pair<Integer, Record>> iter = writePackedDataBlocks(iterRecords, bpt2) ;
++        
++        // ******** Index layer
++        // Loop until one block only.
++        // Never zero blocks.
++        // Output is a single pair pointing to the root - but the root is in the
++        // wrong place.
++
++        boolean leafLayer = true ;
++        while (true) {
++            iter = genTreeLevel(iter, bpt2, leafLayer) ;
++            // Advances iter.
++            IteratorWithBuffer<Pair<Integer, Record>> iter2 = new IteratorWithBuffer<>(iter, 2) ;
++            boolean singleBlock = (iter2.peek(1) == null) ;
++            // Having peeked ahead, use the real stream.
++            iter = iter2 ;
++            if ( singleBlock )
++                break ;
++            leafLayer = false ;
++        }
++
++        // ******** Put root in right place.
++        Pair<Integer, Record> pair = iter.next() ;
++        if ( iter.hasNext() ) {
++            log.error("**** Building index layers didn't result in a single block") ;
++            return null ;
++        }
++        fixupRoot(root, pair, bpt2) ;
++        // ****** Finish the tree.
++        //bpt2.getStateManager().
++        blkMgrNodes.sync() ;
++        blkMgrRecords.sync() ;
++        return bpt2 ;
++    }
++
++    // **** data block phase
++
++    /** Pack record blocks into linked RecordBufferPages */
++    private static Iterator<Pair<Integer, Record>> writePackedDataBlocks(Iterator<Record> records, final BPlusTree bpt) {
++        if ( debug ) {
++            divider() ;
++            System.out.println("---- Data level") ;
++        }
++
++        final RecordBufferPageMgr mgr = bpt.getRecordsMgr().getRecordBufferPageMgr() ;
++        Iterator<RecordBufferPage> iter = new RecordBufferPageLinker(new RecordBufferPagePacker(records, mgr)) ;
++
++        // Write and convert to split pairs.
++        Iterator<Pair<Integer, Record>> iter2 = Iter.map(iter, rbp-> {
++            mgr.put(rbp) ;
++            Record r = rbp.getRecordBuffer().getHigh() ;
++            r = bpt.getRecordFactory().createKeyOnly(r) ;
++            return new Pair<>(rbp.getId(), r) ;
++        }) ;
++
++        if ( debug ) {
++            if ( rebalance )
++                System.out.println("Before rebalance (data)") ;
++            iter2 = summarizeDataBlocks(iter2, bpt.getRecordsMgr().getRecordBufferPageMgr()) ;
++            // iter2 = printDataBlocks(iter2,
++            // bpt.getRecordsMgr().getRecordBufferPageMgr()) ;
++        }
++
++        if ( rebalance )
++            iter2 = new RebalenceDataEnd(iter2, bpt) ;
++
++        // Testing - materialize - debug wil have done this
++        if ( materialize && !debug )
++            iter2 = Iter.toList(iter2).iterator() ;
++
++        if ( debug && rebalance ) {
++            System.out.println("After rebalance (data)") ;
++            iter2 = summarizeDataBlocks(iter2, bpt.getRecordsMgr().getRecordBufferPageMgr()) ;
++            // iter2 = printDataBlocks(iter2,
++            // bpt.getRecordsMgr().getRecordBufferPageMgr()) ;
++        }
++        return iter2 ;
++    }
++
++    private static class RebalenceDataEnd extends RebalenceBase {
++        private RecordBufferPageMgr mgr ;
++        private BPlusTree           bpt ;
++
++        public RebalenceDataEnd(Iterator<Pair<Integer, Record>> iter, BPlusTree bpt) {
++            super(iter) ;
++            this.bpt = bpt ;
++        }
++
++        @Override
++        protected Record rebalance(int id1, Record r1, int id2, Record r2) {
++            RecordBufferPageMgr mgr = bpt.getRecordsMgr().getRecordBufferPageMgr() ;
++            RecordBufferPage page1 = mgr.getWrite(id1) ;
++            RecordBufferPage page2 = mgr.getWrite(id2) ;
++
++            // Wrong calculatation.
++            for ( int i = page2.getCount() ; i < page1.getMaxSize() / 2 ; i++ ) {
++                // shiftOneup(node1, node2) ;
++                Record r = page1.getRecordBuffer().getHigh() ;
++                page1.getRecordBuffer().removeTop() ;
++
++                page2.getRecordBuffer().add(0, r) ;
++            }
++
++            mgr.put(page1) ;
++            mgr.put(page2) ;
++
++            Record splitPoint = page1.getRecordBuffer().getHigh() ;
++            splitPoint = bpt.getRecordFactory().createKeyOnly(splitPoint) ;
++            // Record splitPoint = node1.maxRecord() ;
++            return splitPoint ;
++        }
++    }
++
++    // ---------------------------------------------------------------------------------------------
++
++    // **** tree block phase
++
++    /*
++     * Status: need to address the parent issue - how to set the parent as we
++     * work up the BPT. One way is to work in memory and assume that there is
++     * enough room for the B+Tree nodes (not the record nodes).
++     * 
++     * Another way is to rely on the caching and two-pass each new layer to fix
++     * up parents.
++     * 
++     * 3rd way - parents aren't actually used in B+Trees. Set the BPT code to
++     * ignore parent of -2. Always set the new nodes to -2 for a rebuild.
++     */
++
++    // Idea a layer processor for BPT nodes has a sink that is the parent.
++    // Need to rebal the last two blocks of a level.
++    // Same for data blocks.
++
++    // ---- Block stream to BTreeNodeStream.
++
++    private static Iterator<Pair<Integer, Record>> genTreeLevel(Iterator<Pair<Integer, Record>> iter, BPlusTree bpt, boolean leafLayer) {
++        if ( debug ) {
++            divider() ;
++            System.out.println("---- Index level") ;
++            List<Pair<Integer, Record>> x = Iter.toList(iter) ;
++            System.out.println(x) ;
++            iter = x.iterator() ;
++        }
++
++        Iterator<Pair<Integer, Record>> iter2 = new BPTreeNodeBuilder(iter, bpt.getNodeManager(), leafLayer, bpt.getRecordFactory()) ;
++
++        if ( debug ) {
++            if ( rebalance )
++                System.out.println("Before rebalance (index)") ;
++            // iter2 = summarizeIndexBlocks(iter2, bpt.getNodeManager()) ;
++            iter2 = printIndexBlocks(iter2, bpt.getNodeManager()) ;
++        }
++
++        if ( rebalance )
++            iter2 = new RebalenceIndexEnd(iter2, bpt, leafLayer) ;
++
++        if ( materialize && !debug )
++            iter2 = Iter.toList(iter2).iterator() ;
++
++        if ( debug && rebalance ) {
++            System.out.println("After rebalance (index)") ;
++            // iter2 = summarizeIndexBlocks(iter2, bpt.getNodeManager()) ;
++            iter2 = printIndexBlocks(iter2, bpt.getNodeManager()) ;
++        }
++        return iter2 ;
++    }
++
++    private abstract static class RebalenceBase extends IteratorWithBuffer<Pair<Integer, Record>> {
++        protected RebalenceBase(Iterator<Pair<Integer, Record>> iter) {
++            super(iter, 2) ;
++        }
++
++        @Override
++        protected final void endReachedInner() {
++            Pair<Integer, Record> pair1 = peek(0) ;
++            Pair<Integer, Record> pair2 = peek(1) ;
++            if ( pair1 == null || pair2 == null )
++                // Insufficient blocks to repack.
++                return ;
++
++            if ( debug )
++                System.out.printf("Rebalance: %s %s\n", pair1, pair2) ;
++            Record newSplitPoint = rebalance(pair1.car(), pair1.cdr(), pair2.car(), pair2.cdr()) ;
++            // Needed??
++            if ( newSplitPoint != null ) {
++                if ( debug )
++                    System.out.println("Reset split point: " + pair1.cdr() + " => " + newSplitPoint) ;
++                pair1 = new Pair<>(pair1.car(), newSplitPoint) ;
++                if ( debug )
++                    System.out.printf("   %s %s\n", pair1, pair2) ;
++                set(0, pair1) ;
++            }
++        }
++
++        protected abstract Record rebalance(int id1, Record r1, int id2, Record r2) ;
++    }
++
++    private static class RebalenceIndexEnd extends RebalenceBase {
++        private BPlusTree bpt ;
++
++        public RebalenceIndexEnd(Iterator<Pair<Integer, Record>> iter, BPlusTree bpt, boolean leafLayer) {
++            super(iter) ;
++            this.bpt = bpt ;
++        }
++
++        @Override
++        protected Record rebalance(int id1, Record r1, int id2, Record r2) {
++            BPTreeNodeMgr mgr = bpt.getNodeManager() ;
++            BPTreeNode node1 = mgr.getWrite(id1) ;
++            BPTreeNode node2 = mgr.getWrite(id2) ;
++
++            // rebalence
++            // ** Need rebalance of data leaf layer.
++            int count = node2.getCount() ;
++            if ( count >= bpt.getParams().getMinRec() )
++                return null ;
++
++            Record splitPoint = r1 ;
++
++            // Shift up all in one go and use .set.
++            // Convert to block move ; should be code in BPTreeNode to do this
++            // (insert).
++            for ( int i = count ; i < bpt.getParams().getMinRec() ; i++ ) {
++
++                Record r = splitPoint ;
++
++                // shiftOneup(node1, node2) ;
++                int ptr = node1.getPtrBuffer().getHigh() ;
++                splitPoint = node1.getRecordBuffer().getHigh() ;
++
++                node1.getPtrBuffer().removeTop() ;
++                node1.getRecordBuffer().removeTop() ;
++                node1.setCount(node1.getCount() - 1) ;
++
++                node2.getPtrBuffer().add(0, ptr) ;
++                node2.getRecordBuffer().add(0, r) ;
++                node2.setCount(node2.getCount() + 1) ;
++
++                // Need high of moved substree.
++
++                if ( debug )
++                    System.out.printf("-- Shift up: %d %s\n", ptr, r) ;
++            }
++            mgr.put(node1) ;
++            mgr.put(node2) ;
++
++            return splitPoint ;
++        }
++    }
++
++    private static void fixupRoot(BPTreeNode root, Pair<Integer, Record> pair, BPlusTree bpt2) {
++        root.getPtrBuffer().clear() ;
++        root.getRecordBuffer().clear() ;
++
++        if ( BPlusTreeRewriter.debug ) {
++            divider() ;
++            System.out.printf("** Process root: %s\n", pair) ;
++        }
++
++        // Node or records?
++        // BPTreeNode => BPTree copy.
++        BPTreeNode node = bpt2.getNodeManager().getRead(pair.car(), BPlusTreeParams.RootParent) ;
++        copyBPTreeNode(node, root, bpt2) ;
++        bpt2.getNodeManager().release(node) ;
++        bpt2.getNodeManager().write(root);
++    }
++
++    private static void copyBPTreeNode(BPTreeNode nodeSrc, BPTreeNode nodeDst, BPlusTree bpt2) {
++        PtrBuffer pBuff = nodeSrc.getPtrBuffer() ;
++        pBuff.copy(0, nodeDst.getPtrBuffer(), 0, pBuff.getSize()) ;
++        RecordBuffer rBuff = nodeSrc.getRecordBuffer() ;
++        rBuff.copy(0, nodeDst.getRecordBuffer(), 0, rBuff.getSize()) ;
++        nodeDst.setCount(nodeSrc.getCount()) ;
++        nodeDst.setIsLeaf(nodeSrc.isLeaf()) ;
++        bpt2.getNodeManager().put(nodeDst) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/rewriter/BPlusTreeRewriterUtils.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/rewriter/BPlusTreeRewriterUtils.java
index 0000000,0000000..77cfdb2
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/rewriter/BPlusTreeRewriterUtils.java
@@@ -1,0 -1,0 +1,104 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.bplustree.rewriter ;
++
++import java.util.Iterator ;
++import java.util.List ;
++
++import org.apache.jena.atlas.iterator.Iter ;
++import org.apache.jena.atlas.lib.Pair ;
++import org.seaborne.dboe.base.record.Record ;
++import org.seaborne.dboe.base.recordbuffer.RecordBufferPage ;
++import org.seaborne.dboe.base.recordbuffer.RecordBufferPageMgr ;
++import org.seaborne.dboe.trans.bplustree.BPTreeNode ;
++import org.seaborne.dboe.trans.bplustree.BPTreeNodeMgr ;
++import org.seaborne.dboe.trans.bplustree.BPlusTreeParams ;
++
++class BPlusTreeRewriterUtils {
++    static String divider     = "----------------------------------------" ;
++    static String nextDivider = null ;
++
++    static Iterator<Pair<Integer, Record>> summarizeDataBlocks(Iterator<Pair<Integer, Record>> iter, RecordBufferPageMgr recordPageMgr) {
++        divider() ;
++        List<Pair<Integer, Record>> pairs = Iter.toList(iter) ;
++        System.out.println("summarizeDataBlocks: " + pairs) ;
++        for ( Pair<Integer, Record> pair : pairs ) {
++            RecordBufferPage rbp = recordPageMgr.getRead(pair.car()) ;
++            System.out.printf("%s -- RecordBufferPage[id=%d,link=%d] (%d) -> [%s]\n", pair, rbp.getId(), rbp.getLink(), rbp.getCount(),
++                              rbp.getRecordBuffer().getHigh()) ;
++            recordPageMgr.release(rbp) ;
++        }
++        return pairs.iterator() ;
++    }
++
++    static Iterator<Pair<Integer, Record>> summarizeIndexBlocks(Iterator<Pair<Integer, Record>> iter2, BPTreeNodeMgr bptNodeMgr) {
++        divider() ;
++        List<Pair<Integer, Record>> pairs = Iter.toList(iter2) ;
++        for ( Pair<Integer, Record> pair : pairs ) {
++            BPTreeNode bpNode = bptNodeMgr.getRead(pair.car(), BPlusTreeParams.RootParent) ;
++
++            String hr = "null" ;
++            if ( !bpNode.getRecordBuffer().isEmpty() )
++                hr = bpNode.getRecordBuffer().getHigh().toString() ;
++
++            System.out.printf("%s -- BPTreeNode: %d (%d) -> [%s]\n", pair, bpNode.getId(), bpNode.getCount(), hr) ;
++            bptNodeMgr.release(bpNode) ;
++        }
++        return pairs.iterator() ;
++    }
++
++    private static Iterator<Pair<Integer, Record>> printDataBlocks(Iterator<Pair<Integer, Record>> iter, RecordBufferPageMgr recordPageMgr) {
++        divider() ;
++        List<Pair<Integer, Record>> pairs = Iter.toList(iter) ;
++        System.out.printf(">>Packed data blocks\n") ;
++        for ( Pair<Integer, Record> pair : pairs ) {
++            System.out.printf("  %s\n", pair) ;
++            RecordBufferPage rbp = recordPageMgr.getRead(pair.car()) ;
++            // System.out.printf("RecordBufferPage[id=%d,link=%d] %d\n",
++            // rbp.getId(), rbp.getLink(), rbp.getCount() ) ;
++            System.out.println(rbp) ;
++            recordPageMgr.release(rbp) ;
++        }
++        System.out.printf("<<Packed data blocks\n") ;
++        System.out.printf("Blocks: %d\n", pairs.size()) ;
++        return pairs.iterator() ;
++    }
++
++    static Iterator<Pair<Integer, Record>> printIndexBlocks(Iterator<Pair<Integer, Record>> iter2, BPTreeNodeMgr bptNodeMgr) {
++        divider() ;
++        List<Pair<Integer, Record>> pairs = Iter.toList(iter2) ;
++        System.out.printf(">>Packed index blocks\n") ;
++        for ( Pair<Integer, Record> pair : pairs ) {
++            System.out.printf("  %s\n", pair) ;
++            BPTreeNode bpNode = bptNodeMgr.getRead(pair.car(), BPlusTreeParams.RootParent) ;
++            bpNode.setIsLeaf(true) ;
++            System.out.printf("BPTreeNode: %d\n", bpNode.getId()) ;
++            System.out.println(bpNode) ;
++            bptNodeMgr.release(bpNode) ;
++        }
++        System.out.printf("<<Packed index blocks\n") ;
++        return pairs.iterator() ;
++    }
++
++    static void divider() {
++        if ( nextDivider != null )
++            System.out.println(nextDivider) ;
++        nextDivider = divider ;
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/rewriter/RecordBufferPageLinker.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/rewriter/RecordBufferPageLinker.java
index 0000000,0000000..42c2b9c
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/rewriter/RecordBufferPageLinker.java
@@@ -1,0 -1,0 +1,81 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.bplustree.rewriter ;
++
++import java.util.Iterator ;
++import java.util.NoSuchElementException ;
++
++import org.apache.jena.atlas.iterator.PeekIterator ;
++import org.seaborne.dboe.base.recordbuffer.RecordBufferPage ;
++
++/**
++ * From a stream of RecordBufferPage, manage the link fields. That is, be a one
++ * slot delay so that the "link" field can point to the next page. Be careful
++ * about the last block.
++ *
++ */
++class RecordBufferPageLinker implements Iterator<RecordBufferPage> {
++    PeekIterator<RecordBufferPage> peekIter ;
++
++    RecordBufferPage               slot = null ;
++
++    RecordBufferPageLinker(Iterator<RecordBufferPage> iter) {
++        if ( !iter.hasNext() ) {
++            peekIter = null ;
++            return ;
++        }
++
++        peekIter = new PeekIterator<>(iter) ;
++    }
++
++    @Override
++    public boolean hasNext() {
++        if ( slot != null )
++            return true ;
++
++        if ( peekIter == null )
++            return false ;
++
++        if ( !peekIter.hasNext() ) {
++            peekIter = null ;
++            return false ;
++        }
++
++        slot = peekIter.next() ;
++        RecordBufferPage nextSlot = peekIter.peek() ;
++        // If null, no slot ahead so no linkage field to set.
++        if ( nextSlot != null )
++            // Set the slot to the id of the next one
++            slot.setLink(nextSlot.getId()) ;
++        return true ;
++    }
++
++    @Override
++    public RecordBufferPage next() {
++        if ( !hasNext() )
++            throw new NoSuchElementException() ;
++        RecordBufferPage rbp = slot ;
++        slot = null ;
++        return rbp ;
++    }
++
++    @Override
++    public void remove() {
++        throw new UnsupportedOperationException() ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/rewriter/RecordBufferPagePacker.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/rewriter/RecordBufferPagePacker.java
index 0000000,0000000..bb0485a
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/rewriter/RecordBufferPagePacker.java
@@@ -1,0 -1,0 +1,90 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.bplustree.rewriter ;
++
++import java.util.Iterator ;
++import java.util.NoSuchElementException ;
++
++import org.seaborne.dboe.base.buffer.RecordBuffer ;
++import org.seaborne.dboe.base.record.Record ;
++import org.seaborne.dboe.base.recordbuffer.RecordBufferPage ;
++import org.seaborne.dboe.base.recordbuffer.RecordBufferPageMgr ;
++
++/**
++ * Iterate over a stream of records, packing them into RecordBufferPage -- the
++ * leaf of a B+Tree This class does not write the blocks back to the block
++ * manager. This class does allocate block ids and blocks.
++ * 
++ * @see RecordBufferPageLinker
++ */
++
++class RecordBufferPagePacker implements Iterator<RecordBufferPage> {
++    Iterator<Record>    records          = null ;
++    RecordBufferPage    recordBufferPage = null ;
++    RecordBufferPageMgr rbMgr            = null ;
++
++    RecordBufferPagePacker(Iterator<Record> records, RecordBufferPageMgr rbMgr) {
++        this.records = records ;
++        this.rbMgr = rbMgr ;
++    }
++
++    @Override
++    public boolean hasNext() {
++        if ( recordBufferPage == null ) {
++            if ( records == null )
++                return false ;
++
++            if ( !records.hasNext() ) {
++                records = null ;
++                return false ;
++            }
++            // At least one record to be processed.
++            // No pending RecordBufferPage
++            // ==> There will be a RecordBufferPage to yield.
++
++            // int id = rbMgr.allocateId() ;
++            // //System.out.println("Allocate : "+id) ;
++            recordBufferPage = rbMgr.create() ;
++
++            RecordBuffer rb = recordBufferPage.getRecordBuffer() ;
++            while (!rb.isFull() && records.hasNext()) {
++                Record r = records.next() ;
++                rb.add(r) ;
++            }
++            if ( !records.hasNext() )
++                records = null ;
++            return true ;
++        }
++        return true ;
++
++    }
++
++    @Override
++    public RecordBufferPage next() {
++        if ( !hasNext() )
++            throw new NoSuchElementException() ;
++        RecordBufferPage rbp = recordBufferPage ;
++        recordBufferPage = null ;
++        return rbp ;
++    }
++
++    @Override
++    public void remove() {
++        throw new UnsupportedOperationException() ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/data/TransBinaryDataFile.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/data/TransBinaryDataFile.java
index 0000000,0000000..569db33
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/data/TransBinaryDataFile.java
@@@ -1,0 -1,0 +1,237 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.data;
++
++import java.nio.ByteBuffer ;
++import java.util.concurrent.atomic.AtomicLong ;
++
++import org.apache.jena.atlas.RuntimeIOException ;
++import org.apache.jena.atlas.io.IO ;
++import org.apache.jena.query.ReadWrite ;
++import org.seaborne.dboe.base.file.BinaryDataFile ;
++import org.seaborne.dboe.base.file.BufferChannel ;
++import org.seaborne.dboe.transaction.txn.* ;
++
++/** Transactional {@link BinaryDataFile}.
++ *  An binary file that is append-only and allows only one writer at a time.
++ *  All readers see the file up to the last commit point at the time 
++ *  they started.  The sole writer sees more of the file.
++ */
++
++public class TransBinaryDataFile extends TransactionalComponentLifecycle<TransBinaryDataFile.TxnBinFile>
++    implements BinaryDataFile {
++
++    /*
++     * The file is written to as we go along so abort requires some action. We
++     * can't recover from just the file, without any redo or undo recovery
++     * action.
++     */
++    
++    private final FileState stateMgr ;
++
++    // The current committed position and the limit as seen by readers.
++    // This is also the abort point.
++    private final AtomicLong committedLength ;
++    
++    // The per thread runtime state
++    static class TxnBinFile {
++        final long length ; 
++
++        TxnBinFile(long length) {
++            this.length = length ;
++        }
++    }
++
++    // Prepare record
++    static class FileState extends StateMgrData {
++        FileState(BufferChannel bufferChannel, long length, long position) {
++            super(bufferChannel, length, position) ;
++        }
++        private static int idxLength = 0 ; 
++        long length()               { return get(idxLength) ; }
++        void length(long len)       { set(idxLength, len) ; } 
++    }
++
++    private final BinaryDataFile binFile ;
++    
++    /** Create a transactional BinaryDataFile over a base implementation.
++     *  The base file must provide thread-safe operation.   
++     */
++    public TransBinaryDataFile(BinaryDataFile binFile, ComponentId cid, BufferChannel bufferChannel) {
++        super(cid) ;
++        stateMgr = new FileState(bufferChannel, 0L, 0L) ;
++        this.binFile = binFile ;
++        if ( ! binFile.isOpen() )
++            binFile.open();
++        // Internal state may be updated by recovery. Start by
++        // setting to the "clean start" settings.
++        committedLength = new AtomicLong(binFile.length()) ;
++    }
++    
++    private boolean recoveryAction = false ; 
++
++    @Override
++    public void startRecovery() {
++        recoveryAction = false ;
++    }
++    
++    @Override
++    public void recover(ByteBuffer ref) {
++        stateMgr.setState(ref);
++        committedLength.set(stateMgr.length()) ;
++        recoveryAction = true ;
++    }
++
++    @Override
++    public void finishRecovery() {
++        if ( recoveryAction ) {
++            long length = committedLength.get() ;
++            binFile.truncate(length) ;
++            binFile.sync();
++            committedLength.set(length) ; 
++        }
++    }
++    
++    @Override
++    public void cleanStart() { }
++
++    @Override
++    protected TxnBinFile _begin(ReadWrite readWrite, TxnId txnId) {
++        // Atomic read across the two because it's called from within 
++        // TransactionCoordinator.begin$ where there is a lock.
++        return createState() ;
++    }
++    
++    private TxnBinFile createState() {
++        long xLength = committedLength.get() ;
++        return new TxnBinFile(xLength) ;
++    }
++
++    @Override
++    protected TxnBinFile _promote(TxnId txnId, TxnBinFile state) {
++        return createState() ;
++    }
++
++    @Override
++    protected ByteBuffer _commitPrepare(TxnId txnId, TxnBinFile state) {
++        // Force to disk but do not set the on disk state to record that.
++        binFile.sync();
++        stateMgr.length(binFile.length()) ;
++        return stateMgr.getState() ;
++    }
++
++    @Override
++    protected void _commit(TxnId txnId, TxnBinFile state) {
++        if ( isWriteTxn() ) {
++            // Force to disk happens in _commitPrepare
++            stateMgr.writeState();
++            // Move visible commit point forward (not strictly necessary - transaction is ending. 
++            committedLength.set(binFile.length()) ;
++        }
++    }
++
++    @Override
++    protected void _commitEnd(TxnId txnId, TxnBinFile state) {
++    }
++
++    @Override
++    protected void _abort(TxnId txnId, TxnBinFile state) {
++        if ( isWriteTxn() ) {
++            binFile.truncate(committedLength.get()) ;
++            binFile.sync() ;
++        }
++    }
++
++    @Override
++    protected void _complete(TxnId txnId, TxnBinFile state) {}
++
++    @Override
++    protected void _shutdown() {}
++    
++    private void checkBoundsReader(long requestedPoint, TxnBinFile state) { }
++    
++    @Override
++    public void open() {
++        if ( ! binFile.isOpen() )
++            binFile.open();
++    }
++
++    @Override
++    public boolean isOpen() {
++        return binFile.isOpen() ;
++    }
++
++    @Override
++    public int read(long posn, byte[] b, int start, int length) {
++        checkTxn();
++        if ( isReadTxn() )
++            checkRead(posn) ;
++        return binFile.read(posn, b, start, length) ;
++    }
++
++    private void checkRead(long posn) {
++        if ( posn > getDataState().length )
++            IO.exception("Out of bounds: (limit "+getDataState().length+")"+posn) ;
++    }
++
++    @Override
++    public long write(byte[] b, int start, int length) {
++        checkWriteTxn() ;
++        return binFile.write(b, start, length);
++    }
++
++    /** 
++     * Truncate only supported for an abort - this transactional version of
++     * BinaryDataFile will not truncate to earlier than the commited length.
++     */
++    @Override
++    public void truncate(long size) {
++        checkWriteTxn();
++        TxnBinFile state = getDataState() ;
++        if ( size < state.length )
++            throw new RuntimeIOException("truncate("+size+") to smaller than commited length "+state.length) ;
++        binFile.truncate(size) ;
++    }
++
++    @Override
++    public void sync() {
++        // No-op in transactional mode.
++        checkWriteTxn();
++    }
++
++    @Override
++    public void close() {
++        binFile.close() ;
++    }
++
++    @Override
++    public long length() {
++        super.checkTxn();
++        if ( isReadTxn() )
++            // Reader view.
++            return getDataState().length ;
++        return binFile.length() ;
++    }
++    
++    @Override
++    public boolean isEmpty() {
++        super.checkTxn();
++        return binFile.isEmpty() ;
++    }    
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/data/TransBlob.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/data/TransBlob.java
index 0000000,0000000..a2dfbf3
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/data/TransBlob.java
@@@ -1,0 -1,0 +1,209 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.data;
++
++import java.nio.ByteBuffer ;
++import java.util.concurrent.atomic.AtomicReference ;
++
++import org.apache.jena.atlas.RuntimeIOException ;
++import org.apache.jena.atlas.lib.Bytes ;
++import org.apache.jena.query.ReadWrite ;
++import org.seaborne.dboe.base.file.BufferChannel ;
++import org.seaborne.dboe.transaction.txn.ComponentId ;
++import org.seaborne.dboe.transaction.txn.TransactionalComponentLifecycle ;
++import org.seaborne.dboe.transaction.txn.TxnId ;
++
++/** Manage a single binary (not too large) object.
++ * It is written and read from a file in one action, 
++ * so changes completely replace the original contents.
++ * The whole object is written to the journal during prepare.  
++ */
++public class TransBlob extends TransactionalComponentLifecycle<TransBlob.BlobState> {
++
++    // The last commited state.
++    // Immutable ByteBuffer.
++    private final AtomicReference<ByteBuffer> blobRef = new AtomicReference<>() ;
++    private final BufferChannel file ;
++
++    static class BlobState {
++        boolean hasChanged = false; 
++        ByteBuffer $txnBlob ;
++        
++        BlobState(ByteBuffer bb) {
++            setByteBuffer(bb) ;
++        }
++        void setByteBuffer(ByteBuffer bb) {
++            $txnBlob = bb ;
++            // Could compare - seems like added complexity.
++            hasChanged = true;
++        }
++
++        ByteBuffer getByteBuffer() { return $txnBlob ; } 
++    }
++    
++    public TransBlob(ComponentId cid, BufferChannel file) {
++        super(cid) ;
++        this.file = file ;
++        read() ;
++    }
++    
++    private void read() {
++        long x = file.size() ;
++        ByteBuffer blob = ByteBuffer.allocate((int)x) ;
++        int len = file.read(blob) ;
++        if ( len != x )
++            throw new RuntimeIOException("Short read: "+len+" of "+x) ;
++        blob.rewind() ;
++        blobRef.set(blob) ; 
++    }
++
++    private void write() {
++        ByteBuffer blob = blobRef.get();
++        blob.rewind() ;
++        int x = blob.remaining() ;
++        file.truncate(0);
++        int len = file.write(blob) ;
++        if ( len != x )
++            throw new RuntimeIOException("Short write: "+len+" of "+x) ;
++        file.sync(); 
++        blob.rewind() ;
++    }
++
++    /** Set the byte buffer.
++     * The byte buffer should not be accessed except by {@link #getBlob}.
++     * We avoid a copy in and copy out - we trust the caller.
++     * The byte buffer should be configured for read if used with {@link #getString}.
++     */
++    public void setBlob(ByteBuffer bb) {
++        checkWriteTxn();
++        getDataState().setByteBuffer(bb);
++    }
++    
++    public ByteBuffer getBlob() {
++        if ( isActiveTxn() )
++            return getDataState().getByteBuffer() ;
++        return blobRef.get() ;
++    }
++
++    /**  Set data from string - convenience operation */ 
++    public void setString(String dataStr) {
++        checkWriteTxn();
++        if ( dataStr == null ) {
++            setBlob(null);
++            return ;
++        }
++
++        // Attempt to reuse the write-transaction byte buffer
++        // We can't reuse if it's the blobRef (shared by other transactions)
++        // but if it's a new to this write transaction buffer we can reuse.
++        
++        int maxNeeded = dataStr.length()*4 ;
++        ByteBuffer bb = getDataState().getByteBuffer() ;
++        if ( bb == blobRef.get() )
++            bb = ByteBuffer.allocate(maxNeeded) ;
++        else if ( bb.capacity() >= maxNeeded )
++            bb.clear() ;
++        else
++            bb = ByteBuffer.allocate(maxNeeded) ;
++        Bytes.toByteBuffer(dataStr, bb) ;
++        bb.flip() ;
++        setBlob(bb);
++    }
++    
++    /**  Get data as string - convenience operation */ 
++    public String getString() {
++        ByteBuffer bb = getBlob() ;
++        if (bb == null )
++            return null ;
++        int x = bb.position() ;
++        String s = Bytes.fromByteBuffer(bb) ;
++        bb.position(x) ;
++        return s ;
++    }
++
++    private boolean recoveryChange = false ; 
++    @Override
++    public void startRecovery() {
++        recoveryChange = false ;
++    }
++
++    @Override
++    public void recover(ByteBuffer ref) {
++        blobRef.set(ref) ;
++        recoveryChange = true ;
++    }
++
++    @Override
++    public void finishRecovery() {
++        if ( recoveryChange )
++            write() ;
++    }
++
++    @Override
++    public void cleanStart() { }
++    
++    @Override
++    protected BlobState _begin(ReadWrite readWrite, TxnId txnId) {
++        return createState();
++    }
++
++    private BlobState createState() {
++        ByteBuffer blob = blobRef.get() ;
++        // Save reference to ByteBuffer into the transaction state.
++        return new BlobState(blob) ;
++    }
++    
++    @Override
++    protected BlobState _promote(TxnId txnId, BlobState state) {
++        // Our write state is the read state.
++        return createState();
++    }
++    
++    @Override
++    protected ByteBuffer _commitPrepare(TxnId txnId, BlobState state) {
++        if ( ! state.hasChanged )
++            return null;
++        return state.getByteBuffer() ;
++    }
++
++    @Override
++    protected void _commit(TxnId txnId, BlobState state) {
++        if ( ! state.hasChanged )
++            return;
++        // NB Change reference. 
++        blobRef.set(state.getByteBuffer()) ;
++        write() ;
++    }
++
++    @Override
++    protected void _commitEnd(TxnId txnId, BlobState state) {}
++
++    @Override
++    protected void _abort(TxnId txnId, BlobState state) {}
++
++    @Override
++    protected void _complete(TxnId txnId, BlobState state) {}
++
++    @Override
++    protected void _shutdown() {}
++    
++    @Override
++    public String toString()    { return getComponentId().label() ; } 
++
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/TC_TransData.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/TC_TransData.java
index 0000000,0000000..9681f2c
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/TC_TransData.java
@@@ -1,0 -1,0 +1,33 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans;
++
++import org.junit.runner.RunWith ;
++import org.junit.runners.Suite ;
++import org.seaborne.dboe.trans.bplustree.TS_TxnBPTree ;
++import org.seaborne.dboe.trans.data.TS_TransData ;
++import org.seaborne.dboe.trans.recovery.TestRecovery ;
++
++@RunWith(Suite.class)
++@Suite.SuiteClasses( {
++    TS_TransData.class
++    , TS_TxnBPTree.class 
++    , TestRecovery.class
++})
++public class TC_TransData {}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/bplustree/BPlusTreeMaker.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/bplustree/BPlusTreeMaker.java
index 0000000,0000000..5487178
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/bplustree/BPlusTreeMaker.java
@@@ -1,0 -1,0 +1,55 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.bplustree;
++
++import org.seaborne.dboe.index.Index ;
++import org.seaborne.dboe.index.RangeIndex ;
++import org.seaborne.dboe.trans.bplustree.BPlusTree ;
++import org.seaborne.dboe.index.test.RangeIndexMaker ;
++import org.seaborne.dboe.test.RecordLib ;
++
++public class BPlusTreeMaker implements RangeIndexMaker
++{
++    private int order ;
++    private int recordOrder ;
++    private boolean trackers ;
++
++
++    public BPlusTreeMaker(int order, int recordOrder, boolean trackers)
++    { 
++        this.order = order ; 
++        this.recordOrder = recordOrder ;
++        this.trackers = trackers ;
++    }
++    
++    @Override
++    public Index makeIndex() { return makeRangeIndex() ; }
++
++    @Override
++    public RangeIndex makeRangeIndex()
++    {
++        BPlusTree bpTree = BPlusTreeFactory.makeMem(order, recordOrder, RecordLib.TestRecordLength, 0) ;
++        if ( trackers )
++            bpTree = BPlusTreeFactory.addTracking(bpTree) ;
++        return bpTree ;
++    }
++
++    @Override
++    public String getLabel() { return "B+Tree order = "+order ; } 
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/bplustree/TS_TxnBPTree.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/bplustree/TS_TxnBPTree.java
index 0000000,0000000..e8016c8
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/bplustree/TS_TxnBPTree.java
@@@ -1,0 -1,0 +1,41 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.bplustree;
++
++import org.junit.runner.RunWith ;
++import org.junit.runners.Suite ;
++import org.seaborne.dboe.trans.bplustree.rewriter.TestBPlusTreeRewriterNonTxn ;
++
++@RunWith(Suite.class)
++@Suite.SuiteClasses( {
++    
++    // Non-transactional tests -- that is, algorithms and machinary. 
++    TestBPTreeRecordsNonTxn.class,
++    TestBPlusTreeIndexNonTxn.class,
++    TestBPlusTreeNonTxn.class,
++    TestBPTreeModes.class,
++    
++    // Transactional tests
++    TestBPlusTreeTxn.class,
++    
++    // Rewriter
++    TestBPlusTreeRewriterNonTxn.class
++} )
++
++public class TS_TxnBPTree
++{ }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/bplustree/TestBPTreeModes.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/bplustree/TestBPTreeModes.java
index 0000000,0000000..71168cb
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/bplustree/TestBPTreeModes.java
@@@ -1,0 -1,0 +1,77 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.bplustree;
++
++import java.util.Arrays ;
++import java.util.Collection ;
++
++import org.junit.After ;
++import org.junit.AfterClass ;
++import org.junit.Before ;
++import org.junit.BeforeClass ;
++import org.junit.runner.RunWith ;
++import org.junit.runners.Parameterized ;
++import org.junit.runners.Parameterized.Parameters ;
++
++/** Run the B+Tree algorithm tests but for each combination of explicit 
++ * write-in-place / always copy modes.  
++ */
++@RunWith(Parameterized.class)
++public class TestBPTreeModes extends TestBPlusTreeNonTxn
++{
++    @Parameters(name="Node dup={0}, Record dup={1}")
++    public static Collection<Object[]> data()
++    {
++        return Arrays.asList(new Object[][]{
++            {true, true},
++            {true, false},
++            {false, true},
++            {false, false}
++            }) ;
++    }
++    
++    public TestBPTreeModes(boolean nodeMode, boolean recordsMode) {
++        
++        BPT.promoteDuplicateNodes = nodeMode ;
++        BPT.promoteDuplicateRecords = recordsMode ;
++    }
++
++    boolean modeAtStartNodes ;
++    boolean modeAtStartRecords ;
++    
++    @BeforeClass public static void setupSuite() {
++        BPT.forcePromoteModes = true ;
++    }
++    
++    @AfterClass public static void resetSuite() {
++        BPT.forcePromoteModes = false ;
++    }
++    
++    @Before public void setModes() {
++        BPT.forcePromoteModes = true ;
++        modeAtStartNodes = BPT.promoteDuplicateNodes ;
++        modeAtStartRecords = BPT.promoteDuplicateRecords ;
++    }
++
++    
++    @After public void resetModes() {
++        BPT.promoteDuplicateNodes = modeAtStartNodes ;
++        BPT.promoteDuplicateRecords = modeAtStartRecords ;
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/bplustree/TestBPTreeRecordsNonTxn.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/bplustree/TestBPTreeRecordsNonTxn.java
index 0000000,0000000..fad84f4
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/bplustree/TestBPTreeRecordsNonTxn.java
@@@ -1,0 -1,0 +1,326 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.bplustree ;
++
++import org.junit.* ;
++import org.seaborne.dboe.base.block.BlockMgr ;
++import org.seaborne.dboe.base.block.BlockMgrFactory ;
++import org.seaborne.dboe.base.buffer.RecordBuffer ;
++import org.seaborne.dboe.base.record.Record ;
++import org.seaborne.dboe.base.record.RecordFactory ;
++import org.seaborne.dboe.base.recordbuffer.RecordBufferPage ;
++import org.seaborne.dboe.base.recordbuffer.RecordBufferPageMgr ;
++import org.seaborne.dboe.sys.SystemIndex ;
++import org.seaborne.dboe.test.RecordLib ;
++
++public class TestBPTreeRecordsNonTxn extends Assert {
++    static private boolean             oldNullOut ;
++
++    static private int                 blockSize ;
++    static private RecordFactory       recordFactory ;
++
++    static private int                 bufSizeRecord ;
++    static private BlockMgr            blkMgrRecords ;
++    static private RecordBufferPageMgr recordBufferPageMgr ;
++
++    @BeforeClass
++    public static void beforeClass() {
++        oldNullOut = SystemIndex.getNullOut() ;
++        SystemIndex.setNullOut(true) ;
++
++        blockSize = 4 * 8 ; // Which is 6 int records
++        recordFactory = new RecordFactory(4, 0) ;
++
++        bufSizeRecord = RecordBufferPage.calcRecordSize(recordFactory, blockSize) ;
++        blkMgrRecords = BlockMgrFactory.createMem("BPTreeRecords", blockSize) ;
++        recordBufferPageMgr = new RecordBufferPageMgr(recordFactory, blkMgrRecords) ;
++
++        BlockMgr blkMgrNodes = BlockMgrFactory.createMem("BPTreeNs", blockSize) ;
++        
++        // Copy on write.
++        BPT.forcePromoteModes = true ;
++        BPT.promoteDuplicateRecords = true ;
++    }
++
++    @AfterClass
++    public static void afterClass() {
++        SystemIndex.setNullOut(oldNullOut) ;
++        BPT.forcePromoteModes = false ;
++    }
++
++    @Before
++    public void before() {
++        blkMgrRecords.beginUpdate() ;
++    }
++
++    @After
++    public void after() {
++        blkMgrRecords.endUpdate() ;
++    }
++
++    @Test
++    public void bpt_records_1() {
++        BPTreeRecords bpr = make() ;
++        fill(bpr) ;
++        check(bpr) ;
++        bpr.release() ;
++    }
++
++    @Test
++    public void bpt_records_2() {
++        BPTreeRecords bpr = make() ;
++        fill(bpr) ;
++        int s = bpr.getCount() ;
++        assertTrue(bpr.isFull()) ;
++        BPTreePage z = bpr.split() ;
++        assertTrue(z instanceof BPTreeRecords) ;
++        assertEquals(s, z.getCount() + bpr.getCount()) ;
++        check(bpr) ;
++        check((BPTreeRecords)z) ;
++        bpr.release() ;
++        z.release() ;
++    }
++
++    @Test
++    public void bpt_records_3() {
++        BPTreeRecords bpr = make() ;
++        for ( int i = 0 ; bpr.getCount() < bpr.getMaxSize() ; i++ )
++            insert(bpr, (i + 0x20)) ;
++        check(bpr) ;
++        bpr.release() ;
++    }
++
++    @Test
++    public void bpt_records_4() {
++        BPTreeRecords bpr = make() ;
++        for ( int i = bpr.getMaxSize() - 1 ; i >= 0 ; i-- )
++            insert(bpr, i + 0x20) ;
++        check(bpr) ;
++        bpr.release() ;
++    }
++
++    @Test
++    public void bpt_records_5() {
++        BPTreeRecords bpr = make() ;
++        int N = bpr.getMaxSize() ;
++
++        for ( int i = bpr.getMaxSize() - 1 ; i >= 0 ; i-- )
++            insert(bpr, (i + 0x20)) ;
++
++        delete(bpr, (1 + 0x20)) ;
++        assertEquals(N - 1, bpr.getCount()) ;
++        check(bpr) ;
++
++        delete(bpr, (2 + 0x20)) ;
++        assertEquals(N - 2, bpr.getCount()) ;
++        check(bpr) ;
++
++        delete(bpr, bpr.getLowRecord()) ;
++        assertEquals(N - 3, bpr.getCount()) ;
++        check(bpr) ;
++
++        delete(bpr, bpr.getHighRecord()) ;
++        assertEquals(N - 4, bpr.getCount()) ;
++        check(bpr) ;
++
++        bpr.release() ;
++    }
++
++    @Test
++    public void bpt_records_6() {
++        BPTreeRecords bpr = make() ;
++        fill(bpr) ;
++
++        // No match.
++        assertNull(search(bpr, RecordLib.intToRecord(0x20))) ;
++
++        Record r = RecordLib.intToRecord(0x32) ;
++        Record r2 = search(bpr, r) ;
++        assertTrue(Record.keyEQ(r, r2)) ;
++
++        r = bpr.getLowRecord() ;
++        r2 = search(bpr, r) ;
++        assertTrue(Record.keyEQ(r, r2)) ;
++
++        r = bpr.getHighRecord() ;
++        r2 = search(bpr, r) ;
++        assertTrue(Record.keyEQ(r, r2)) ;
++
++        bpr.release() ;
++    }
++
++    @Test
++    public void bpt_shift_1() {
++        BPTreeRecords bpr1 = make() ;
++        BPTreeRecords bpr2 = make() ;
++
++        insert(bpr1, 10) ;
++        Record r = bpr1.shiftRight(bpr2, null) ;
++        assertNull(r) ;
++        // assertTrue(Record.keyEQ(r, RecordTestLib.intToRecord(10))) ;
++        contains(bpr1) ;
++        contains(bpr2, 10) ;
++
++        bpr1.release() ;
++        bpr2.release() ;
++
++    }
++
++    @Test
++    public void bpt_shift_2() {
++        BPTreeRecords bpr1 = make() ;
++        BPTreeRecords bpr2 = make() ;
++
++        insert(bpr1, 10) ;
++        Record r = bpr2.shiftLeft(bpr1, null) ;
++
++        assertTrue(Record.keyEQ(r, RecordLib.intToRecord(10))) ;
++        contains(bpr1) ;
++        contains(bpr2, 10) ;
++        bpr1.release() ;
++        bpr2.release() ;
++    }
++
++    @Test
++    public void bpt_shift_3() {
++        BPTreeRecords bpr1 = make() ;
++        BPTreeRecords bpr2 = make() ;
++
++        insert(bpr1, 10, 20) ;
++        insert(bpr2, 99) ;
++
++        Record r = bpr1.shiftRight(bpr2, null) ;
++
++        assertTrue(r + " != " + RecordLib.intToRecord(10), Record.keyEQ(r, RecordLib.intToRecord(10))) ;
++        contains(bpr1, 10) ;
++        contains(bpr2, 20, 99) ;
++        bpr1.release() ;
++        bpr2.release() ;
++    }
++
++    @Test
++    public void bpt_shift_4() {
++        BPTreeRecords bpr1 = make() ;
++        BPTreeRecords bpr2 = make() ;
++
++        insert(bpr1, 10, 20) ;
++        insert(bpr2, 5) ;
++
++        Record r = bpr2.shiftLeft(bpr1, null) ;
++        assertTrue(Record.keyEQ(r, RecordLib.intToRecord(10))) ;
++
++        contains(bpr1, 20) ;
++        contains(bpr2, 5, 10) ;
++        bpr1.release() ;
++        bpr2.release() ;
++    }
++
++    @Test
++    public void bpt_merge_1() {
++        BPTreeRecords bpr1 = make() ;
++        BPTreeRecords bpr2 = make() ;
++
++        insert(bpr1, 10, 20) ;
++        insert(bpr2, 99) ;
++
++        BPTreeRecords bpr3 = (BPTreeRecords)bpr1.merge(bpr2, null) ;
++        contains(bpr1, 10, 20, 99) ;
++        contains(bpr2) ;
++        assertSame(bpr1, bpr3) ;
++        bpr1.release() ;
++        bpr2.release() ;
++    }
++
++    @Test
++    public void bpt_merge_2() {
++        BPTreeRecords bpr1 = make() ;
++        BPTreeRecords bpr2 = make() ;
++
++        insert(bpr1, 10, 20) ;
++        insert(bpr2, 5) ;
++
++        BPTreeRecords bpr3 = (BPTreeRecords)bpr2.merge(bpr1, null) ;
++        contains(bpr1) ;
++        contains(bpr2, 5, 10, 20) ;
++        assertSame(bpr2, bpr3) ;
++        bpr1.release() ;
++        bpr2.release() ;
++    }
++
++    protected static void check(BPTreeRecords bpr) {
++        assertTrue(bpr.getCount() >= 0) ;
++        assertTrue(bpr.getCount() <= bpr.getMaxSize()) ;
++
++        assertEquals(bpr.getRecordBuffer().getLow(), bpr.getLowRecord()) ;
++        assertEquals(bpr.getRecordBuffer().getHigh(), bpr.getHighRecord()) ;
++
++        for ( int i = 1 ; i < bpr.getCount() ; i++ ) {
++            Record r1 = bpr.getRecordBuffer().get(i - 1) ;
++            Record r2 = bpr.getRecordBuffer().get(i) ;
++            assertTrue(Record.keyLE(r1, r2)) ;
++        }
++    }
++
++    private static Record search(BPTreeRecords bpr, int x) {
++        return search(bpr, RecordLib.intToRecord(x)) ;
++    }
++
++    private static Record search(BPTreeRecords bpr, Record r) {
++        return bpr.internalSearch(null, r) ;
++    }
++
++    private static void insert(BPTreeRecords bpr, int... values) {
++        for ( int value : values ) {
++            bpr.internalInsert(null, RecordLib.intToRecord(value)) ;
++        }
++    }
++
++    private static void insert(BPTreeRecords bpr, Record r) {
++        bpr.internalInsert(null, r) ;
++    }
++
++    private static void delete(BPTreeRecords bpr, int... values) {
++        for ( int value : values ) {
++            delete(bpr, RecordLib.intToRecord(value)) ;
++        }
++    }
++
++    private static void delete(BPTreeRecords bpr, Record r) {
++        bpr.internalDelete(null, r) ;
++    }
++
++    private static void contains(BPTreeRecords bpr, int... values) {
++        assertEquals(values.length, bpr.getCount()) ;
++        for ( int i = 1 ; i < values.length ; i++ )
++            assertTrue(Record.compareByKeyValue(RecordLib.intToRecord(values[i]), bpr.getRecordBuffer().get(i)) == 0) ;
++    }
++
++    protected static BPTreeRecords make() {
++        RecordBufferPage page = recordBufferPageMgr.create() ;
++        BPTreeRecordsMgr mgr = new BPTreeRecordsMgr(null, recordFactory, recordBufferPageMgr) ;
++        return new BPTreeRecords(mgr, page) ;
++    }
++
++    protected static void fill(BPTreeRecords bpr) {
++        int N = bpr.getRecordBuffer().maxSize() ;
++        for ( int i = 0 ; i < N ; i++ ) {
++            RecordBuffer rb = bpr.getRecordBuffer() ;
++            insert(bpr, (i + 0x30)) ;
++        }
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/bplustree/TestBPlusTreeIndexNonTxn.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/bplustree/TestBPlusTreeIndexNonTxn.java
index 0000000,0000000..1834f77
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/bplustree/TestBPlusTreeIndexNonTxn.java
@@@ -1,0 -1,0 +1,60 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.bplustree ;
++
++import org.apache.jena.atlas.logging.LogCtl ;
++import org.junit.AfterClass ;
++import org.junit.BeforeClass ;
++import org.seaborne.dboe.base.block.BlockMgr ;
++import org.seaborne.dboe.index.test.AbstractTestIndex ;
++import org.seaborne.dboe.sys.SystemIndex ;
++import org.seaborne.dboe.test.RecordLib ;
++
++/** Run the tests in default settings for a tree in "non-transactional" mode */ 
++public class TestBPlusTreeIndexNonTxn extends AbstractTestIndex {
++    static boolean addTracker = true  ;
++    // Panic.
++    static boolean addLogger  = false  ;
++
++    static boolean originalNullOut ;
++    @BeforeClass
++    static public void beforeClass() {
++        BPT.CheckingNode = true ;
++        originalNullOut = SystemIndex.getNullOut() ;
++        SystemIndex.setNullOut(true) ;
++    }
++
++    @AfterClass
++    static public void afterClass() {
++        SystemIndex.setNullOut(originalNullOut) ;
++    }
++    @Override
++    protected BPlusTree makeIndex(int order, int minRecords) {
++        BPlusTree bpt = BPlusTreeFactory.makeMem(order, minRecords, RecordLib.TestRecordLength, 0) ;
++        if ( addLogger ) {
++            // Put it in but disable it so that it can be enabled
++            // in the middle of a complex operation.
++            LogCtl.disable(BlockMgr.class) ;
++            bpt = BPlusTreeFactory.addLogging(bpt) ;
++        }
++        if ( addTracker )
++            bpt = BPlusTreeFactory.addTracking(bpt) ;
++        bpt.nonTransactional() ;
++        return bpt ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/bplustree/TestBPlusTreeNonTxn.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/bplustree/TestBPlusTreeNonTxn.java
index 0000000,0000000..266326a
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/bplustree/TestBPlusTreeNonTxn.java
@@@ -1,0 -1,0 +1,90 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.bplustree ;
++
++import static org.seaborne.dboe.index.test.IndexTestLib.add ;
++import static org.seaborne.dboe.test.RecordLib.intToRecord ;
++
++import java.util.List ;
++
++import org.apache.jena.atlas.logging.LogCtl ;
++import org.junit.AfterClass ;
++import org.junit.BeforeClass ;
++import org.seaborne.dboe.base.block.BlockMgr ;
++import org.seaborne.dboe.base.record.Record ;
++import org.seaborne.dboe.index.test.AbstractTestRangeIndex ;
++import org.seaborne.dboe.sys.SystemIndex ;
++import org.seaborne.dboe.test.RecordLib ;
++
++/** Run the tests in default settings for a tree in "non-transactional" mode */ 
++public class TestBPlusTreeNonTxn extends AbstractTestRangeIndex {
++    // See TestBPTreeModes for parameterised tests for the duplication modes.
++    
++    // Add tracker or not (usually not)
++    // The tracker checking can impose more constraints than are needed
++    // giving false negatives.  Iterator aren't tracked (they may not
++    // be consumed; don't release pages properly)
++    static boolean addTracker = false  ;
++    // Panic.
++    static boolean addLogger  = false  ;
++
++    static boolean originalNullOut ;
++    @BeforeClass
++    static public void beforeClass() {
++        BPT.CheckingNode = true ;
++        originalNullOut = SystemIndex.getNullOut() ;
++        SystemIndex.setNullOut(true) ;
++    }
++
++    @AfterClass
++    static public void afterClass() {
++        SystemIndex.setNullOut(originalNullOut) ;
++    }
++    
++    protected void testClearX(int N) {
++        int[] keys = new int[N] ; // Slice is 1000.
++        for ( int i = 0 ; i < keys.length ; i++ )
++            keys[i] = i ;
++        BPlusTree rIndex = makeRangeIndex(2, 2) ;
++        add(rIndex, keys) ;
++        rIndex.dump() ;
++        if ( N > 0 )
++            assertFalse(rIndex.isEmpty()) ;
++        List<Record> x = intToRecord(keys, RecordLib.TestRecordLength) ;
++        for ( int i = 0 ; i < keys.length ; i++ ) {
++            System.out.println(i+": "+x.get(i)) ;
++            rIndex.delete(x.get(i)) ;
++        }
++        assertTrue(rIndex.isEmpty()) ;
++    }
++    
++    @Override
++    protected BPlusTree makeRangeIndex(int order, int minRecords) {
++        BPlusTree bpt = BPlusTreeFactory.makeMem(order, minRecords, RecordLib.TestRecordLength, 0) ;
++        if ( addLogger ) {
++            // Put it in but disable it so that it can be enabled
++            // in the middle of a complex operation.
++            LogCtl.disable(BlockMgr.class) ;
++            bpt = BPlusTreeFactory.addLogging(bpt) ;
++        }
++        if ( addTracker )
++            bpt = BPlusTreeFactory.addTracking(bpt) ;
++        bpt.nonTransactional() ;
++        return bpt ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/bplustree/TestBPlusTreeTxn.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/bplustree/TestBPlusTreeTxn.java
index 0000000,0000000..70000f6
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/bplustree/TestBPlusTreeTxn.java
@@@ -1,0 -1,0 +1,205 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.bplustree;
++
++import org.apache.jena.query.ReadWrite ;
++import org.junit.Assert ;
++import org.junit.Test ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.dboe.index.test.IndexTestLib ;
++import org.seaborne.dboe.jenax.Txn ;
++import org.seaborne.dboe.test.RecordLib ;
++import org.seaborne.dboe.transaction.Transactional ;
++import org.seaborne.dboe.transaction.TransactionalFactory ;
++import org.seaborne.dboe.transaction.txn.TransactionalComponent ;
++
++/** Tests of B+Tree and transactions */ 
++public class TestBPlusTreeTxn extends Assert {
++    
++    static BPlusTree createBPTree() { 
++        return BPlusTreeFactory.makeMem(2, 2, RecordLib.TestRecordLength, 0) ;
++    }
++    
++    static Transactional transactional(TransactionalComponent ... components) {
++        return transactional(Location.mem(), components) ;
++    }
++    
++    static Transactional transactional(Location location, TransactionalComponent ... components) {
++        return TransactionalFactory.createTransactional(location, components) ;
++    }
++    
++    // Commit
++    @Test public void bptree_txn_01() {
++        BPlusTree bpt = createBPTree() ;
++        assertNotNull(bpt.getComponentId()) ;
++        int outerRootIdx1 = bpt.getRootId() ;
++        Transactional thing = transactional(bpt) ;
++        Txn.executeWrite(thing, () -> { 
++            IndexTestLib.add(bpt, 1, 2, 3, 4) ;
++        } ); 
++        int outerRootIdx2 = bpt.getRootId() ;
++        assertNotEquals("After txn", outerRootIdx1, outerRootIdx2); 
++    }
++ 
++    // Commit - only the first changes the root.
++    @Test public void bptree_txn_02() {
++        BPlusTree bpt = createBPTree() ;
++        int outerRootIdx1 = bpt.getRootId() ;
++        Transactional thing = transactional(bpt) ;
++        Txn.executeWrite(thing, () -> { 
++            int rootIdx1 = bpt.getRootId() ;
++            assertEquals("Inside txn (1)", outerRootIdx1, rootIdx1);
++            IndexTestLib.add(bpt, 1) ;
++            int rootIdx2 = bpt.getRootId() ;
++            assertNotEquals("Inside txn (2)", rootIdx1, rootIdx2);
++            IndexTestLib.add(bpt, 2, 3, 4) ;
++            int rootIdx3 = bpt.getRootId() ;
++            assertEquals("Inside txn (3)", rootIdx2, rootIdx3);
++        } ) ; 
++        int outerRootIdx2 = bpt.getRootId() ;
++        assertNotEquals("After txn", outerRootIdx1, outerRootIdx2); 
++    }
++
++    // Abort
++    @Test public void bptree_txn_03() {
++        BPlusTree bpt = createBPTree() ;
++        int outerRootIdx1 = bpt.getRootId() ;
++        Transactional thing = transactional(bpt) ;
++        thing.begin(ReadWrite.WRITE);
++        IndexTestLib.add(bpt, 1, 2, 3, 4) ;
++        thing.abort() ;
++        thing.end() ;
++        int outerRootIdx2 = bpt.getRootId() ;
++        assertEquals("After txn", outerRootIdx1, outerRootIdx2); 
++    }
++    
++    // Two transactions
++    @Test public void bptree_txn_04() {
++        BPlusTree bpt = createBPTree() ;
++        int outerRootIdx1 = bpt.getRootId() ;
++        Transactional thing = transactional(bpt) ;
++        Txn.executeWrite(thing, () -> { 
++            IndexTestLib.add(bpt, 1, 2, 3, 4) ;
++        } ); 
++        int outerRootIdx2 = bpt.getRootId() ;
++        assertNotEquals("After txn(1)", outerRootIdx1, outerRootIdx2); 
++        Txn.executeWrite(thing, () -> { 
++            IndexTestLib.add(bpt, 5, 6) ;
++        } ); 
++        int outerRootIdx3 = bpt.getRootId() ;
++        assertNotEquals("After txn (2)", outerRootIdx1, outerRootIdx3); 
++        assertNotEquals("After txn (3)", outerRootIdx2, outerRootIdx3); 
++    }
++    
++    // Two transactions, second an insert no-op.
++    // Relies on all blocks not being full and so not being
++    // split on the way down due to the early split algorithm. 
++    @Test public void bptree_txn_05() {
++        BPlusTree bpt = createBPTree() ;
++        int outerRootIdx1 = bpt.getRootId() ;
++        Transactional thing = transactional(bpt) ;
++        Txn.executeWrite(thing, () -> { 
++            IndexTestLib.add(bpt, 1, 2, 3) ;
++        } ); 
++        int outerRootIdx2 = bpt.getRootId() ;
++        assertNotEquals("After txn(1)", outerRootIdx1, outerRootIdx2); 
++        Txn.executeWrite(thing, () -> { 
++            IndexTestLib.add(bpt, 1, 2) ;
++        } ); 
++        int outerRootIdx3 = bpt.getRootId() ;
++        assertNotEquals("After txn (2)", outerRootIdx1, outerRootIdx3); 
++        assertEquals("After txn (3)", outerRootIdx2, outerRootIdx3); 
++    }
++
++    // Two transactions, second a delete no-op.
++    // Relies on all blocks not being min0size so not rebalanced.
++    @Test public void bptree_txn_06() {
++        BPlusTree bpt = createBPTree() ;
++        int outerRootIdx1 = bpt.getRootId() ;
++        Transactional thing = transactional(bpt) ;
++        Txn.executeWrite(thing, () -> { 
++            IndexTestLib.add(bpt, 1, 2, 3) ;
++        } ); 
++        int outerRootIdx2 = bpt.getRootId() ;
++        assertNotEquals("After txn(1)", outerRootIdx1, outerRootIdx2); 
++        Txn.executeWrite(thing, () -> { 
++            IndexTestLib.delete(bpt, 5, 6) ;
++        } ); 
++        int outerRootIdx3 = bpt.getRootId() ;
++        assertNotEquals("After txn (2)", outerRootIdx1, outerRootIdx3); 
++        assertEquals("After txn (3)", outerRootIdx2, outerRootIdx3); 
++    }
++    
++    // Two trees
++    @Test public void bptree_txn_10() {
++        BPlusTree bpt1 = createBPTree() ;
++        BPlusTree bpt2 = createBPTree() ;
++        assertNotEquals(bpt1.getComponentId(), bpt2.getComponentId()) ;
++        
++        Transactional thing = transactional(bpt1, bpt2) ;
++        Txn.executeWrite(thing, () -> { 
++            IndexTestLib.add(bpt1, 1, 2, 3) ;
++            IndexTestLib.add(bpt2, 4, 5) ;
++        } );
++        Txn.executeRead(thing, ()->{
++            IndexTestLib.testIndexContents(bpt2, 4, 5);
++            IndexTestLib.testIndexContents(bpt1, 1, 2, 3);
++        } );
++    }
++    
++    @Test public void bptree_txn_11() {
++        BPlusTree bpt1 = createBPTree() ;
++        BPlusTree bpt2 = createBPTree() ;
++        assertNotEquals(bpt1.getComponentId(), bpt2.getComponentId()) ;
++        
++        Transactional thing = transactional(bpt1, bpt2) ;
++        
++        // Commit 1
++        Txn.executeWrite(thing, () -> { 
++            IndexTestLib.add(bpt1, 2, 1) ;
++            IndexTestLib.add(bpt2, 3, 4, 5) ;
++        }) ;
++        Txn.executeRead(thing, ()->{
++            IndexTestLib.testIndexContents(bpt2, 3, 4, 5);
++            IndexTestLib.testIndexContents(bpt1, 1, 2);
++        } );
++        
++        // Abort
++        thing.begin(ReadWrite.WRITE);
++        IndexTestLib.add(bpt1, 9, 10) ;
++        IndexTestLib.delete(bpt2, 3, 11) ;
++        thing.abort() ;
++        Txn.executeRead(thing, ()->{
++            IndexTestLib.testIndexContents(bpt2, 3, 4, 5);
++            IndexTestLib.testIndexContents(bpt1, 1, 2);
++        } );
++        
++        // Commit 2
++        Txn.executeWrite(thing, () -> { 
++            IndexTestLib.delete(bpt1, 1,3) ;
++            IndexTestLib.add(bpt1, 4) ;
++            IndexTestLib.add(bpt2, 11, 12, 13) ;
++        }) ;
++        Txn.executeRead(thing, ()->{
++            IndexTestLib.testIndexContents(bpt2, 3, 4, 5, 11, 12, 13);
++            IndexTestLib.testIndexContents(bpt1, 2, 4);
++        } );
++    }
++    
++    
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/bplustree/rewriter/TestBPlusTreeRewriterNonTxn.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/bplustree/rewriter/TestBPlusTreeRewriterNonTxn.java
index 0000000,0000000..d9c2b6a
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/bplustree/rewriter/TestBPlusTreeRewriterNonTxn.java
@@@ -1,0 -1,0 +1,179 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.bplustree.rewriter;
++
++import java.util.ArrayList ;
++import java.util.Iterator ;
++import java.util.List ;
++
++import org.apache.jena.atlas.lib.Bytes ;
++import org.junit.AfterClass ;
++import org.junit.Assert ;
++import org.junit.BeforeClass ;
++import org.junit.Test ;
++import org.seaborne.dboe.base.block.BlockMgr ;
++import org.seaborne.dboe.base.block.BlockMgrFactory ;
++import org.seaborne.dboe.base.file.BufferChannel ;
++import org.seaborne.dboe.base.file.FileFactory ;
++import org.seaborne.dboe.base.file.FileSet ;
++import org.seaborne.dboe.base.record.Record ;
++import org.seaborne.dboe.base.record.RecordFactory ;
++import org.seaborne.dboe.sys.Names ;
++import org.seaborne.dboe.trans.bplustree.BPTreeException ;
++import org.seaborne.dboe.trans.bplustree.BPlusTree ;
++import org.seaborne.dboe.trans.bplustree.BPlusTreeParams ;
++//import org.seaborne.dboe.trans.bplustree.rewriter.BPlusTreeRewriter ;
++
++public class TestBPlusTreeRewriterNonTxn extends Assert
++{
++    // See also CmdTestBlusTreeRewriter for randomized soak testing. 
++    
++    static int KeySize     = 4 ;
++    static int ValueSize   = 8 ;
++    static RecordFactory recordFactory = new RecordFactory(KeySize,ValueSize) ;
++    
++    // The BPlusTreeRewriter works directly on storage.
++    static boolean b ; 
++    @BeforeClass public static void beforeClass()   { b = BlockMgrFactory.AddTracker ; BlockMgrFactory.AddTracker = false ; }
++    @AfterClass  public static void afterClass()    { BlockMgrFactory.AddTracker = b  ;}
++    
++    @Test public void bpt_rewrite_01()  { runTest(2, 0) ; }
++    @Test public void bpt_rewrite_02()  { runTest(3, 0) ; }
++
++    @Test public void bpt_rewrite_03()  { runTest(2, 1) ; }
++    @Test public void bpt_rewrite_04()  { runTest(3, 1) ; }
++
++    @Test public void bpt_rewrite_05()  { runTest(2, 2) ; }
++    @Test public void bpt_rewrite_06()  { runTest(3, 2) ; }
++    
++    @Test public void bpt_rewrite_07()  { runTest(2, 100) ; }
++    @Test public void bpt_rewrite_08()  { runTest(3, 100) ; }
++    
++    @Test public void bpt_rewrite_99()  { runTest(5, 1000) ; }
++    
++    static void runTest(int order, int N)
++    { runOneTest(order, N , recordFactory, false) ; }
++    
++    public static void runOneTest(int order, int N, RecordFactory recordFactory, boolean debug)
++    {
++        BPlusTreeParams bptParams = new BPlusTreeParams(order, recordFactory) ;
++        BPlusTreeRewriter.debug = debug ;
++
++        // ---- Test data
++        List<Record> originaldata = TestBPlusTreeRewriterNonTxn.createData(N, recordFactory) ;
++        if ( debug )
++            System.out.println("Test data: "+originaldata) ;
++
++        FileSet destination = FileSet.mem() ;
++        // ---- Rewrite
++        BufferChannel rootState = FileFactory.createBufferChannel(destination, Names.extBptState) ;
++        // Write leaves to ...
++        BlockMgr blkMgr1 = BlockMgrFactory.create(destination, Names.extBptTree, bptParams.getCalcBlockSize(), 10, 10) ;
++        // Write nodes to ...
++        BlockMgr blkMgr2 = BlockMgrFactory.create(destination, Names.extBptTree, bptParams.getCalcBlockSize(), 10, 10) ;
++        
++        BPlusTree bpt2 = BPlusTreeRewriter.packIntoBPlusTree(originaldata.iterator(), bptParams, recordFactory,
++                                                             rootState, blkMgr1, blkMgr2) ;
++        if ( debug )
++        {
++            BPlusTreeRewriterUtils.divider() ;
++            bpt2.dump();
++        }
++        
++        // ---- Checking
++        bpt2.check() ;
++        
++        scanComparision(originaldata, bpt2) ;
++        findComparison(originaldata, bpt2) ;
++        sizeComparison(originaldata, bpt2) ;
++    }
++    
++    public static void scanComparision(List<Record> originaldata, BPlusTree bpt2)
++    {
++        // ** Scan comparisonSetupIndex
++        Iterator<Record> iter1 = originaldata.iterator() ;
++        Iterator<Record> iter2 = bpt2.iterator() ;
++        long count = 0 ;
++        for ( ; iter1.hasNext() ; )
++        {
++            count++ ;
++            Record r1 = iter1.next();
++            if ( ! iter2.hasNext() )
++                error("Deviation: new B+Tree is smaller") ; 
++            Record r2 = iter2.next();
++    
++            if ( ! Record.equals(r1, r2) )
++                error("Deviation in iteration record %d: %s : %s", count, r1, r2) ;
++        }
++        if ( iter2.hasNext() )
++            error("New B+Tree larger than original") ; 
++    }
++
++    public static void findComparison(List<Record> originaldata, BPlusTree bpt2)
++    {
++        Iterator<Record> iter1 = originaldata.iterator() ;
++    
++        long count = 0 ;
++        for ( ; iter1.hasNext() ; )
++        {
++            count++ ;
++            Record r1 = iter1.next();
++            
++            Record r3 = bpt2.find(r1) ;
++            if ( r3 == null )
++            {
++                r3 = bpt2.find(r1) ;
++                error("Deviation in find at record %d: %s : null", count, r1) ;
++            }
++            if ( ! Record.equals(r1, r3) )
++                error("Deviation in find at record %d: %s : %s", count, r1, r3) ;
++        }
++    
++    }
++
++    public static void sizeComparison(List<Record> originaldata, BPlusTree bpt2)
++    {
++      long count1 = originaldata.size() ;
++      long count2 = bpt2.size() ;
++      //System.out.printf("Sizes = %d / %d\n", count1, count2) ;
++      if ( count1 != count2 )
++          // Not error - this test does not identify why there was a problem so continue.
++          System.err.println("**** DIFFERENT") ;
++    }
++
++    static List<Record> createData(int N, RecordFactory recordFactory)
++    {
++        List<Record> originaldata = new ArrayList<>(N) ;
++        for ( int i = 0; i < N ; i++ )
++        {
++            Record record = recordFactory.create() ;
++            Bytes.setInt(i+1, record.getKey()) ;
++            if ( recordFactory.hasValue() )
++                Bytes.setInt(10*i+1, record.getValue()) ;
++            originaldata.add(record) ;
++        }
++        return originaldata ;
++    }
++    
++    private static void error(String string, Object ...args)
++    {
++        String msg = String.format(string, args) ;
++        System.err.println(msg) ;
++        throw new BPTreeException(msg) ;
++    }
++}


[49/50] [abbrv] jena git commit: JENA-1396: Merge //github/afs/mantis as subdirectory jena-db/

Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrFactory.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrFactory.java
index 0000000,0000000..583c490
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrFactory.java
@@@ -1,0 -1,0 +1,150 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.block ;
++
++import org.apache.jena.atlas.logging.Log ;
++import org.seaborne.dboe.DBOpEnvException ;
++import org.seaborne.dboe.base.file.* ;
++import org.seaborne.dboe.sys.SystemIndex ;
++
++public class BlockMgrFactory {
++    // This isn't always helpful so be careful if setting the default to "true".
++    // Sometimes the tracking is too strict
++    // e.g. transactions keep blocks and not release them down the layers.
++    public/* final */static boolean AddTracker = false ;
++
++    public static BlockMgr tracker(BlockMgr blockMgr) {
++        if ( blockMgr instanceof BlockMgrTracker )
++            return blockMgr ;
++        return BlockMgrTracker.track(blockMgr) ;
++    }
++
++    /** Add a tracker if the system default is to do so */
++    private static BlockMgr track(BlockMgr blockMgr) {
++        if ( !AddTracker )
++            return blockMgr ;
++        return tracker(blockMgr) ;
++    }
++
++    public static BlockMgr create(FileSet fileSet, String ext, BlockParams params) {
++        return create(fileSet, ext,
++                      params.getFileMode(),
++                      params.getBlockSize(),
++                      params.getBlockReadCacheSize(),
++                      params.getBlockWriteCacheSize()) ;
++    }
++    
++    public static BlockMgr create(FileSet fileSet, String ext, int blockSize, int readBlockCacheSize, int writeBlockCacheSize) {
++        return create(fileSet, ext, null, blockSize, readBlockCacheSize, writeBlockCacheSize) ;
++    }
++
++    // XXX Deprecate?
++    public static BlockMgr create(FileSet fileSet, String ext, FileMode fileMode, int blockSize, int readBlockCacheSize, int writeBlockCacheSize) {
++        if ( fileSet.isMem() )
++            return createMem(fileSet.filename(ext), blockSize) ;
++        else
++            return createFile(fileSet.filename(ext), fileMode, blockSize, readBlockCacheSize, writeBlockCacheSize) ;
++    }
++
++    /** Create an in-memory block manager */
++    public static BlockMgr createMem(String indexName, int blockSize) {
++        BlockAccess file = new BlockAccessMem(indexName, blockSize) ;
++        BlockMgr blockMgr = new BlockMgrFileAccess(file, blockSize) ;
++        blockMgr = new BlockMgrFreeChain(blockMgr) ;
++        // Small cache - testing.
++        blockMgr = BlockMgrCache.create(3, 3, blockMgr) ;
++        return track(blockMgr) ;
++    }
++
++    /** Create a BlockMgr backed by a real file */
++    public static BlockMgr createFile(String filename, BlockParams params) {
++        return createFile(filename, 
++                          params.getFileMode(), params.getBlockSize(),
++                          params.getBlockReadCacheSize(), params.getBlockWriteCacheSize()) ;
++    }
++
++        /** Create a BlockMgr backed by a real file */
++    public static BlockMgr createFile(String filename, FileMode fileMode, int blockSize, int readBlockCacheSize, int writeBlockCacheSize) {
++        if ( fileMode == null )
++            fileMode = SystemIndex.fileMode() ;
++        switch (fileMode) {
++            case mapped :
++                return createMMapFile(filename, blockSize) ;
++            case direct :
++                return createStdFile(filename, blockSize, readBlockCacheSize, writeBlockCacheSize) ;
++        }
++        throw new DBOpEnvException("Unknown file mode: " + fileMode) ;
++    }
++
++    /** Create a NIO Block Manager */
++    public static BlockMgr createMMapFile(String filename, int blockSize) {
++        BlockAccess file = new BlockAccessMapped(filename, blockSize) ;
++        BlockMgr blockMgr = wrapFileAccess(file, blockSize) ;
++        return track(blockMgr) ;
++    }
++
++    /** Create a Block Manager using direct access (and a cache) */
++    public static BlockMgr createStdFile(String filename, int blockSize, int readBlockCacheSize, int writeBlockCacheSize) {
++        BlockAccess file = new BlockAccessDirect(filename, blockSize) ;
++        BlockMgr blockMgr = wrapFileAccess(file, blockSize) ;
++        blockMgr = addCache(blockMgr, readBlockCacheSize, writeBlockCacheSize) ;
++        return track(blockMgr) ;
++    }
++
++    /** Create a Block Manager using direct access, no caching, no nothing. */
++    public static BlockMgr createStdFileNoCache(String filename, int blockSize) {
++        BlockAccess blockAccess = new BlockAccessDirect(filename, blockSize) ;
++        BlockMgr blockMgr = new BlockMgrFileAccess(blockAccess, blockSize) ;
++        return blockMgr ;
++    }
++
++    /**
++     * Add a caching layer to a BlockMgr.
++     * <p>
++     * This does not make sense for memory BlockMgr or for memory mapper files.
++     * This function always add the cache.
++     * 
++     * @see #addCache(BlockMgr, FileSet, FileMode, int, int)
++     */
++    public static BlockMgr addCache(BlockMgr blockMgr, int readBlockCacheSize, int writeBlockCacheSize) {
++        if ( blockMgr instanceof BlockMgrCache )
++            Log.warn(BlockMgrFactory.class, "BlockMgr already has a cache: " + blockMgr.getLabel()) ;
++        return BlockMgrCache.create(readBlockCacheSize, writeBlockCacheSize, blockMgr) ;
++    }
++
++    /**
++     * Add a caching layer to a BlockMgr if appropriate. This does not make
++     * sense for memory BlockMgr or for memory mapper files. These are skipped.
++     */
++    public static BlockMgr addCache(BlockMgr blockMgr, FileSet fileSet, FileMode fileMode, int readBlockCacheSize, int writeBlockCacheSize) {
++        if ( fileSet.isMem() )
++            return blockMgr ;
++        if ( fileMode == null )
++            fileMode = SystemIndex.fileMode() ;
++        if ( fileMode == FileMode.mapped )
++            return blockMgr ;
++        return addCache(blockMgr, readBlockCacheSize, writeBlockCacheSize) ;
++    }
++
++    private static BlockMgr wrapFileAccess(BlockAccess blockAccess, int blockSize) {
++        BlockMgr blockMgr = new BlockMgrFileAccess(blockAccess, blockSize) ;
++        // This is a temporary fix to the problem
++        blockMgr = new BlockMgrFreeChain(blockMgr) ;
++        return blockMgr ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrFileAccess.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrFileAccess.java
index 0000000,0000000..c13ac2c
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrFileAccess.java
@@@ -1,0 -1,0 +1,168 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.block ;
++
++import static java.lang.String.format ;
++
++import org.apache.jena.atlas.lib.InternalErrorException ;
++import org.apache.jena.atlas.lib.Lib ;
++import org.seaborne.dboe.base.file.BlockAccess ;
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++/**
++ * Block manager that maps from the FileAccess layer to a BlockMgr. Add free
++ * block management (but we should layer with BlockMgrFreeChain)
++ */
++
++final public class BlockMgrFileAccess extends BlockMgrBase {
++    private static Logger     log        = LoggerFactory.getLogger(BlockMgrFileAccess.class) ;
++
++    private final BlockAccess file ;
++    private boolean           closed     = false ;
++    // Set on any write operations.
++    private boolean           syncNeeded = false ;
++
++    // Create via the BlockMgrFactory.
++    /* package */BlockMgrFileAccess(BlockAccess blockAccess, int blockSize) {
++        super(blockAccess.getLabel(), blockSize) ;
++        file = blockAccess ;
++    }
++
++    @Override
++    protected Block allocate() {
++        syncNeeded = true ;
++        return file.allocate(blockSize) ;
++    }
++
++    @Override
++    public Block promote(Block block) {
++        return block ;
++    }
++
++    @Override
++    public Block getRead(long id) {
++        return getBlock(id, true) ;
++    }
++
++    @Override
++    public Block getWrite(long id) {
++        return getBlock(id, false) ;
++    }
++
++    private Block getBlock(long id, boolean readOnly) {
++        checkNotClosed() ;
++        Block block = file.read(id) ;
++        block.setReadOnly(readOnly) ;
++        return block ;
++    }
++
++    private void checkNotClosed() {
++        if ( closed )
++            throw new InternalErrorException(Lib.className(this)+" : already closed") ;
++    }
++
++    @Override
++    public void release(Block block) {
++        checkNotClosed() ;
++    }
++
++    @Override
++    public void write(Block block) {
++        checkNotClosed() ;
++        if ( block.isReadOnly() )
++            throw new BlockException("Attempt to write a read-only block ("+block.getId()+")" ) ;
++        syncNeeded = true ;
++        file.write(block) ;
++    }
++
++    @Override
++    public void overwrite(Block block) {
++        checkNotClosed() ;
++        syncNeeded = true ;
++        file.overwrite(block) ;
++    }
++
++    @Override
++    public void free(Block block) {
++        checkNotClosed() ;
++        // syncNeeded = true ;
++        // We do nothing about free blocks currently.
++    }
++
++    @Override
++    public boolean valid(int id) {
++        checkNotClosed() ;
++        return file.valid(id) ;
++    }
++
++    @Override
++    public void sync() {
++        checkNotClosed() ;
++        if ( syncNeeded )
++            file.sync() ;
++        else
++            syncNeeded = true ;
++        syncNeeded = false ;
++    }
++
++    @Override
++    public void syncForce() {
++        checkNotClosed() ;
++        file.sync() ;
++    }
++
++    @Override
++    public boolean isClosed() {
++        return closed ;
++    }
++
++    @Override
++    public void close() {
++        closed = true ;
++        file.close() ;
++    }
++
++    @Override
++    public boolean isEmpty() {
++        checkNotClosed() ;
++        return file.isEmpty() ;
++    }
++    
++    @Override
++    public long allocLimit() {
++        checkNotClosed() ;
++        return file.allocBoundary() ;
++    }
++    
++    @Override
++    public void resetAlloc(long boundary) {
++        checkNotClosed() ;
++        file.resetAllocBoundary(boundary);
++    }
++
++    @Override
++    public String toString() {
++        return format("BlockMgrFileAccess[%d bytes]:%s", blockSize, file) ;
++    }
++
++    @Override
++    protected Logger log() {
++        return log ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrFreeChain.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrFreeChain.java
index 0000000,0000000..b131507
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrFreeChain.java
@@@ -1,0 -1,0 +1,83 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.block ;
++
++import java.util.ArrayDeque ;
++import java.util.Deque ;
++
++/**
++ * Recycle blocks - but only in-session. At the end of JVM run, the blocks are
++ * made "permanent" as no one finds them again on restart.
++ */
++final public class BlockMgrFreeChain extends BlockMgrWrapper {
++    // Could keep Pair<Integer, ByteBuffer>
++    // List<Block> freeBlocks = new ArrayList<Block>() ;
++    private final Deque<Block> freeBlocks = new ArrayDeque<>() ;    // Keep as heap?
++
++    public BlockMgrFreeChain(BlockMgr blockMgr) {
++        super(blockMgr) ;
++    }
++
++    @Override
++    public Block allocate(int blockSize) {
++        if ( !freeBlocks.isEmpty() ) {
++            Block block = freeBlocks.removeFirst() ;
++            block.getByteBuffer().position(0) ;
++            return block ;
++        }
++        return super.allocate(blockSize) ;
++    }
++
++    @Override
++    public void free(Block block) {
++        if ( block.getId() >= blockMgr.allocLimit() )
++            freeBlocks.add(block) ;
++    }
++
++    @Override
++    public void resetAlloc(long boundary) {
++        super.resetAlloc(boundary);
++        // Just clear - assumes this is effectively a transaction boundary.
++        freeBlocks.clear(); 
++//        Iterator<Block> iter = freeBlocks.iterator() ;
++//        while(iter.hasNext()) {
++//            Block blk = iter.next() ;
++//            if ( blk.getId() < boundary )
++//                iter.remove();
++//        }
++    }
++    @Override
++    public boolean valid(int id) {
++        for ( Block blk : freeBlocks ) {
++            if ( blk.getId() == id )
++                return true ;
++        }
++        return super.valid(id) ;
++    }
++
++    @Override
++    public void sync() {
++        // Flush free blocks?
++        super.sync() ;
++    }
++
++    @Override
++    public String toString() {
++        return "Free:" + super.toString() ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrLogger.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrLogger.java
index 0000000,0000000..bd218b8
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrLogger.java
@@@ -1,0 -1,0 +1,180 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.block ;
++
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++public class BlockMgrLogger implements BlockMgr // extends BlockMgrWrapper
++{
++    private final BlockMgr  blockMgr ;
++    protected final Logger  log ;
++    protected final boolean logAllOperations ;
++    private final String    label ;
++
++    public BlockMgrLogger(BlockMgr blockMgr, boolean logAllOperations) {
++        this(null, blockMgr.getLabel(), blockMgr, logAllOperations) ;
++    }
++
++    public BlockMgrLogger(String label, BlockMgr blockMgr, boolean logAllOperations) {
++        this(null, label, blockMgr, logAllOperations) ;
++    }
++
++    public BlockMgrLogger(Logger log, String label, BlockMgr blockMgr, boolean logAllOperations) {
++        this.blockMgr = blockMgr ;
++        if ( log == null )
++            log = LoggerFactory.getLogger(BlockMgr.class) ;
++        this.log = log ;
++        this.logAllOperations = logAllOperations ;
++        if ( label == null )
++            label = blockMgr.getLabel() ; 
++        this.label = label ;
++    }
++
++    @Override
++    public String getLabel() {
++        return label ;
++    }
++
++    @Override
++    public Block allocate(int blockSize) {
++        Block x = blockMgr.allocate(blockSize) ;
++        info("Allocate(" + x.getId() + ")") ;
++        return x ;
++    }
++
++    @Override
++    public boolean isEmpty() {
++        info("isEmpty()") ;
++        return blockMgr.isEmpty() ;
++    }
++
++    @Override
++    public long allocLimit() {
++        info("limit()") ;
++        return blockMgr.allocLimit() ;
++    }
++
++    @Override
++    public void resetAlloc(long boundary) {
++        info("resetAlloc("+boundary+")") ;
++        blockMgr.resetAlloc(boundary) ;
++    }
++    
++    @Override
++    public Block getRead(long id) {
++        info("getRead(" + id + ")") ;
++        return blockMgr.getRead(id) ;
++    }
++
++    @Override
++    public Block getWrite(long id) {
++        info("getWrite(" + id + ")") ;
++        return blockMgr.getWrite(id) ;
++    }
++
++    @Override
++    public Block promote(Block block) {
++        info("promote(" + block.getId() + ")") ;
++        return blockMgr.promote(block) ;
++    }
++
++    @Override
++    public void release(Block block) {
++        info("release(" + block.getId() + ")") ;
++        blockMgr.release(block) ;
++    }
++
++    @Override
++    public void write(Block block) {
++        info("write(" + block.getId() + ")") ;
++        blockMgr.write(block) ;
++    }
++
++    @Override
++    public void overwrite(Block block) {
++        info("overwrite(" + block.getId() + ")") ;
++        blockMgr.overwrite(block) ;
++    }
++
++    @Override
++    public void free(Block block) {
++        info("freeBlock(" + block.getId() + ")") ;
++        blockMgr.free(block) ;
++    }
++
++    @Override
++    public boolean valid(int id) {
++        info("valid(" + id + ")") ;
++        return blockMgr.valid(id) ;
++    }
++
++    @Override
++    public void close() {
++        info("close") ;
++        blockMgr.close() ;
++    }
++
++    @Override
++    public boolean isClosed() {
++        info("isClosed") ;
++        return blockMgr.isClosed() ;
++    }
++
++    @Override
++    public void sync() {
++        info("sync") ;
++        blockMgr.sync() ;
++    }
++
++    @Override
++    public void syncForce() {
++        info("syncForce") ;
++        blockMgr.syncForce() ;
++    }
++
++    @Override
++    public void beginRead() {
++        info("> start read") ;
++        blockMgr.beginRead() ;
++    }
++
++    @Override
++    public void endRead() {
++        info("< finish read") ;
++        blockMgr.endRead() ;
++    }
++
++    @Override
++    public void beginUpdate() {
++        info("> start update") ;
++        blockMgr.beginUpdate() ;
++    }
++
++    @Override
++    public void endUpdate() {
++        info("< finish update") ;
++        blockMgr.endUpdate() ;
++    }
++
++    private void info(String string) {
++        if ( label != null )
++            string = label + ": " + string ;
++        log.info(string) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrReadonly.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrReadonly.java
index 0000000,0000000..eca86af
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrReadonly.java
@@@ -1,0 -1,0 +1,44 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.block;
++
++import org.seaborne.dboe.DBOpEnvException ;
++
++public class BlockMgrReadonly extends BlockMgrWrapper
++{
++    public BlockMgrReadonly(BlockMgr blockMgr) {
++        super(blockMgr) ;
++    }
++
++    @Override public void beginUpdate()                 { throw new DBOpEnvException("Read-only block manager") ; }
++    @Override public void endUpdate()                   { throw new DBOpEnvException("Read-only block manager") ; }
++    @Override public Block allocate(int blockSize)      { throw new DBOpEnvException("Read-only block manager") ; }
++    @Override public Block getWrite(long id)            { throw new DBOpEnvException("Read-only block manager") ; }
++    @Override public Block promote(Block block)         { throw new DBOpEnvException("Read-only block manager") ; }
++    @Override public void write(Block block)            { throw new DBOpEnvException("Read-only block manager") ; }
++    @Override public void overwrite(Block block)        { throw new DBOpEnvException("Read-only block manager") ; }
++    @Override public void free(Block block)             { throw new DBOpEnvException("Read-only block manager") ; }
++    
++//    @Override 
++//    public void sync()
++//    {
++//        blockMgr.sync() ;
++//    }
++
++    @Override public String toString()          { return "RO:"+super.toString() ; } 
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrSwitcher.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrSwitcher.java
index 0000000,0000000..1a05120
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrSwitcher.java
@@@ -1,0 -1,0 +1,39 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.block;
++
++public class BlockMgrSwitcher extends BlockMgrWrapper 
++{
++    protected final BlockMgr blockMgr1 ;
++    protected final BlockMgr blockMgr2 ;
++
++    public BlockMgrSwitcher(BlockMgr blockMgr1, BlockMgr blockMgr2)
++    {
++        super(blockMgr1) ;
++        this.blockMgr1 = blockMgr1 ;
++        this.blockMgr2 = blockMgr2 ;
++    }
++
++    public void switchover()
++    {
++        if ( super.blockMgr == blockMgr1 )
++            setBlockMgr(blockMgr2) ;
++        else
++            setBlockMgr(blockMgr1) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrSync.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrSync.java
index 0000000,0000000..492f543
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrSync.java
@@@ -1,0 -1,0 +1,148 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.block ;
++
++
++/**
++ * Add synchronized to a BlockMgr. This is the same as BlockMgrWrapper but with
++ * 'synchronized' added
++ */
++
++public class BlockMgrSync implements BlockMgr {
++    protected final BlockMgr blockMgr ;
++
++    public BlockMgrSync(BlockMgr blockMgr) {
++        this.blockMgr = blockMgr ;
++    }
++
++    public BlockMgr getWrapped() {
++        return blockMgr ;
++    }
++
++    @Override
++    synchronized public Block allocate(int blockSize) {
++        return blockMgr.allocate(blockSize) ;
++    }
++
++    @Override
++    synchronized public Block getRead(long id) {
++        return blockMgr.getRead(id) ;
++    }
++
++    @Override
++    synchronized public Block getWrite(long id) {
++        return blockMgr.getWrite(id) ;
++    }
++
++    @Override
++    synchronized public Block promote(Block block) {
++        return blockMgr.promote(block) ;
++    }
++
++    @Override
++    synchronized public void release(Block block) {
++        blockMgr.release(block) ;
++    }
++
++    @Override
++    synchronized public void write(Block block) {
++        blockMgr.write(block) ;
++    }
++
++    @Override
++    synchronized public void overwrite(Block block) {
++        blockMgr.overwrite(block) ;
++    }
++
++    @Override
++    synchronized public void free(Block block) {
++        blockMgr.free(block) ;
++    }
++
++    @Override
++    synchronized public void sync() {
++        blockMgr.sync() ;
++    }
++
++    @Override
++    synchronized public void syncForce() {
++        blockMgr.syncForce() ;
++    }
++
++    @Override
++    synchronized public void close() {
++        blockMgr.close() ;
++    }
++
++    @Override
++    synchronized public boolean isEmpty() {
++        return blockMgr.isEmpty() ;
++    }
++
++    @Override
++    synchronized public long allocLimit() {
++        return blockMgr.allocLimit() ;
++    }
++
++    
++    @Override
++    synchronized public void resetAlloc(long boundary) {
++        blockMgr.resetAlloc(boundary) ;
++    }
++    
++    @Override
++    synchronized public void beginRead() {
++        blockMgr.beginRead() ;
++    }
++
++    @Override
++    synchronized public void endRead() {
++        blockMgr.endRead() ;
++    }
++
++    @Override
++    synchronized public void beginUpdate() {
++        blockMgr.beginUpdate() ;
++    }
++
++    @Override
++    synchronized public void endUpdate() {
++        blockMgr.endUpdate() ;
++    }
++
++    @Override
++    synchronized public boolean valid(int id) {
++        return blockMgr.valid(id) ;
++    }
++
++    @Override
++    synchronized public boolean isClosed() {
++        return blockMgr.isClosed() ;
++    }
++
++    @Override
++    synchronized public String getLabel() {
++        return blockMgr.getLabel() ;
++    }
++
++    @Override
++    public String toString() {
++        return "Sync:" + blockMgr.toString() ;
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrTracker.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrTracker.java
index 0000000,0000000..588a455
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrTracker.java
@@@ -1,0 -1,0 +1,413 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.block ;
++
++import static org.seaborne.dboe.base.block.BlockMgrTracker.Action.Alloc ;
++import static org.seaborne.dboe.base.block.BlockMgrTracker.Action.BeginRead ;
++import static org.seaborne.dboe.base.block.BlockMgrTracker.Action.BeginUpdate ;
++import static org.seaborne.dboe.base.block.BlockMgrTracker.Action.EndRead ;
++import static org.seaborne.dboe.base.block.BlockMgrTracker.Action.EndUpdate ;
++import static org.seaborne.dboe.base.block.BlockMgrTracker.Action.Free ;
++import static org.seaborne.dboe.base.block.BlockMgrTracker.Action.GetRead ;
++import static org.seaborne.dboe.base.block.BlockMgrTracker.Action.GetWrite ;
++import static org.seaborne.dboe.base.block.BlockMgrTracker.Action.Promote ;
++import static org.seaborne.dboe.base.block.BlockMgrTracker.Action.Release ;
++import static org.seaborne.dboe.base.block.BlockMgrTracker.Action.Write ;
++
++import java.util.ArrayList ;
++import java.util.List ;
++
++import org.apache.jena.atlas.lib.Pair ;
++import org.apache.jena.ext.com.google.common.collect.HashMultiset ;
++import org.apache.jena.ext.com.google.common.collect.Multiset ;
++import org.seaborne.dboe.DBOpEnvException ;
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++public class BlockMgrTracker /* extends BlockMgrWrapper */ implements BlockMgr {
++    private static Logger loggerDefault = LoggerFactory.getLogger(BlockMgrTracker.class) ;
++    public static boolean verbose       = false ;
++
++    static enum Action {
++        Alloc, Promote, GetRead, GetWrite, Write, Release, Free, 
++        BeginRead, EndRead, 
++        BeginUpdate, EndUpdate
++    }
++    static final Long                        NoId              = (long)-9 ;
++
++    // ---- State for tracking
++    // Track and count block references and releases
++    // No - the page is dirty.
++    
++    
++    protected final Multiset<Long>           activeReadBlocks  = HashMultiset.create() ;
++    protected final Multiset<Long>           activeWriteBlocks = HashMultiset.create() ;
++    // Track the operations
++    protected final List<Pair<Action, Long>> actions           = new ArrayList<>() ;
++    // ---- State for tracking
++
++    protected final BlockMgr                 blockMgr ;
++
++    private void clearInternalRW() {
++        activeReadBlocks.clear() ;
++        activeWriteBlocks.clear() ;
++        actions.clear() ;
++    }
++
++    private void clearInternalIter() {
++        clearInternalRW() ;
++    }
++
++    private int          inRead     = 0 ;
++    private boolean      inUpdate   = false ;
++    private boolean      inBatch    = false ;
++    private final Logger log ;
++    private final String label ;
++
++    public static BlockMgr track(BlockMgr blkMgr) {
++        return track(blkMgr.getLabel(), blkMgr) ;
++    }
++
++    private static BlockMgr track(String label, BlockMgr blkMgr) {
++        return new BlockMgrTracker(label, blkMgr) ;
++    }
++
++    private BlockMgrTracker(BlockMgr blockMgr) {
++        this(LoggerFactory.getLogger(BlockMgrTracker.class), blockMgr.getLabel(), blockMgr) ;
++    }
++
++    private BlockMgrTracker(String label, BlockMgr blockMgr) {
++        this(loggerDefault, label, blockMgr) ;
++    }
++
++    //
++    // public BlockMgrTracker(Class<?> cls, String label, BlockMgr blockMgr)
++    // {
++    // this(LoggerFactory.getLogger(cls), label, blockMgr) ;
++    // }
++
++    private BlockMgrTracker(Logger logger, String label, BlockMgr blockMgr) {
++        this.blockMgr = blockMgr ;
++        this.log = logger ;
++        this.label = blockMgr.getLabel() ;
++    }
++
++    private void add(Action action, Long id) {
++        actions.add(new Pair<>(action, id)) ;
++    }
++
++    @Override
++    public Block allocate(int blockSize) {
++        Block block ;
++        synchronized (this) {
++            checkUpdate(Alloc) ;
++            block = blockMgr.allocate(blockSize) ;
++            Long id = block.getId() ;
++            activeWriteBlocks.add(id) ;
++            add(Alloc, id) ;
++        }
++        return block ;
++    }
++
++    @Override
++    public Block getRead(long id) {
++        // What if this is a write block already?
++        synchronized (this) {
++            checkRead(GetRead) ;
++            Long x = id ;
++            add(GetRead, x) ;
++
++            if ( activeWriteBlocks.contains(x) )
++                activeWriteBlocks.add(x) ;
++            else
++                activeReadBlocks.add(x) ;
++        }
++        return blockMgr.getRead(id) ;
++    }
++
++    @Override
++    public Block getWrite(long id) {
++        synchronized (this) {
++            checkUpdate(GetWrite) ;
++            Long x = id ;
++            add(GetWrite, x) ;
++            activeWriteBlocks.add(x) ;
++        }
++        return blockMgr.getWrite(id) ;
++    }
++
++    @Override
++    public Block promote(Block block) {
++        synchronized (this) {
++            checkUpdate(Promote) ;
++            Long id = block.getId() ;
++            add(Promote, id) ;
++
++            if ( !activeWriteBlocks.contains(id) && !activeReadBlocks.contains(id) )
++                error(Promote, id + " is not an active block") ;
++
++            if ( activeReadBlocks.contains(id) )
++                // Remove one read count
++                // Really, do this if obtained via "getRead" and not "getWrite"
++                activeReadBlocks.remove(id) ;
++
++            // Double promotion results in only one entry.
++            if ( !activeWriteBlocks.contains(id) )
++                activeWriteBlocks.add(id) ;
++        }
++        return blockMgr.promote(block) ;
++    }
++
++    @Override
++    public void release(Block block) {
++        synchronized (this) {
++            checkRead(Release) ;
++            Long id = block.getId() ;
++            add(Release, id) ;
++
++            if ( !activeReadBlocks.contains(id) && !activeWriteBlocks.contains(id) )
++                error(Release, id + " is not an active block") ;
++
++            // May have been promoted.
++            if ( activeWriteBlocks.contains(id) )
++                activeWriteBlocks.remove(id) ;
++            else
++                activeReadBlocks.remove(block.getId()) ;
++        }
++        blockMgr.release(block) ;
++    }
++
++    @Override
++    public void write(Block block) {
++        writeTracker(block) ;
++        blockMgr.write(block) ;
++    }
++
++    @Override
++    synchronized public void overwrite(Block block) {
++        writeTracker(block) ;
++        blockMgr.overwrite(block) ;
++    }
++
++    private void writeTracker(Block block) {
++        synchronized (this) {
++            checkUpdate(Write) ;
++            Long id = block.getId() ;
++            add(Write, id) ;
++            if ( !activeWriteBlocks.contains(id) )
++                error(Write, id + " is not an active write block") ;
++        }
++    }
++
++    @Override
++    public void free(Block block) {
++        synchronized (this) {
++            checkUpdate(Free) ;
++            Long id = block.getId() ;
++            add(Free, id) ;
++            if ( activeReadBlocks.contains(id) )
++                error(Free, id + " is a read block") ;
++            else if ( !activeWriteBlocks.contains(id) )
++                error(Free, id + " is not a write block") ;
++            activeWriteBlocks.remove(id) ;
++        }
++        blockMgr.free(block) ;
++    }
++
++    @Override
++    public void sync() {
++        blockMgr.sync() ;
++    }
++
++    @Override
++    public void syncForce() {
++        blockMgr.syncForce() ;
++    }
++
++    @Override
++    public void close() {
++        blockMgr.close() ;
++    }
++
++    @Override
++    public boolean isEmpty() {
++        return blockMgr.isEmpty() ;
++    }
++    
++    @Override
++    public long allocLimit() {
++        return blockMgr.allocLimit() ;
++    }
++    
++    @Override
++    public void resetAlloc(long boundary) {
++        blockMgr.resetAlloc(boundary) ;
++    }
++    
++    @Override
++    public boolean valid(int id) {
++        return blockMgr.valid(id) ;
++    }
++
++    @Override
++    public boolean isClosed() {
++        return blockMgr.isClosed() ;
++    }
++
++    @Override
++    synchronized public void beginRead() {
++        synchronized (this) {
++            if ( inUpdate )
++                error(BeginRead, "beginRead when already in update") ;
++            inRead++ ;
++            inUpdate = false ;
++        }
++        blockMgr.beginRead() ;
++    }
++
++    @Override
++    synchronized public void endRead() {
++        synchronized (this) {
++            if ( inRead == 0 )
++                error(EndRead, "endRead but not in read") ;
++            if ( inUpdate )
++                error(EndRead, "endRead when in update") ;
++
++            checkEmpty("Outstanding write blocks at end of read operations!", activeWriteBlocks) ;
++
++            if ( inRead == 0 ) {
++                // Check at end of multiple reads or a write
++                checkEmpty("Outstanding read blocks at end of read operations", activeReadBlocks) ;
++                clearInternalRW() ;
++            }
++
++            inUpdate = false ;
++            inRead-- ;
++        }
++        blockMgr.endRead() ;
++    }
++
++    @Override
++    public void beginUpdate() {
++        synchronized (this) {
++            if ( inRead > 0 )
++                error(BeginUpdate, "beginUpdate when already in read") ;
++            if ( inUpdate )
++                error(BeginUpdate, "beginUpdate when already in update") ;
++            inUpdate = true ;
++        }
++        blockMgr.beginUpdate() ;
++    }
++
++    @Override
++    public void endUpdate() {
++        synchronized (this) {
++            if ( !inUpdate )
++                error(EndUpdate, "endUpdate but not in update") ;
++            if ( inRead > 0 )
++                error(EndUpdate, "endUpdate when in read") ;
++
++            checkEmpty("Outstanding read blocks at end of update operations", activeReadBlocks) ;
++
++            checkEmpty("Outstanding write blocks at end of update operations", activeWriteBlocks) ;
++
++            inUpdate = false ;
++            inRead = 0 ;
++            clearInternalRW() ;
++        }
++        blockMgr.endUpdate() ;
++    }
++
++    private void checkUpdate(Action action) {
++//        if ( ! inBatch )
++//            warn(action, "update called outside a batch") ;
++        if ( ! inUpdate )
++            error(action, "called outside update") ;
++    }
++
++    private void checkRead(Action action) {
++        if ( !inUpdate && inRead == 0 )
++            error(action, "Called outside update and read") ;
++    }
++
++    private void checkEmpty(String string, Multiset<Long> blocks) {
++        if ( !blocks.isEmpty() ) {
++            error(string) ;
++            for ( Long id : blocks )
++                info("    Block: " + id) ;
++            history() ;
++            throw new DBOpEnvException() ;
++            // debugPoint() ;
++        }
++    }
++
++    private String msg(String string) {
++        if ( label == null )
++            return string ;
++        return label + ": " + string ;
++    }
++
++    private void info(String string) {
++        log.info(msg(string)) ;
++    }
++
++    private void warn(String string) {
++        log.warn(msg(string)) ;
++    }
++
++    private void warn(Action action, String string) {
++        warn(action + ": " + string) ;
++    }
++
++    private void error(String string) {
++        log.error(msg(string)) ;
++    }
++
++    private void error(Action action, String string) {
++        if ( verbose ) {
++            error(action + ": " + string) ;
++            history() ;
++        }
++        throw new BlockException(msg(action + ": " + string)) ;
++        // debugPoint() ;
++    }
++
++    // Do nothing - but use a a breakpoint point.
++    private void debugPoint() {}
++
++    private void history() {
++        info("History") ;
++        for ( Pair<Action, Long> p : actions ) {
++            if ( p.getRight() != NoId )
++                log.info(String.format("%s:     %-12s  %d", label, p.getLeft(), p.getRight())) ;
++            else
++                log.info(String.format("%s:     %-12s", label, p.getLeft())) ;
++
++        }
++    }
++
++    @Override
++    public String toString() {
++        return "BlockMgrTracker" + ((label == null) ? "" : (": " + label)) ;
++    }
++
++    @Override
++    public String getLabel() {
++        return label ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrWrapper.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrWrapper.java
index 0000000,0000000..029d309
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockMgrWrapper.java
@@@ -1,0 -1,0 +1,148 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.block ;
++
++
++public class BlockMgrWrapper implements BlockMgr {
++    protected BlockMgr blockMgr ;
++
++    public BlockMgrWrapper(BlockMgr blockMgr) {
++        setBlockMgr(blockMgr) ;
++    }
++
++    /** Set another BlockMgr as the target of the wrapper - return the old one */
++    protected final BlockMgr setBlockMgr(BlockMgr blockMgr) {
++        BlockMgr old = this.blockMgr ;
++        this.blockMgr = blockMgr ;
++        return old ;
++    }
++
++    public BlockMgr getWrapped() {
++        return blockMgr ;
++    }
++
++    @Override
++    public Block allocate(int blockSize) {
++        return blockMgr.allocate(blockSize) ;
++    }
++
++    @Override
++    public Block getRead(long id) {
++        return blockMgr.getRead(id) ;
++    }
++
++    @Override
++    public Block getWrite(long id) {
++        return blockMgr.getWrite(id) ;
++    }
++
++    @Override
++    public Block promote(Block block) {
++        return blockMgr.promote(block) ;
++    }
++
++    @Override
++    public void release(Block block) {
++        blockMgr.release(block) ;
++    }
++
++    @Override
++    public void write(Block block) {
++        blockMgr.write(block) ;
++    }
++
++    @Override
++    public void overwrite(Block block) {
++        blockMgr.overwrite(block) ;
++    }
++
++    @Override
++    public void free(Block block) {
++        blockMgr.free(block) ;
++    }
++
++    @Override
++    public boolean isEmpty() {
++        return blockMgr.isEmpty() ;
++    }
++    
++    @Override
++    public long allocLimit() {
++        return blockMgr.allocLimit() ;
++    }
++    
++    @Override
++    public void resetAlloc(long boundary) {
++        blockMgr.resetAlloc(boundary) ;
++    }
++    
++    @Override
++    public void sync() {
++        blockMgr.sync() ;
++    }
++
++    @Override
++    public void syncForce() {
++        blockMgr.syncForce() ;
++    }
++
++    @Override
++    public boolean valid(int id) {
++        return blockMgr.valid(id) ;
++    }
++
++    @Override
++    public boolean isClosed() {
++        return blockMgr.isClosed() ;
++    }
++
++    @Override
++    public void close() {
++        blockMgr.close() ;
++    }
++
++    @Override
++    public void beginRead() {
++        blockMgr.beginRead() ;
++    }
++
++    @Override
++    public void endRead() {
++        blockMgr.endRead() ;
++    }
++
++    @Override
++    public void beginUpdate() {
++        blockMgr.beginUpdate() ;
++    }
++
++    @Override
++    public void endUpdate() {
++        blockMgr.endUpdate() ;
++    }
++
++    @Override
++    public String getLabel() {
++        return blockMgr.getLabel() ;
++    }
++
++    @Override
++    public String toString() {
++        return blockMgr.toString() ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockParams.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockParams.java
index 0000000,0000000..28afbfa
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockParams.java
@@@ -1,0 -1,0 +1,26 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.block;
++
++public interface BlockParams {
++    public FileMode getFileMode() ;
++    public Integer getBlockSize() ;
++    public Integer getBlockReadCacheSize() ;
++    public Integer getBlockWriteCacheSize() ;
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockType.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockType.java
index 0000000,0000000..0acc6b1
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/BlockType.java
@@@ -1,0 -1,0 +1,68 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.block;
++
++import org.apache.jena.atlas.io.IndentedWriter ;
++import org.apache.jena.atlas.io.Printable ;
++import org.seaborne.dboe.DBOpEnvException ;
++
++import org.apache.jena.sparql.util.Named ;
++
++public enum BlockType implements Printable, Named
++{
++    // The id should fit into an unsigned byte.
++    FREE(-1, "Free"), 
++    BTREE_NODE(5, "BTreeNode") ,
++    BPTREE_BRANCH(6, "BPlusTreeBranch") ,
++    BPTREE_LEAF(7, "BPlusTreeLeaf") ,
++    DICTIONARY(10, "Dictionary") ,
++    RECORD_BLOCK(99, "RecordBlock"),
++    UNDEF(255, "UndefinedBlockType")
++    ;
++
++    private final int id ;
++    private final String name ;
++
++    BlockType(int id, String name)
++    {
++        this.id = id ;
++        this.name = name ;
++        
++    }
++    
++    @Override
++    public void output(IndentedWriter out)
++    { out.print(getName()) ; }
++
++    final public int id() { return id ; }
++    
++    @Override
++    final public String getName() { return name ; }
++    
++    @Override public String toString() { return getName() ; }
++    
++    public static BlockType extract(int x)
++    {
++        if ( x == BTREE_NODE.id() )         return BTREE_NODE ;
++        if ( x == BPTREE_BRANCH.id() )      return BPTREE_BRANCH ;
++        if ( x == BPTREE_LEAF.id() )        return BPTREE_LEAF ;
++        if ( x == RECORD_BLOCK.id() )       return RECORD_BLOCK ;
++        if ( x == DICTIONARY.id() )         return DICTIONARY ;
++        throw new DBOpEnvException("No known block type for "+x) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/FileMode.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/FileMode.java
index 0000000,0000000..c6531a6
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/block/FileMode.java
@@@ -1,0 -1,0 +1,26 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.block;
++
++public enum FileMode
++{
++    /** Use memory mapped files */
++    mapped,
++    /** Use in-JVM caching */
++    direct ;
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/buffer/BufferBase.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/buffer/BufferBase.java
index 0000000,0000000..ca170a4
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/buffer/BufferBase.java
@@@ -1,0 -1,0 +1,262 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.buffer;
++
++import static java.lang.String.format ;
++import static org.seaborne.dboe.sys.SystemIndex.FillByte ;
++import static org.seaborne.dboe.sys.Sys.NetworkOrder ;
++import static org.seaborne.dboe.sys.SystemIndex.NullOut ;
++
++import java.nio.ByteBuffer ;
++
++import org.apache.jena.atlas.lib.ByteBufferLib ;
++import org.seaborne.dboe.base.record.RecordException ;
++
++
++/** ByteBuffer specialization - Common operations for RecordBuffer and PtrBuffer */  
++abstract class BufferBase
++{
++    public static final boolean CheckBuffer = false ;
++    
++    protected ByteBuffer bb ;
++    protected int slotLen ;
++    protected int numSlot ;                 // Number of records in use
++    protected final int maxSlot ;           // Maximum number of records
++
++    protected BufferBase(ByteBuffer bb, int slotLen, int num)
++    {
++        if ( CheckBuffer )
++        {
++            if ( ! bb.order().equals(NetworkOrder) || !bb.hasArray() )
++                throw new RecordException("Duff buffer (byte order is not network order)") ;
++            if ( bb.limit() == 0 )
++                throw new RecordException("Duff buffer (zero length byte buffer)") ;
++            int size = bb.limit() ;
++            int slots = size/slotLen  ;
++            if ( size%slotLen != 0 )
++                throw new RecordException(format("Misaligned buffer: size=%d, keyLen=%d",size, slotLen)) ; 
++            if ( slots < num )
++                throw new RecordException(format("Wrong size: slots=%d, len=%d", slots, num)) ;
++        }
++        this.bb = bb ; 
++        this.slotLen = slotLen ;
++        this.numSlot = num ;
++        this.maxSlot = bb.limit()/slotLen ;
++        if ( ! bb.isReadOnly() && NullOut )
++            clear(numSlot, maxSlot-numSlot) ;
++    }
++    
++    // Why so many final methods?  This code is performace critical and "final" methods
++    // can be inlined by the JIT - or just not have object dispatch done each time
++    // because the destination is fixed.
++    // (and here - there are quite a few assumed relationships of the ints). 
++    
++    final
++    public void copy(int srcIdx, BufferBase dst, int dstIdx, int len)
++    {
++        if ( len == 0 )
++            return ;
++        
++        // Check end index is inside the buffer.
++        checkBounds(srcIdx+len-1, maxSlot) ;
++     
++        BufferBase src = this ;   // Clarity
++
++        if ( dst.numSlot < dstIdx-1 )
++            // Allow copy to be just off the end of dst.
++            throw new IllegalArgumentException(format("copy: Out of bounds: dstIdx=%d, dst size=%d", dstIdx, dst.numSlot)) ;
++        if ( src.slotLen != dst.slotLen )
++            throw new RecordException(format("copy: records of differnt sizes: %d, %d",src.slotLen, dst.slotLen)) ;
++        
++        // How do we set the numRec in dst? max(dstIdx+len, old count)  
++        ByteBufferLib.bbcopy(src.bb, srcIdx, dst.bb, dstIdx, len, slotLen) ;
++        dst.numSlot = Math.max(dstIdx+len, dst.numSlot) ; 
++    }
++    
++    final
++    public void copyToTop(BufferBase dst)
++    { 
++        copy(0, dst, dst.numSlot, numSlot) ; } 
++    
++    /** Remove top element */
++    final public void removeTop()
++    {
++        if ( numSlot == 0 )
++            throw new IndexOutOfBoundsException("removeTop: empty buffer") ; 
++        clear(numSlot-1) ;
++        numSlot-- ;
++    }
++    
++    final public void remove(int idx) { shiftDown(idx) ; }
++    
++    /** Does not reset the size */
++    final
++    public void clear(int idx, int len)
++    {
++        if ( NullOut )
++            ByteBufferLib.bbfill(bb, idx, (idx+len), FillByte, slotLen) ;
++    }
++
++    /** Does not reset the size */
++    final public void clear()           { clear(0, maxSlot) ; numSlot = 0 ; }
++    
++    /** Does not reset the size */
++    final public void clear(int idx)    { clear(idx, 1) ; }
++    
++    /** Is the record at idx set clear or not?
++        This is done without regard to buffer size.
++        Requires NullOut to be accurate.
++        Testing.
++     */
++    final public boolean isClear(int idx)
++    {
++        checkBounds(idx, maxSlot) ;
++        int x = idx*slotLen ;
++        int y = (idx+1)*slotLen ;
++        for ( int i = x ; i < y ; i++ )
++            if ( bb.get(i) != FillByte )
++            {
++//                byte b = bb.get(i) ;
++//                lib.ByteBufferLib.print(bb) ;
++                return false ;
++            }
++        return true ;
++    }
++
++    final public boolean isFull()
++    {
++        return numSlot >= maxSlot ; 
++    }
++
++    final public boolean isEmpty()
++    {
++        return numSlot == 0 ; 
++    }
++    
++    final public void incSize() { incSize(1) ; }
++    final public void incSize(int n)
++    { 
++        if ( numSlot+n > maxSlot )
++            throw new IllegalArgumentException(format("inc(%d): out of range: max=%d", n, maxSlot)) ;
++        numSlot += n ;
++    }
++
++    final public void decSize() { decSize(1) ; }
++    final public void decSize(int n)
++    { 
++        if ( numSlot-n < 0 )
++            throw new IllegalArgumentException(format("dec(%d): out of range: max=%d", n, maxSlot)) ;
++        numSlot -= n ;
++    }
++    
++    final public int slotLen()      { return slotLen ; }
++    
++    final public int getSize()      { return numSlot ; } 
++    
++    final public void setSize(int n)
++    {
++        if ( n < 0 || n > maxSlot )
++            throw new IllegalArgumentException(format("size(%d): out of range: max=%d", n, maxSlot)) ;
++        numSlot = n ;
++    }
++    
++    final public int size()       { return numSlot ; }
++    final public int maxSize()    { return maxSlot ; }
++    
++    final public void shiftUp(int idx) { shiftUpN(idx, 1) ; }
++    final public void shiftUpN(int idx, int num)
++    {
++        checkBounds(idx, numSlot) ;
++        if ( numSlot + num > maxSlot )
++            throw new IllegalArgumentException(format("Shift up(%d): out of range: len=%d max=%d", num, num, maxSlot)) ;
++        
++        ByteBufferLib.bbcopy(bb, idx, idx+num, (numSlot-idx), slotLen) ;       // src, dst 
++        if ( NullOut )
++            clear(idx, num) ;
++
++        numSlot += num ;
++    }
++    
++    final public void shiftDown(int idx) { shiftDownN(idx, 1) ; }
++    final public void shiftDownN(int idx, int num)
++    {
++        checkBounds(idx, numSlot) ;
++        if ( idx+num > numSlot )
++            throw new IllegalArgumentException(format("Shift down(%d,%d): out of range: len=%d", idx, num, num)) ;
++
++        ByteBufferLib.bbcopy(bb, idx+num, idx, (numSlot-num-idx), slotLen) ;       // src, dst
++
++        if ( NullOut )
++            clear(numSlot-num, num) ;
++        numSlot -= num ;
++    }
++    
++//    @Override
++//    final public String toString()
++//    {
++//        StringBuilder str = new StringBuilder() ;
++//        str.append(format("Len=%d Count=%d ", bb.limit()/recLen, num)) ;
++//        
++//        for ( int i = 0 ; i < max*recLen ; i++ )
++//        {
++//            if ( i != 0 && i%recLen == 0 )
++//                str.append(" ") ;
++//            byte b = bb.get(i) ;
++//            str.append(format("%02x", b)) ;
++//        }
++//        return str.toString() ;
++//    }
++
++    /** Move the element from the high end of this to the low end of other */
++    public void shiftRight(BufferBase other)
++    {
++        if ( other.numSlot >= other.maxSlot )
++            throw new BufferException("No space in destination buffer") ;
++        if ( numSlot <= 0 )
++            throw new BufferException("Empty buffer") ;
++
++        if ( other.numSlot > 0 )
++            other.shiftUp(0) ;
++        else
++            other.numSlot++ ;
++        // Copy high to low slot.
++        ByteBufferLib.bbcopy(bb, (numSlot-1), other.bb, 0, 1, slotLen) ;
++        removeTop() ;
++    }
++
++    /** Move the element from the low end of other to the high end of this */
++    public void shiftLeft(BufferBase other)
++    {
++        if ( numSlot >= maxSlot )
++            throw new BufferException("No space in destination buffer") ;
++        if ( other.numSlot <= 0 )
++            throw new BufferException("Empty buffer") ;
++
++        // Copy low to above high slot.
++        ByteBufferLib.bbcopy(other.bb, 0, bb, numSlot, 1, slotLen) ;
++        // Correct length.
++        numSlot ++ ;
++        other.shiftDown(0) ;
++    }
++
++    final private static void checkBounds(int idx, int len)
++    {
++        if ( idx < 0 || idx >= len )
++            throw new BufferException(format("Out of bounds: idx=%d, size=%d", idx, len)) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/buffer/BufferException.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/buffer/BufferException.java
index 0000000,0000000..ee90872
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/buffer/BufferException.java
@@@ -1,0 -1,0 +1,26 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.buffer;
++
++public class BufferException extends RuntimeException
++{
++    public BufferException()                            { super() ; }
++    public BufferException(String msg)                  { super(msg) ; }
++    public BufferException(Throwable th)                { super(th) ; }
++    public BufferException(String msg, Throwable th)    { super(msg, th) ; }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/buffer/PtrBuffer.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/buffer/PtrBuffer.java
index 0000000,0000000..0c084fa
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/buffer/PtrBuffer.java
@@@ -1,0 -1,0 +1,124 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.buffer ;
++
++import static java.lang.String.format ;
++
++import java.nio.ByteBuffer ;
++import java.nio.IntBuffer ;
++
++import org.seaborne.dboe.base.record.RecordException ;
++import org.seaborne.dboe.sys.Sys;
++import org.seaborne.dboe.sys.SystemIndex ;
++
++/** An IntBuffer with extra operations */
++
++final public class PtrBuffer extends BufferBase {
++    private IntBuffer iBuff ;
++
++    private PtrBuffer(int maxRec) {
++        this(ByteBuffer.allocate(SystemIndex.SizeOfPointer * maxRec), 0) ;
++    }
++
++    public PtrBuffer(ByteBuffer bb, int num) {
++        super(bb, SystemIndex.SizeOfPointer, num) ;
++        iBuff = bb.asIntBuffer() ;
++
++        if ( CheckBuffer ) {
++            // It is a IntBuffer with associated ByteBuffer
++            if ( iBuff.position() != 0 || bb.order() != Sys.NetworkOrder )
++                throw new RecordException("Duff pointer buffer") ;
++        }
++    }
++
++    public int get(int idx) {
++        checkBounds(idx, numSlot) ;
++        return _get(idx) ;
++    }
++
++    public int getHigh() {
++        if ( numSlot == 0 )
++            throw new IllegalArgumentException("Empty PtrBuffer") ;
++        return _get(numSlot - 1) ;
++    }
++
++    public int getLow() {
++        if ( numSlot == 0 )
++            throw new IllegalArgumentException("Empty PtrBuffer") ;
++        return _get(0) ;
++    }
++
++    public void add(int val) {
++        add(numSlot, val) ;
++    }
++
++    public void add(int idx, int val) {
++        if ( idx != numSlot ) {
++            checkBounds(idx, numSlot) ;
++            shiftUp(idx) ;
++        } else {
++            if ( numSlot >= maxSlot )
++                throw new BufferException(format("Out of bounds: idx=%d, ptrs=%d", idx, maxSlot)) ;
++            numSlot++ ;
++        }
++        // Add right at the top.
++        _set(idx, val) ;
++    }
++
++    public void set(int idx, int val) {
++        checkBounds(idx, numSlot) ;
++        _set(idx, val) ;
++    }
++
++    private final int _get(int idx) {
++        return iBuff.get(idx) ;
++    }
++
++    private final void _set(int idx, int val) {
++        iBuff.put(idx, val) ;
++    }
++
++    @Override
++    public String toString() {
++        StringBuilder str = new StringBuilder() ;
++        str.append(format("Len=%d Max=%d ", numSlot, maxSlot)) ;
++
++        for ( int i = 0 ; i < numSlot ; i++ ) {
++            if ( i != 0 )
++                str.append(" ") ;
++            int x = _get(i) ;
++            str.append(format("%04d", x)) ;
++        }
++        return str.toString() ;
++    }
++
++    private static void checkBounds(int idx, int len) {
++        if ( idx < 0 || idx >= len )
++            throw new BufferException(format("Out of bounds: idx=%d, ptrs=%d", idx, len)) ;
++    }
++
++    /**
++     * A duplicate which does not share anything with the original - for testing
++     */
++    public PtrBuffer duplicate() {
++        PtrBuffer n = new PtrBuffer(maxSlot) ;
++        copy(0, n, 0, maxSlot) ; // numSlot
++        n.numSlot = numSlot ; // Reset
++        return n ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/buffer/RecordBuffer.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/buffer/RecordBuffer.java
index 0000000,0000000..2a715bf
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/buffer/RecordBuffer.java
@@@ -1,0 -1,0 +1,235 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.buffer ;
++
++import static java.lang.String.format ;
++import static org.apache.jena.atlas.lib.Alg.encodeIndex ;
++
++import java.nio.ByteBuffer ;
++import java.util.Iterator ;
++
++import org.seaborne.dboe.base.record.Record ;
++import org.seaborne.dboe.base.record.RecordFactory ;
++import org.seaborne.dboe.base.record.RecordMapper ;
++
++final public class RecordBuffer extends BufferBase {
++    private RecordFactory factory ;
++
++    // Need own specialized binary search :-(
++
++    public RecordBuffer(RecordFactory recFactory, int maxRec) {
++        this(ByteBuffer.allocate(recFactory.recordLength() * maxRec), recFactory, 0) ;
++    }
++
++    public RecordBuffer(ByteBuffer bb, RecordFactory recFactory, int num) {
++        super(bb, recFactory.recordLength(), num) ;
++        this.factory = recFactory ;
++    }
++
++    public <X> X access(int idx, byte[] key, RecordMapper<X> mapper) {
++        checkBounds(idx, numSlot) ;
++        return _access(idx, key, mapper) ;
++    }
++
++    public Record get(int idx) {
++        checkBounds(idx, numSlot) ;
++        return _get(idx) ;
++    }
++
++    public Record getLow() {
++        if ( numSlot == 0 )
++            throw new IllegalArgumentException("getLow: Empty RecordBuffer") ;
++        return _get(0) ;
++    }
++
++    public Record getHigh() {
++        if ( numSlot == 0 )
++            throw new IllegalArgumentException("getHigh: Empty RecordBuffer") ;
++        return _get(numSlot - 1) ;
++    }
++
++    // Inserts at top.
++    public void add(Record record) {
++        add(numSlot, record) ;
++    }
++
++    // Inserts at slot idx
++    public void add(int idx, Record record) {
++        if ( idx != numSlot ) {
++            checkBounds(idx, numSlot) ;
++            shiftUp(idx) ; // Changes count.
++        } else {
++            if ( numSlot >= maxSlot )
++                throw new BufferException(format("Out of bounds: idx=%d, ptrs=%d", idx, maxSlot)) ;
++            numSlot++ ;
++        }
++        _set(idx, record) ;
++    }
++
++    // Overwrites the contents of slot idx
++    public void set(int idx, Record record) {
++        if ( idx == numSlot ) {
++            add(idx, record) ;
++            return ;
++        } else
++            checkBounds(idx, numSlot) ;
++        _set(idx, record) ;
++    }
++
++    // No checking bound : careful use only!
++
++    public <X> X _access(int idx, byte[] key, RecordMapper<X> mapper) {
++        return factory.access(bb, idx, key, mapper) ;
++    }
++
++    public Record _get(int idx) {
++        return factory.buildFrom(bb, idx) ;
++    }
++
++    // No bounds checking : careful use only!
++    void _set(int idx, Record rec) {
++        factory.insertInto(rec, bb, idx) ;
++    }
++
++    // Linear search for testing.
++    int find1(byte[] data) {
++        for ( int i = 0 ; i < numSlot ; i++ ) {
++            int x = compare(i, data) ;
++            if ( x == 0 )
++                return i ;
++            if ( x > 0 )
++                return encodeIndex(i) ;
++        }
++        return encodeIndex(numSlot) ;
++    }
++
++    // Binary search
++    public int find(Record k) {
++        return find(k, 0, numSlot) ;
++    }
++
++    public Iterator<Record> iterator() {
++        return new RecordBufferIterator(this) ;
++    }
++
++    /** Iterator over a range from min (inclusive) to max(exclusive) */
++    public Iterator<Record> iterator(Record min, Record max) {
++        return new RecordBufferIterator(this, min, max) ;
++    }
++
++    public Record findGet(Record k) {
++        int x = find(k) ;
++        if ( x >= 0 )
++            return get(x) ;
++        return null ;
++    }
++
++    /** return true is removed anything */
++    public boolean removeByKey(Record k) {
++        int x = find(k) ;
++        if ( x < 0 )
++            return false ;
++        super.remove(x) ;
++        return true ;
++    }
++
++    /** Search for key in range fromIndex (inclusive) to toIndex (exclusive) */
++    public int find(Record rec, int fromIndex, int toIndex) {
++        int low = fromIndex ;
++        int high = toIndex - 1 ;
++
++        byte[] key = rec.getKey() ;
++        // http://en.wikipedia.org/wiki/Binary_search
++
++        while (low <= high) {
++            int mid = (low + high) >>> 1 ; // int divide by 2
++
++            int x = compare(mid, key) ;
++            // System.out.printf("Compare: %d(%s) %s ==> %d\n", mid,
++            // Record.str(get(mid)), Record.str(data), x) ;
++
++            if ( x < 0 )
++                low = mid + 1 ;
++            else if ( x > 0 )
++                high = mid - 1 ;
++            else
++                return mid ;
++        }
++        // On exit, when not finding, low is the least value
++        // above, including off the end of the array.
++        return encodeIndex(low) ;
++    }
++
++    // Record compareByKey except we avoid touching bytes by exiting as soon as
++    // possible.
++    // No record created as would be by using
++    // compareByKey(RecordBuffer.get(idx), record)
++    // Compare the slot at idx with value.
++    private int compare(int idx, byte[] value) {
++        idx = idx * slotLen ;
++
++        for ( int i = 0 ; i < value.length ; i++ ) {
++            byte b1 = bb.get(idx + i) ;
++            byte b2 = value[i] ;
++            if ( b1 == b2 )
++                continue ;
++            return (b1 & 0xFF) - (b2 & 0xFF) ;
++        }
++        return 0 ;
++    }
++
++    @Override
++    public String toString() {
++        StringBuilder str = new StringBuilder(40000) ;
++        str.append(format("Len=%d Max=%d: ", numSlot, bb.limit() / slotLen)) ;
++
++        // Print active slots as records.
++        for ( int i = 0 ; i < numSlot ; i++ ) {
++            if ( i != 0 )
++                str.append(" ") ;
++            Record r = _get(i) ;
++            str.append(r.toString()) ;
++        }
++
++        // // Print empty slots
++        // for ( int i = numSlot*slotLen ; i < maxSlot*slotLen ; i++ )
++        // {
++        // if ( i != 0 && i%slotLen == 0 )
++        // str.append(" ") ;
++        // byte b = bb.get(i) ;
++        // str.append(format("%02x", b)) ;
++        // }
++        String s = str.toString() ;
++        return s ;
++    }
++
++    private static void checkBounds(int idx, int len) {
++        if ( idx < 0 || idx >= len )
++            throw new IllegalArgumentException(format("Out of bounds: idx=%d, size=%d", idx, len)) ;
++    }
++
++    /**
++     * A duplicate which does not share anything with the original - for testing
++     */
++    public RecordBuffer duplicate() {
++        RecordBuffer n = new RecordBuffer(factory, maxSlot) ;
++        copy(0, n, 0, maxSlot) ;    // numSlot
++        n.numSlot = numSlot ;       // reset the allocated length
++        return n ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/buffer/RecordBufferIterator.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/buffer/RecordBufferIterator.java
index 0000000,0000000..bef7a7a
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/buffer/RecordBufferIterator.java
@@@ -1,0 -1,0 +1,97 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.buffer;
++
++import static org.apache.jena.atlas.lib.Alg.decodeIndex ;
++
++import java.util.Iterator;
++import java.util.NoSuchElementException;
++
++import org.seaborne.dboe.base.record.Record ;
++
++public class RecordBufferIterator implements Iterator<Record>
++{
++    private RecordBuffer rBuff ;
++    private int nextIdx ;
++    private Record slot = null ;
++    private final Record maxRec ;
++    private final Record minRec ;
++    
++    RecordBufferIterator(RecordBuffer rBuff)
++    { this(rBuff, null, null); }
++    
++    RecordBufferIterator(RecordBuffer rBuff, Record minRecord, Record maxRecord)
++    {
++        this.rBuff = rBuff ;
++        nextIdx = 0 ;
++        minRec = minRecord ;
++        if ( minRec != null )
++        {
++            nextIdx = rBuff.find(minRec) ;
++            if ( nextIdx < 0 )
++                nextIdx = decodeIndex(nextIdx) ;
++        }
++        
++        maxRec = maxRecord ; 
++    }
++
++    private void finish()
++    {
++        rBuff = null ;
++        nextIdx = -99 ;
++        slot = null ;
++    }
++    
++    @Override
++    public boolean hasNext()
++    {
++        if ( slot != null )
++            return true ;
++        if ( nextIdx < 0 )
++            return false ;
++        if ( nextIdx >= rBuff.size() )
++        {
++            finish() ;
++            return false ;
++        }
++        
++        slot = rBuff.get(nextIdx) ;
++        if ( maxRec != null && Record.keyGE(slot, maxRec) )
++        {
++            // Finished - now to large
++            finish() ;
++            return false ;
++        }
++        nextIdx ++ ;
++        return true ;
++    }
++
++    @Override
++    public Record next()
++    {
++        if ( ! hasNext() )
++            throw new NoSuchElementException("RecordBufferIterator") ;
++        Record r = slot ;
++        slot = null ;
++        return r ;
++    }
++
++    @Override
++    public void remove()
++    { throw new UnsupportedOperationException("RecordBufferIterator.remove") ; }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/AlreadyLocked.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/AlreadyLocked.java
index 0000000,0000000..dfe4125
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/AlreadyLocked.java
@@@ -1,0 -1,0 +1,29 @@@
++/*
++ * 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.seaborne.dboe.base.file;
++
++import org.seaborne.dboe.DBOpEnvException;
++
++/** Exception throws when an attempt to take a {@link ProcessFileLock}
++ *  with a call of {@link ProcessFileLock#lockEx}
++ *  is made and the lock is already held.
++ */
++public class AlreadyLocked extends DBOpEnvException {
++    public AlreadyLocked(String msg) { super(msg); }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BinaryDataFile.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BinaryDataFile.java
index 0000000,0000000..3d9772c
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BinaryDataFile.java
@@@ -1,0 -1,0 +1,99 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++import java.io.RandomAccessFile ;
++
++import org.apache.jena.atlas.lib.Closeable ;
++import org.apache.jena.atlas.lib.Sync ;
++
++/** An append-only, read-anywhere, binary file.
++ * A {@code BinaryDataFile} does not record the length and assumes the
++ * entries are self-defining.
++ * 
++ *  @see RandomAccessFile
++ */
++public interface BinaryDataFile extends Closeable, Sync {
++    // What about java.nio.channels.FileChannel?
++    // On OpenJDK,  RandomAccessFile and FileChannelImpl both dive into native code.
++    //  
++    // The choice seems to come down to ByteBuffers vs byte[]
++    // which in turn is a small/large data (scattered data)
++    // issue.  We are currently expecting small(er) I/O so byte[]
++    // and being like Thrift is better.
++
++    // byte[] vs ByteBuffer
++    
++    /** Open the file */
++    public void open() ;
++    
++    /** Is it open? */ 
++    public boolean isOpen() ;
++    
++    /** Read into a byte array, returning the number of bytes read. 
++     * Reads are at an absolute position and a read is atomic/thread-safe.
++     * 
++     * @param posn Location of the read operation.
++     * @param b byte array
++     * 
++     * @return The number of bytes read
++     */
++    public default int read(long posn, byte b[]) {
++        return read(posn, b, 0, b.length);
++    }
++
++    /** Read into a byte array, returning the number of bytes read.
++     * Reads are at an absolute position and a read is atomic/thread-safe.
++     * 
++     * @param posn Location of the read operation.
++     * @param b
++     * @param start of bytesarray to read into
++     * @param length Maximum number of bytes to read. 
++     * @return The number of bytes read
++     */
++ 
++    public int read(long posn, byte b[], int start, int length) ;
++
++    /** Write bytes - bytes are always written to the end of the file.
++     * Return the location where the write started.
++     */ 
++    public default long write(byte b[]) {
++        return write(b, 0, b.length) ;
++    }
++    
++    /** Write bytes - bytes are always written to the end of the file.
++     * Return the location where the write started.
++     */ 
++    public long write(byte b[], int start, int length) ;
++    
++    /** Return the length of the file (including any buffered writes) */
++    public long length() ;
++
++    /** Truncate the file */ 
++    public void truncate(long length) ; 
++
++    /** Return whether this is an empty file or not */ 
++    public default boolean isEmpty() { return length() == 0 ; } 
++
++    @Override
++    public void sync() ;
++    
++    @Override
++    public void close() ;
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BinaryDataFileMem.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BinaryDataFileMem.java
index 0000000,0000000..17513a8
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BinaryDataFileMem.java
@@@ -1,0 -1,0 +1,114 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++import org.apache.jena.atlas.RuntimeIOException ;
++import org.apache.jena.atlas.io.IO ;
++
++/** Implementation of {@link BinaryDataFile} in memory for testing
++ * and development use. Raw performance is not an objective.
++ * 
++ * <li>This implementation is thread-safe. 
++ */
++public class BinaryDataFileMem implements BinaryDataFile {
++
++    private boolean readMode ;
++    private SegmentedMemBuffer storage ;
++    
++    public BinaryDataFileMem() { 
++    }
++    
++    @Override
++    synchronized
++    public void open() {
++        if ( storage != null )
++            throw new RuntimeIOException("Already open") ;
++        storage = new SegmentedMemBuffer() ;
++        readMode = true ;
++    }
++
++    @Override
++    synchronized
++    public boolean isOpen() {
++        return storage != null ;
++    }
++    
++    @Override
++    synchronized
++    public int read(long posn, byte[] b, int start, int length) {
++        checkOpen() ;
++        switchToReadMode() ;
++        return storage.read(posn, b, start, length) ;
++    }
++
++    @Override
++    synchronized
++    public long write(byte[] b, int start, int length) {
++        checkOpen() ;
++        switchToWriteMode() ;
++        long x = storage.length() ;
++        storage.write(x, b, start, length) ;
++        return x ; 
++    }
++
++    @Override
++    synchronized
++    public void truncate(long length) {
++        if ( length < 0 )
++            IO.exception(String.format("truncate: bad length : %d", length)) ;
++        checkOpen() ;
++        switchToWriteMode() ;
++        storage.truncate(length); 
++    }
++
++    @Override
++    synchronized
++    public void sync() {
++        checkOpen() ;
++        storage.sync();
++    }
++
++    @Override
++    synchronized
++    public void close() {
++        if ( ! isOpen() )
++            return ;
++        storage.close();
++        storage = null ;
++    }
++
++    @Override
++    synchronized
++    public long length() {
++        return storage.length() ;
++    }
++    
++    private void switchToReadMode() {
++        readMode = true ;
++    }
++
++    private void switchToWriteMode() {
++        readMode = false ;
++    }
++
++    private void checkOpen() {
++        if ( ! isOpen() ) 
++            throw new RuntimeIOException("Not open") ;
++    }
++}
++


[14/50] [abbrv] jena git commit: IteratorCheckNotConcurrent

Posted by an...@apache.org.
IteratorCheckNotConcurrent


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

Branch: refs/heads/jena-tdb2
Commit: ef68a8d694269931935b32efc058060286e689b3
Parents: 982c0c7
Author: Andy Seaborne <an...@seaborne.org>
Authored: Mon Sep 4 10:41:23 2017 +0100
Committer: Andy Seaborne <an...@seaborne.org>
Committed: Mon Sep 4 10:41:23 2017 +0100

----------------------------------------------------------------------
 .../tdb2/store/IteratorCheckNotConcurrent.java  | 93 ++++++++++++++++++++
 .../org/seaborne/tdb2/store/TripleTable.java    |  3 +-
 2 files changed, 94 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/ef68a8d6/tdb2/src/main/java/org/seaborne/tdb2/store/IteratorCheckNotConcurrent.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/IteratorCheckNotConcurrent.java b/tdb2/src/main/java/org/seaborne/tdb2/store/IteratorCheckNotConcurrent.java
new file mode 100644
index 0000000..0aa4182
--- /dev/null
+++ b/tdb2/src/main/java/org/seaborne/tdb2/store/IteratorCheckNotConcurrent.java
@@ -0,0 +1,93 @@
+/*
+ * 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.seaborne.tdb2.store;
+
+import static java.lang.String.format ;
+
+import java.util.ConcurrentModificationException ;
+import java.util.Iterator ;
+import java.util.NoSuchElementException ;
+import java.util.concurrent.atomic.AtomicLong ;
+
+import org.apache.jena.atlas.iterator.Iter ;
+import org.apache.jena.atlas.lib.Closeable ;
+
+/** Wrapper to check an iterator has not been used across some write operation. */ 
+class IteratorCheckNotConcurrent<T> implements Iterator<T>, Closeable
+{
+    private Iterator<T> iter ;
+    private AtomicLong eCount ;  // Needs fixing to be per thera in some way - either from the caller or here. 
+    private boolean finished = false ;
+    private long startEpoch ; 
+
+    IteratorCheckNotConcurrent(Iterator<T> iter, AtomicLong eCount) {
+        // Assumes correct locking to set up, i.e. eCount not changing
+        // (writer on separate thread).
+        this.iter = iter ;
+        this.eCount = eCount ;
+        this.startEpoch = eCount.get() ;
+    }
+
+    private void checkConcurrentModification() {
+        if ( finished )
+            return ;
+
+        long now = eCount.get() ;
+        if ( now != startEpoch ) {
+            policyError(format("Iterator: started at %d, now %d", startEpoch, now)) ;
+        }
+    }
+    
+    private static void policyError(String message) {
+        throw new ConcurrentModificationException(message) ;
+    } 
+
+    @Override
+    public boolean hasNext() {
+        checkConcurrentModification() ;
+        boolean b = iter.hasNext() ;
+        if ( !b )
+            close() ;
+        return b ;
+    }
+
+    @Override
+    public T next() {
+        checkConcurrentModification() ;
+        try { return iter.next() ; }
+        catch (NoSuchElementException ex) {
+            close() ;
+            throw ex ;
+        }
+    }
+
+    @Override
+    public void remove() {
+        checkConcurrentModification() ;
+        iter.remove() ;
+        // Reset the epoch.
+        startEpoch = eCount.get() ;
+    }
+
+    @Override
+    public void close() {
+        finished = true ;
+        Iter.close(iter) ;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jena/blob/ef68a8d6/tdb2/src/main/java/org/seaborne/tdb2/store/TripleTable.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/TripleTable.java b/tdb2/src/main/java/org/seaborne/tdb2/store/TripleTable.java
index a85494b..54dcac3 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/store/TripleTable.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/store/TripleTable.java
@@ -30,8 +30,7 @@ import org.seaborne.tdb2.store.tupletable.TupleIndex ;
 /**
  * TripleTable - a collection of TupleIndexes for 3-tuples together with a node
  * table. Normally, based on 3 indexes (SPO, POS, OSP) but other indexing
- * structures can be configured. The node table form can map to and from NodeIds
- * (longs)
+ * structures can be configured.
  */
 
 public class TripleTable extends TableBase {


[07/50] [abbrv] jena git commit: Note about gpg and SHA512

Posted by an...@apache.org.
Note about gpg and SHA512


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

Branch: refs/heads/jena-tdb2
Commit: 0d5a46884e21f807f2433947da5890191d07bbd8
Parents: 218d10c
Author: Andy Seaborne <an...@seaborne.org>
Authored: Sat Sep 2 18:40:46 2017 +0100
Committer: Andy Seaborne <an...@seaborne.org>
Committed: Sat Sep 2 18:40:46 2017 +0100

----------------------------------------------------------------------
 pom.xml | 8 ++++++++
 1 file changed, 8 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/0d5a4688/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 77a2033..af22a7d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -350,6 +350,14 @@
             <groupId>org.apache.maven.plugins</groupId>
             <artifactId>maven-gpg-plugin</artifactId>
             <version>1.6</version>
+            <!-- Does this interfer with external -Darguments=-Dgpg.keyname=
+                  i.e. is it additive?
+                  <configuration>
+              <gpgArguments>
+                <arg>- -digest-algo=SHA512</arg> <comment fix
+              </gpgArguments>
+            </configuration>
+            -->
             <executions>
               <execution>
                 <id>sign-artifacts</id>


[02/50] [abbrv] jena git commit: Switchable conatiner datasets

Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseOps.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseOps.java b/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseOps.java
new file mode 100644
index 0000000..8a375b9
--- /dev/null
+++ b/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseOps.java
@@ -0,0 +1,261 @@
+/*
+ * 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.seaborne.tdb2.sys;
+
+import java.io.BufferedOutputStream ;
+import java.io.IOException ;
+import java.io.OutputStream ;
+import java.nio.file.* ;
+import java.util.List;
+import java.util.zip.GZIPOutputStream ;
+
+import org.apache.jena.atlas.RuntimeIOException ;
+import org.apache.jena.atlas.lib.DateTimeUtils ;
+import org.apache.jena.atlas.logging.Log;
+import org.apache.jena.riot.Lang ;
+import org.apache.jena.riot.RDFDataMgr ;
+import org.apache.jena.sparql.core.DatasetGraph;
+import org.seaborne.dboe.base.file.Location;
+import org.seaborne.dboe.jenax.Txn ;
+import org.seaborne.dboe.transaction.txn.TransactionCoordinator;
+import org.seaborne.dboe.transaction.txn.TransactionalSystem;
+import org.seaborne.tdb2.TDBException;
+import org.seaborne.tdb2.repack.DatasetGraphSwitchable ;
+import org.seaborne.tdb2.repack.sys.CopyDSG;
+import org.seaborne.tdb2.repack.sys.FilenameUtils;
+import org.seaborne.tdb2.repack.sys.Util;
+import org.seaborne.tdb2.setup.StoreParams;
+import org.seaborne.tdb2.store.DatasetGraphTDB;
+
+/** Operations on and about TDB2 databases. 
+ * <p>
+ * TDB2 uses a hierarchical structure to manage on disk.
+ * <p>
+ * If in-memory (non-scalable, not-performant, perfect simulation for functionality using a RAM disk, for testing mainly),
+ * then the switchable layer is just a covenience. DatasetGrapOps such as {@link #compact}
+ * and {@link #backup} do not apply.
+ * <p>
+ * Directory and files on disk:
+ * <ul>
+ * <li> {@code Data-NNNN/} -- databases by version. Compacting creates a new directory, leaving 
+ * <li> store params, static (disk layout related - can not chnage once created) and dynamic (settings related to in-memory).
+ * <li> {@code Backups/} -- backups.
+ * <li> External indexes like {@code TextIndex/} -- the text index only applies to the current, latest database.
+ * </ul>
+ */
+public class DatabaseOps {
+    private static final String dbPrefix     = "Data";
+    private static final String SEP          = "-";
+    private static final String startCount   = "0001";
+
+    private static final String BACKUPS_DIR  = "Backups";
+    // Basename of the backup file. "backup_{DateTime}.nq.gz
+    private static final String BACKUPS_FN   = "backup";
+    
+    /*package*/ static DatasetGraph create(Location location) {
+        // Hide implementation class.
+        return createSwitchable(location);
+    }
+    
+    /** Create a fresh database - called by {@code DatabaseMgr}.
+     * It is important to go via {@code DatabaseMgr} to avoid
+     * duplicate {@code DatasetGraphSwitchable}s for the same location.
+     */
+    private static DatasetGraphSwitchable createSwitchable(Location location) {
+        if ( location.isMem() ) {
+            DatasetGraph dsg = StoreConnection.connectCreate(location).getDatasetGraph();
+            return new DatasetGraphSwitchable(null, dsg);
+        }
+        // Exists?
+       if ( ! location.exists() )
+           throw new TDBException("No such location: "+location);
+       Path path = IOX.asPath(location);
+       // Scan for DBs
+       Path db = findLocation(path, dbPrefix);
+       // XXX [StoreParams]
+       StoreParams params;
+       if ( db == null ) {
+           db = path.resolve(dbPrefix+SEP+startCount);
+           IOX.createDirectory(db);
+       }
+       Location loc2 = IOX.asLocation(db);
+       DatasetGraphTDB dsg = StoreConnection.connectCreate(loc2).getDatasetGraphTDB();
+       DatasetGraphSwitchable appDSG = new DatasetGraphSwitchable(path, dsg);
+       return appDSG;
+    }
+    
+    public static void backup(DatasetGraphSwitchable container) {
+        checkSupportsAdmin(container);
+        Path dbPath = container.getContainerPath();
+        Path backupDir = dbPath.resolve(BACKUPS_DIR);
+        if ( ! Files.exists(backupDir) )
+            IOX.createDirectory(backupDir);
+      
+        DatasetGraph dsg = container;
+        
+//  // Per backup source lock. 
+//  synchronized(activeBackups) {
+//      // Atomically check-and-set
+//      if ( activeBackups.contains(dsg) )
+//          Log.warn(Fuseki.serverLog, "Backup already in progress") ;
+//      activeBackups.add(dsg) ;
+//  }
+
+        try (OutputStream out2 = openUniqueFileForWriting(backupDir, BACKUPS_FN, "nq.gz");
+             OutputStream out1 = new GZIPOutputStream(out2, 8 * 1024) ;
+             OutputStream out = new BufferedOutputStream(out1)) {
+            Txn.executeRead(dsg, ()->RDFDataMgr.write(out, dsg, Lang.NQUADS));
+        } catch (IOException e) {
+            throw IOX.exception(e) ;
+        } finally {
+        }
+    }
+    
+    private static void checkSupportsAdmin(DatasetGraphSwitchable container) {
+        if ( ! container.hasContainerPath() )
+            throw new TDBException("Dataset does not support admin operations");
+    }
+
+    //private static void logWarn(String msg) {}
+    
+    
+    // --> IOX
+    private static OutputStream openUniqueFileForWriting(Path dirPath, String basename, String ext) {
+        if ( ! Files.isDirectory(dirPath) )
+            throw new IllegalArgumentException("Not a directory: "+dirPath);
+        if ( basename.contains("/") || basename.contains("\\") )
+            throw new IllegalArgumentException("Basename must not contain a file path separator (\"/\" or \"\\\")");
+        
+        String timestamp = DateTimeUtils.nowAsString("yyyy-MM-dd_HH:mm:ss") ;
+        String filename = basename + "_" + timestamp ;
+        Path p = dirPath.resolve(filename+"."+ext);
+        int x = 0 ;
+        for(;;) {
+            try {
+                return Files.newOutputStream(p, StandardOpenOption.CREATE_NEW);
+            } catch (AccessDeniedException ex)  { 
+                throw IOX.exception("Access denied", ex);
+            } catch (FileAlreadyExistsException ex) {
+                // Drop through and try again.
+            } catch (IOException ex) {
+                throw IOX.exception(ex) ;
+            }
+            // Try again.
+            x++;
+            if ( x >= 5 )
+                throw new RuntimeIOException("Can't create the unique name: number of attempts exceeded");
+            p = dirPath.resolve(filename+"_"+x+"."+ext);
+        }
+    }
+
+    
+    // JVM-wide :-(
+    private static Object compactionLock = new Object();
+    
+    public static void compact(DatasetGraphSwitchable container) {
+        checkSupportsAdmin(container);
+        synchronized(compactionLock) {
+            Path base = container.getContainerPath();
+            Path db1 = findLocation(base, dbPrefix);
+            Location loc1 = IOX.asLocation(db1);
+
+            // -- Checks
+            Location loc1a = ((DatasetGraphTDB)container.get()).getLocation();
+            if ( loc1a.isMem() ) {}
+            if ( ! loc1a.exists() )
+                throw new TDBException("No such location: "+loc1a);
+            
+            // Is this the same database location?
+            if ( ! loc1.equals(loc1a) )
+                throw new TDBException("Inconsistent (not latested?) : "+loc1a+" : "+loc1);
+            // -- Checks
+            
+            // Version
+            int v = IOX.extractIndex(db1.getFileName().toString(), dbPrefix, SEP);
+            String next = FilenameUtils.filename(dbPrefix, SEP, v+1);
+            
+            Path db2 = db1.getParent().resolve(next);
+            IOX.createDirectory(db2);
+            Location loc2 = IOX.asLocation(db2);
+            System.out.printf("Compact %s -> %s\n", db1.getFileName(), db2.getFileName());
+            
+            compact(container, loc1, loc2);
+        }
+    }
+    
+    // XXX Later - switch in a recording dataset, not block writers, and reply after
+    // switch over before releasing the new dataset to the container.
+    // Maybe copy indexes and switch the DSG over (drop switchable).
+    
+    /** Copy the latest version from one location to another. */
+    private static void compact(DatasetGraphSwitchable container, Location loc1, Location loc2) {
+        if ( loc1.isMem() || loc2.isMem() )
+            throw new TDBException("Compact involves a memory location: "+loc1+" : "+loc2);
+        StoreConnection srcConn = StoreConnection.connectExisting(loc1);
+        if ( srcConn == null ) {
+            throw new TDBException("No database at location : "+loc1); 
+        }
+        
+        DatasetGraphTDB dsgCurrent = (DatasetGraphTDB)container.get();
+        if ( ! dsgCurrent.getLocation().equals(loc1) )
+            throw new TDBException("Inconsistent locations for base : "+dsgCurrent.getLocation()+" , "+dsgCurrent.getLocation());
+        
+        DatasetGraphTDB dsgBase = srcConn.getDatasetGraphTDB();
+        if ( dsgBase != dsgCurrent )
+            throw new TDBException("Inconsistent datasets : "+dsgCurrent.getLocation()+" , "+dsgBase.getLocation());
+        
+        TransactionalSystem txnSystem = dsgBase.getTxnSystem();
+        TransactionCoordinator txnMgr = dsgBase.getTxnSystem().getTxnMgr();
+        
+        // Stop update.  On exit there are no writers and none will start until switched over.
+        txnMgr.tryBlockWriters();
+        // txnMgr.begin(WRITE, false) will now bounce.
+        
+        // Copy the latest generation.
+        DatasetGraphTDB dsgCompact = StoreConnection.connectCreate(loc2).getDatasetGraphTDB();
+        CopyDSG.copy(dsgBase, dsgCompact);   
+
+        TransactionCoordinator txnMgr2 = dsgCompact.getTxnSystem().getTxnMgr();
+        txnMgr2.startExclusiveMode();
+
+        txnMgr.startExclusiveMode();
+
+        // No transactions on either database.
+        // Switch.
+        if ( ! container.change(dsgCurrent, dsgCompact) ) {
+            Log.warn(DatabaseOps.class, "Inconistent: old datasetgraph not as expected");
+            container.set(dsgCompact);
+        }
+        txnMgr2.finishExclusiveMode();
+        // New database running.
+
+        // Clean-up.
+        // txnMgr.finishExclusiveMode();
+        // Don't call : txnMgr.startWriters();
+        StoreConnection.release(dsgBase.getLocation());
+    }
+    
+    private static Path findLocation(Path directory, String namebase) {
+        if ( ! Files.exists(directory) )
+            return null;
+        // In-order, low to high.
+        List<Path> maybe = IOX.scanForDirByPattern(directory, namebase, SEP);
+        return Util.getLastOrNull(maybe);
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/main/java/org/seaborne/tdb2/sys/DatasetControl.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/sys/DatasetControl.java b/tdb2/src/main/java/org/seaborne/tdb2/sys/DatasetControl.java
deleted file mode 100644
index e389e52..0000000
--- a/tdb2/src/main/java/org/seaborne/tdb2/sys/DatasetControl.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- *  Licensed 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.
- *
- *  See the NOTICE file distributed with this work for additional
- *  information regarding copyright ownership.
- */
-
-package org.seaborne.tdb2.sys;
-
-import java.util.Iterator ;
-
-// Used in NodeTupleTables.
-
-/** A DatasetControl is an encapsulation of what to do on reads and writes.
- *  In addition, iterators returned can be tied back to the original request
- *  to check they are still valid.  
- */
-public interface DatasetControl
-{
-    /** Fine grained, internal update - start */  
-    public void startUpdate() ;
-    
-    /** Fine grained, internal update - finish */
-    public void finishUpdate();
-
-    /** Signal the start of an internal read operation */
-    public void startRead();
-    
-    /** Signal the completion of an internal read operation */
-    public void finishRead();
-    
-    /** Notify an iterator being created. */
-    public <T> Iterator<T> iteratorControl(Iterator<T> iter) ;
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/main/java/org/seaborne/tdb2/sys/DatasetControlMRSW.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/sys/DatasetControlMRSW.java b/tdb2/src/main/java/org/seaborne/tdb2/sys/DatasetControlMRSW.java
deleted file mode 100644
index 518bb6e..0000000
--- a/tdb2/src/main/java/org/seaborne/tdb2/sys/DatasetControlMRSW.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- *  Licensed 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.
- *
- *  See the NOTICE file distributed with this work for additional
- *  information regarding copyright ownership.
- */
-
-package org.seaborne.tdb2.sys;
-
-import static java.lang.String.format ;
-
-import java.util.ConcurrentModificationException ;
-import java.util.Iterator ;
-import java.util.NoSuchElementException ;
-import java.util.concurrent.atomic.AtomicLong ;
-
-import org.apache.jena.atlas.iterator.Iter ;
-import org.apache.jena.atlas.lib.Closeable ;
-
-/** A policy that checks, but does not enforce, single writer or multiple writer locking policy */ 
-public class DatasetControlMRSW implements DatasetControl
-{
-    private final boolean concurrencyChecking = true ;
-    private final AtomicLong epoch = new AtomicLong(5) ;                // Update counters, used to check iterators. No need to start at 0.
-    private final AtomicLong readCounter = new AtomicLong(0) ;
-    private final AtomicLong writeCounter = new AtomicLong(0) ;
-    
-    public DatasetControlMRSW()
-    { }
-
-    @Override
-    public void startRead() {
-        readCounter.getAndIncrement() ;
-        checkConcurrency() ;
-    }
-
-    @Override
-    public void finishRead() {
-        readCounter.decrementAndGet() ;
-    }
-
-    @Override
-    public void startUpdate() {
-        epoch.getAndIncrement() ;
-        writeCounter.getAndIncrement() ;
-        checkConcurrency() ;
-    }
-
-    @Override
-    public void finishUpdate() {
-        writeCounter.decrementAndGet() ;
-    }
-
-    private void checkConcurrency() {
-        long R, W ;
-        synchronized (this) {
-            R = readCounter.get() ;
-            W = writeCounter.get() ;
-        }
-
-        if ( R > 0 && W > 0 )
-            policyError(R, W) ;
-        if ( W > 1 )
-            policyError(R, W) ;
-    }
-    
-    @Override
-    public <T> Iterator<T> iteratorControl(Iterator<T> iter) { return new IteratorCheckNotConcurrent<>(iter, epoch) ; }
-    
-    private static class IteratorCheckNotConcurrent<T> implements Iterator<T>, Closeable
-    {
-        private Iterator<T> iter ;
-        private AtomicLong eCount ;
-        private boolean finished = false ;
-        private long startEpoch ; 
-
-        IteratorCheckNotConcurrent(Iterator<T> iter, AtomicLong eCount) {
-            // Assumes correct locking to set up, i.e. eCount not changing
-            // (writer on separate thread).
-            this.iter = iter ;
-            this.eCount = eCount ;
-            this.startEpoch = eCount.get() ;
-        }
-
-        private void checkCourrentModification() {
-            if ( finished )
-                return ;
-
-            long now = eCount.get() ;
-            if ( now != startEpoch ) {
-                policyError(format("Iterator: started at %d, now %d", startEpoch, now)) ;
-
-            }
-        }
-
-        @Override
-        public boolean hasNext() {
-            checkCourrentModification() ;
-            boolean b = iter.hasNext() ;
-            if ( !b )
-                close() ;
-            return b ;
-        }
-
-        @Override
-        public T next() {
-            checkCourrentModification() ;
-            try { return iter.next() ; }
-            catch (NoSuchElementException ex) {
-                close() ;
-                throw ex ;
-            }
-        }
-
-        @Override
-        public void remove() {
-            checkCourrentModification() ;
-            iter.remove() ;
-        }
-
-        @Override
-        public void close() {
-            finished = true ;
-            Iter.close(iter) ;
-        }
-    }
-    
-    private static void policyError(long R, long W) {
-        policyError(format("Reader = %d, Writer = %d", R, W)) ;
-    }
-
-    private static void policyError(String message) {
-        throw new ConcurrentModificationException(message) ;
-    }    
-
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/main/java/org/seaborne/tdb2/sys/DatasetControlMutable.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/sys/DatasetControlMutable.java b/tdb2/src/main/java/org/seaborne/tdb2/sys/DatasetControlMutable.java
deleted file mode 100644
index cfce6e7..0000000
--- a/tdb2/src/main/java/org/seaborne/tdb2/sys/DatasetControlMutable.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- *  Licensed 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.
- *
- *  See the NOTICE file distributed with this work for additional
- *  information regarding copyright ownership.
- */
-
-package org.seaborne.tdb2.sys ;
-
-import java.util.Iterator ;
-
-/** A DatasetControl tat is an indirection to another and can be changed */  
-public class DatasetControlMutable implements DatasetControl {
-    private DatasetControl other ;
-    
-    public DatasetControlMutable(DatasetControl control) { set(control) ; } 
-    
-    public void set(DatasetControl newOther) { other = newOther ; }
-    
-    @Override
-    public void startUpdate() {
-        other.startUpdate() ;
-    }
-
-    @Override
-    public void finishUpdate() {
-        other.finishUpdate() ;
-    }
-
-    @Override
-    public void startRead() {
-        other.startRead() ;
-    }
-
-    @Override
-    public void finishRead() {
-        other.finishUpdate() ;
-    }
-
-    @Override
-    public <T> Iterator<T> iteratorControl(Iterator<T> iter) {
-        return other.iteratorControl(iter) ;
-    }
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/main/java/org/seaborne/tdb2/sys/DatasetControlNone.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/sys/DatasetControlNone.java b/tdb2/src/main/java/org/seaborne/tdb2/sys/DatasetControlNone.java
deleted file mode 100644
index 8206574..0000000
--- a/tdb2/src/main/java/org/seaborne/tdb2/sys/DatasetControlNone.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- *  Licensed 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.
- *
- *  See the NOTICE file distributed with this work for additional
- *  information regarding copyright ownership.
- */
-
-package org.seaborne.tdb2.sys;
-
-import java.util.Iterator ;
-
-/** Concurrency policy  that is a "free for all".
- *  No checking, no locking.
- */
-public final class DatasetControlNone implements DatasetControl
-{
-    @Override
-    public void startUpdate()
-    {}
-
-    @Override
-    public void finishUpdate()
-    {}
-
-    @Override
-    public void startRead()
-    {}
-
-    @Override
-    public void finishRead()
-    {}
-
-    @Override
-    public <T> Iterator<T> iteratorControl(Iterator<T> iter)
-    {
-        return iter ;
-    }
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/main/java/org/seaborne/tdb2/sys/DatasetControlReadOnly.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/sys/DatasetControlReadOnly.java b/tdb2/src/main/java/org/seaborne/tdb2/sys/DatasetControlReadOnly.java
deleted file mode 100644
index 0bde32e..0000000
--- a/tdb2/src/main/java/org/seaborne/tdb2/sys/DatasetControlReadOnly.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- *  Licensed 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.
- *
- *  See the NOTICE file distributed with this work for additional
- *  information regarding copyright ownership.
- */
-
-package org.seaborne.tdb2.sys;
-
-import java.util.Iterator ;
-import java.util.concurrent.atomic.AtomicLong ;
-
-import org.seaborne.tdb2.TDBException ;
-
-/** A policy that provide read-only access */ 
-public class DatasetControlReadOnly implements DatasetControl
-{
-    private final AtomicLong readCounter = new AtomicLong(0) ;
-    
-    public DatasetControlReadOnly()
-    { }
-
-    @Override
-    public void startRead()
-    {
-        readCounter.getAndIncrement() ;
-    }
-
-    @Override
-    public void finishRead()
-    {
-        readCounter.decrementAndGet() ;
-    }
-
-    @Override
-    public void startUpdate()
-    {
-        throw new TDBException("Read-only") ;
-    }
-
-    @Override
-    public void finishUpdate()
-    {
-        throw new TDBException("Read-only") ;
-    }
-
-    @Override
-    public <T> Iterator<T> iteratorControl(Iterator<T> iter) { return iter ; }
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/main/java/org/seaborne/tdb2/sys/IOX.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/sys/IOX.java b/tdb2/src/main/java/org/seaborne/tdb2/sys/IOX.java
new file mode 100644
index 0000000..049fbb3
--- /dev/null
+++ b/tdb2/src/main/java/org/seaborne/tdb2/sys/IOX.java
@@ -0,0 +1,293 @@
+/*
+ * 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.seaborne.tdb2.sys;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.*;
+import java.nio.file.attribute.FileAttribute;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.function.Function ;
+import java.util.regex.Pattern;
+
+import org.apache.jena.atlas.RuntimeIOException;
+import org.apache.jena.atlas.logging.FmtLog;
+import org.seaborne.dboe.DBOpEnvException;
+import org.seaborne.dboe.base.file.Location;
+import org.seaborne.tdb2.repack.sys.Util ;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *  PathX
+ */
+public class IOX {
+    
+    public static Path currentDirectory = Paths.get(".");
+    
+    /** A Consumer that can throw {@link IOException}. */
+    public interface IOConsumer<X> {
+        void actionEx(X arg) throws IOException;
+    }
+
+    /** Convert an {@link IOException} into a {@link RuntimeIOException}.
+     * <p>
+     * Idiom:
+     * <pre>
+     *     catch(IOException ex) { throw new exception(ex); } 
+     * </pre>
+     * @param ioException
+     * @return RuntimeIOException
+     */
+    public static RuntimeIOException exception(IOException ioException) {
+        return new RuntimeIOException(ioException);
+    }
+    
+    /** Convert an {@link IOException} into a {@link RuntimeIOException}.
+     * <p>
+     * Idiom:
+     * <pre>
+     *     catch(IOException ex) { throw new exception("Oh dear", ex); } 
+     * </pre>
+     * @param message
+     * @param ioException
+     * @return RuntimeIOException
+     */
+    public static RuntimeIOException exception(String message, IOException ioException) {
+        return new RuntimeIOException(message, ioException);
+    }
+    
+    /** Write a file safely - the change happens (the function returns true) or
+     * somthing went wrong (the function throws a runtime exception) and the file is not changed.
+     * Note that the tempfile must be in the same direct as the actual file so an OS-atomic rename can be done.  
+     */
+    public static boolean safeWrite(Path file, IOConsumer<OutputStream> writerAction) {
+        Path tmp = createTempFile(file.getParent(), file.getFileName().toString(), ".tmp");
+        return safeWrite(file, tmp, writerAction);
+    }
+
+    /** Write a file safely - the change happens (the function returns true) or
+     * somthing went wrong (the function throws a runtime exception) and the file is not changed.
+     * Note that the tempfile must be in the same direct as the actual file so an OS-atomic rename can be done.  
+     */
+    public static boolean safeWrite(Path file, Path tmpFile, IOConsumer<OutputStream> writerAction) {
+        try {
+            try(OutputStream out = new BufferedOutputStream(Files.newOutputStream(tmpFile)) ) {
+                writerAction.actionEx(out);
+            }
+            move(tmpFile, file);
+            return true;
+        } catch(IOException ex) { throw IOX.exception(ex); }
+    }
+
+    /** Delete a file. */
+    public static void delete(Path path) {
+        try { Files.delete(path); }
+        catch (IOException ex) {
+            FmtLog.error(IOX.class, ex, "IOException deleting %s", path);
+            throw IOX.exception(ex);
+        }
+    }
+    
+    /** Atomically move a file. */
+    public static void move(Path src, Path dst) {
+        try { Files.move(src, dst, StandardCopyOption.ATOMIC_MOVE) ; }
+        catch (IOException ex) {
+            FmtLog.error(IOX.class, ex, "IOException moving %s to %s", src, dst);
+            throw IOX.exception(ex);
+        }
+    }
+    
+    /** Copy a file, not atomic. *
+     * Can copy to a directory or over an existing file.
+     * @param srcFilename
+     * @param dstFilename
+     */
+    public static void copy(String srcFilename, String dstFilename) {
+        Path src = Paths.get(srcFilename);
+        if ( ! Files.exists(src) )
+            throw new RuntimeIOException("No such file: "+srcFilename);
+        
+        Path dst = Paths.get(dstFilename);
+        if ( Files.isDirectory(dst) )
+            dst = dst.resolve(src.getFileName());
+        
+        try { Files.copy(src, dst); }
+        catch (IOException ex) {
+            FmtLog.error(IOX.class, ex, "IOException copying %s to %s", srcFilename, dstFilename);
+            throw IOX.exception(ex);
+        }
+    }
+    
+    /** Create a directory - thgrow a runtime exception if theer are any problems.
+     * This function wraps {@code Files.createDirectory}.
+     */
+    public static void createDirectory(Path dir) {
+        try { Files.createDirectory(dir); }
+        catch (IOException ex) { throw IOX.exception(ex); }
+    }
+
+    /** Convert a {@link Path}  to a {@link Location}. */
+    public static Location asLocation(Path path) {
+        if ( ! Files.isDirectory(path) )
+            throw new RuntimeIOException("Path is not naming a directory: "+path);
+        return Location.create(path.toString());
+    }
+    
+//    /** Convert a {@link org.apache.jena.tdb.base.file.Location} to a {@link Path}. */
+//    public static Path asPath(org.apache.jena.tdb.base.file.Location location) {
+//        if ( location.isMem() )
+//            throw new RuntimeIOException("Location is a memory location: "+location);
+//        return Paths.get(location.getDirectoryPath());
+//    }
+
+    /** Convert a {@link Location} to a {@link Path}. */
+    public static Path asPath(org.seaborne.dboe.base.file.Location location) {
+        if ( location.isMem() )
+            throw new RuntimeIOException("Location is a memory location: "+location);
+        return Paths.get(location.getDirectoryPath());
+    }
+
+    /** Convert a {@link Location} to a {@link File}. */
+    public static File asFile(Location loc) {
+        return new File(loc.getDirectoryPath());
+    }
+
+    /** Read the whole of a file */
+    public static byte[] readAll(Path pathname) {
+        try {
+            return Files.readAllBytes(pathname);
+        } catch (IOException ex) { throw IOX.exception(ex); }
+    }
+    
+    /** Write the whole of a file */
+    public static void writeAll(Path pathname, byte[] value) {
+        try {
+            Files.write(pathname, value, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE);
+        } catch (IOException ex) { throw IOX.exception(ex); }
+    }
+
+//    /**
+//     * Ensure a directory exists, creating the directory and any missing
+//     * directories on the path to it.
+//     */
+//    public static void ensureDirectory(Path dir, FileAttribute<? >... attrs) {
+//        if ( Files.exists(dir) ) {
+//            if ( ! Files.isDirectory(dir) )
+//                throw new DeltaFileException("Exists but not a directory: "+dir);
+//        }
+//        try { Files.createDirectories(dir, attrs); }
+//        catch (IOException ex) { new DeltaFileException("Failed to create directory: "+dir, ex);}
+//    }
+//    
+//    /**
+//     * Ensure a file exists - create an empty one if not. This operation does
+//     * not create a directory path to the file.
+//     */
+//    public static void ensureFile(Path filePath, FileAttribute<? >... attrs) {
+//        if ( Files.exists(filePath) ) {
+//            if ( ! Files.isRegularFile(filePath) )
+//                throw new DeltaFileException("Exists but not a regular file: "+filePath);
+//        }
+//        try { Files.createFile(filePath); }
+//        catch (IOException ex) { new DeltaFileException("Failed to create file: "+filePath, ex);}
+//    }
+
+    /**
+     * Return a temporary filename path.
+     * <p>
+     * This operation is thread-safe.
+     */
+    public static Path createTempFile(Path dir, String prefix, String suffix, FileAttribute<? >... attrs) {
+        try {
+            return Files.createTempFile(dir, prefix, suffix, attrs);
+        } catch (IOException ex) { throw IOX.exception(ex); }
+    }
+    
+    /** Generate a unique place related to path; 
+     * Optionally, provide a mapping of old name to new nae base.
+     * This method always adds "-1", "-2" etc. 
+     */  
+    public static Path uniqueDerivedPath(Path path, Function<String, String> basenameMapping) {
+        String base = path.getFileName().toString();
+        if ( basenameMapping != null )
+            base = basenameMapping.apply(base);
+        // Some large limit "just in case"
+        for(int x = 0 ; x < 10_000 ; x++ ) {
+            String destname = base+"-"+x; 
+            Path destpath = path.resolveSibling(destname);
+            if ( ! Files.exists(destpath) )
+                return destpath;
+        }
+        return null ;
+    }
+    
+    private static Logger LOG = LoggerFactory.getLogger(Util.class);
+    
+    /** Find the files in this directory that have namebase as a prefix and 
+     *  are then numbered.
+     *  <p>
+     *  Returns a sorted list from, low to high index.
+     */
+    public static List<Path> scanForDirByPattern(Path directory, String namebase, String nameSep) {
+        Pattern pattern = Pattern.compile(Pattern.quote(namebase)+
+                                          Pattern.quote(nameSep)+
+                                          "[\\d]+");
+        List<Path> paths = new ArrayList<>();
+        try (DirectoryStream<Path> stream = Files.newDirectoryStream(directory, namebase + "*")) {
+            for ( Path entry : stream ) {
+                if ( !pattern.matcher(entry.getFileName().toString()).matches() ) {
+                    throw new DBOpEnvException("Invalid filename for matching: "+entry.getFileName());
+                    // Alternative: Skip bad trailing parts but more likely there is a naming problem.  
+                    //   LOG.warn("Invalid filename for matching: {} skipped", entry.getFileName());
+                    //   continue;
+                }
+                // Follows symbolic links.
+                if ( !Files.isDirectory(entry) )
+                    throw new DBOpEnvException("Not a directory: "+entry);
+                paths.add(entry);
+            }
+        }
+        catch (IOException ex) {
+            FmtLog.warn(LOG, "Can't inspect directory: (%s, %s)", directory, namebase);
+            throw new DBOpEnvException(ex);
+        }
+        Comparator<Path> comp = (f1, f2) -> {
+            int num1 = extractIndex(f1.getFileName().toString(), namebase, nameSep);
+            int num2 = extractIndex(f2.getFileName().toString(), namebase, nameSep);
+            return Integer.compare(num1, num2);
+        };
+        paths.sort(comp);
+        //indexes.sort(Long::compareTo);
+        return paths;
+    }
+
+    /** Given a filename in "base-NNNN" format, return the value of NNNN */
+    public static int extractIndex(String name, String namebase, String nameSep) {
+        int i = namebase.length()+nameSep.length();
+        String numStr = name.substring(i);
+        int num = Integer.parseInt(numStr);
+        return num;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/test/java/org/seaborne/tdb2/TestTDBFactory.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/TestTDBFactory.java b/tdb2/src/test/java/org/seaborne/tdb2/TestTDBFactory.java
index 8805bbc..fd6ba1c 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/TestTDBFactory.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/TestTDBFactory.java
@@ -37,35 +37,35 @@ public class TestTDBFactory extends BaseTest
     static Quad quad1 = SSE.parseQuad("(_ <s> <p> 1)") ;
     static Quad quad2 = SSE.parseQuad("(_ <s> <p> 1)") ;
     
-    @Before public void before()
-    {
-        FileOps.clearDirectory(DIRx) ; 
+    @Before
+    public void before() {
+        FileOps.clearDirectory(DIRx) ;
     }
-    
-    @After public void after()
-    {
-        FileOps.clearDirectory(DIRx) ; 
+
+    @After
+    public void after() {
+        FileOps.clearDirectory(DIRx) ;
     }
-    
-    @Test public void testTDBFactory1()
-    {
+
+    @Test
+    public void testTDBFactory1() {
         StoreConnection.reset() ;
-        DatasetGraph dg1 = TDB2Factory.connectDatasetGraph(Location.mem("FOO")) ;
-        DatasetGraph dg2 = TDB2Factory.connectDatasetGraph(Location.mem("FOO")) ;
+        DatasetGraph dg1 = DatabaseMgr.connectDatasetGraph(Location.mem("FOO")) ;
+        DatasetGraph dg2 = DatabaseMgr.connectDatasetGraph(Location.mem("FOO")) ;
         Txn.executeWrite(dg1, ()->{
-            dg1.add(quad1) ;    
+            dg1.add(quad1) ;
         }) ;
         Txn.executeRead(dg2, ()->{
             assertTrue(dg2.contains(quad1)) ;
         }) ;
     }
     
-    @Test public void testTDBFactory2()
-    {
+    @Test
+    public void testTDBFactory2() {
         StoreConnection.reset() ;
         // The unnamed location is unique each time.
-        DatasetGraph dg1 = TDB2Factory.connectDatasetGraph(Location.mem()) ;
-        DatasetGraph dg2 = TDB2Factory.connectDatasetGraph(Location.mem()) ;
+        DatasetGraph dg1 = DatabaseMgr.connectDatasetGraph(Location.mem()) ;
+        DatasetGraph dg2 = DatabaseMgr.connectDatasetGraph(Location.mem()) ;
         Txn.executeWrite(dg1, ()->{
             dg1.add(quad1) ;    
         }) ;
@@ -74,16 +74,17 @@ public class TestTDBFactory extends BaseTest
         }) ;
     }
 
-    @Test public void testStoreConnectionTxn1()
-    {
+
+    @Test
+    public void testStoreConnectionTxn1() {
         StoreConnection.reset() ;
         DatasetGraph dg1 = StoreConnection.connectCreate(DIR).getDatasetGraph() ;
         DatasetGraph dg2 = StoreConnection.connectCreate(DIR).getDatasetGraph() ;
         assertSame(dg1, dg2) ;
     }
     
-    @Test public void testStoreConnectionTxn2()
-    {
+    @Test
+    public void testStoreConnectionTxn2() {
         // Named memory locations
         StoreConnection.reset() ;
         DatasetGraph dg1 = StoreConnection.connectCreate(Location.mem("FOO")).getDatasetGraph() ;
@@ -92,8 +93,8 @@ public class TestTDBFactory extends BaseTest
         assertSame(dg1, dg2) ;
     }
     
-    @Test public void testStoreConnectionTxn3()
-    {
+    @Test
+    public void testStoreConnectionTxn3() {
         // Un-named memory locations
         StoreConnection.reset() ;
         DatasetGraph dg1 = StoreConnection.connectCreate(Location.mem()).getDatasetGraph() ;
@@ -102,4 +103,19 @@ public class TestTDBFactory extends BaseTest
         assertNotSame(dg1, dg2) ;
     }
 
+    @Test
+    public void testTDBFactoryDisk() {
+        StoreConnection.reset() ;
+        // The named disk location
+        Location loc = DIR;
+        DatasetGraph dg1 = DatabaseMgr.connectDatasetGraph(loc) ;
+        DatasetGraph dg2 = DatabaseMgr.connectDatasetGraph(Location.create(loc.getDirectoryPath()));
+        assertSame(dg1, dg2) ;
+        Txn.executeWrite(dg1, ()->{
+            dg1.add(quad1) ;    
+        }) ;
+        Txn.executeRead(dg2, ()->{
+            assertTrue(dg2.contains(quad1)) ;
+        }) ;
+    }
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/test/java/org/seaborne/tdb2/junit/TL.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/junit/TL.java b/tdb2/src/test/java/org/seaborne/tdb2/junit/TL.java
index ad1f452..47ee5d0 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/junit/TL.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/junit/TL.java
@@ -23,6 +23,7 @@ import org.apache.jena.query.Dataset ;
 import org.apache.jena.sparql.core.DatasetGraph ;
 import org.seaborne.dboe.base.file.Location ;
 import org.seaborne.tdb2.ConfigTest ;
+import org.seaborne.tdb2.DatabaseMgr ;
 import org.seaborne.tdb2.TDB2Factory ;
 import org.seaborne.tdb2.sys.StoreConnection ;
 import org.seaborne.tdb2.sys.SystemTDB ;
@@ -76,7 +77,7 @@ public class TL {
     }
 
     public static DatasetGraph createTestDatasetGraphMem() {
-        DatasetGraph dataset = TDB2Factory.createDatasetGraph() ;
+        DatasetGraph dataset = DatabaseMgr.createDatasetGraph() ;
         // Non-transactional tests
         dataset = SystemTDB.setNonTransactional(dataset) ;
         return dataset ;

http://git-wip-us.apache.org/repos/asf/jena/blob/d72f6961/tdb2/src/test/java/org/seaborne/tdb2/store/TestTransPromoteTDB.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/store/TestTransPromoteTDB.java b/tdb2/src/test/java/org/seaborne/tdb2/store/TestTransPromoteTDB.java
index 9976f78..2814f32 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/store/TestTransPromoteTDB.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/store/TestTransPromoteTDB.java
@@ -23,7 +23,7 @@ import org.apache.log4j.Logger;
 import org.seaborne.dboe.transaction.txn.Transaction;
 import org.seaborne.dboe.transaction.txn.TransactionCoordinator;
 import org.seaborne.dboe.transaction.txn.TransactionException;
-import org.seaborne.tdb2.TDB2Factory;
+import org.seaborne.tdb2.DatabaseMgr ;
 
 public class TestTransPromoteTDB extends AbstractTestTransPromoteTDB2 {
 
@@ -62,7 +62,7 @@ public class TestTransPromoteTDB extends AbstractTestTransPromoteTDB2 {
 
     @Override
     protected DatasetGraph create() {
-        return TDB2Factory.createDatasetGraph();
+        return DatabaseMgr.createDatasetGraph();
     }
 
     @Override


[06/50] [abbrv] jena git commit: Migrate tests to DatasetGraphSwitchable

Posted by an...@apache.org.
Migrate tests to DatasetGraphSwitchable


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

Branch: refs/heads/jena-tdb2
Commit: 218d10c5010f670566bd33cb49503370834388bd
Parents: 780d671
Author: Andy Seaborne <an...@seaborne.org>
Authored: Sat Sep 2 18:21:59 2017 +0100
Committer: Andy Seaborne <an...@seaborne.org>
Committed: Sat Sep 2 18:40:31 2017 +0100

----------------------------------------------------------------------
 .../java/org/seaborne/tdb2/DatabaseMgr.java     |  18 +-
 tdb2/src/main/java/org/seaborne/tdb2/TDB2.java  |   8 +-
 .../tdb2/repack/DatasetGraphRecorder.java       |  53 -----
 .../tdb2/repack/DatasetGraphSwitchable.java     | 214 -------------------
 .../org/seaborne/tdb2/repack/sys/CopyDSG.java   |  53 -----
 .../seaborne/tdb2/repack/sys/FilenameUtils.java | 102 ---------
 .../java/org/seaborne/tdb2/repack/sys/Util.java |  49 -----
 .../seaborne/tdb2/solver/QueryEngineTDB.java    |  24 +--
 .../tdb2/store/DatasetGraphSwitchable.java      | 197 +++++++++++++++++
 .../seaborne/tdb2/store/DatasetGraphTxn.java    |   2 +-
 .../tdb2/store/DatasetGraphWrapperTxn.java      |  34 +++
 .../tdb2/store/GraphViewSwitchable.java         | 139 ++++++++++++
 .../java/org/seaborne/tdb2/sys/CopyDSG.java     |  53 +++++
 .../seaborne/tdb2/sys/DatabaseConnection.java   |  16 +-
 .../java/org/seaborne/tdb2/sys/DatabaseOps.java |   5 +-
 .../org/seaborne/tdb2/sys/FilenameUtils.java    | 102 +++++++++
 .../main/java/org/seaborne/tdb2/sys/IOX.java    |   1 -
 .../org/seaborne/tdb2/sys/StoreConnection.java  |  14 +-
 .../java/org/seaborne/tdb2/sys/TDBInternal.java |  56 ++++-
 .../main/java/org/seaborne/tdb2/sys/Util.java   |  49 +++++
 .../src/test/java/org/seaborne/tdb2/TC_TDB.java |   2 +-
 .../test/java/org/seaborne/tdb2/TS_Factory.java |   3 -
 .../seaborne/tdb2/TestDatabaseConnection.java   |  68 ------
 .../java/org/seaborne/tdb2/TestDatabaseMgr.java |   8 +-
 .../java/org/seaborne/tdb2/TestDatabaseOps.java | 114 ----------
 .../java/org/seaborne/tdb2/TestTDBFactory.java  |  16 +-
 .../tdb2/assembler/TestTDBAssembler.java        |  31 ++-
 .../tdb2/graph/TestPrefixMappingTDB.java        |   8 +-
 .../test/java/org/seaborne/tdb2/junit/TL.java   |   8 +-
 .../tdb2/setup/TestStoreParamsCreate.java       |  12 +-
 .../org/seaborne/tdb2/solver/TestStats.java     |   3 +-
 .../AbstractTestStoreConnectionBasics.java      |   6 +-
 .../tdb2/store/TestDatasetTDBPersist.java       |  11 +-
 .../seaborne/tdb2/store/TestTransactions.java   |   7 +-
 .../test/java/org/seaborne/tdb2/sys/TS_Sys.java |  10 +-
 .../tdb2/sys/TestDatabaseConnection.java        |  69 ++++++
 .../org/seaborne/tdb2/sys/TestDatabaseOps.java  | 117 ++++++++++
 37 files changed, 913 insertions(+), 769 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/main/java/org/seaborne/tdb2/DatabaseMgr.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/DatabaseMgr.java b/tdb2/src/main/java/org/seaborne/tdb2/DatabaseMgr.java
index 7b3ff17..b4dfd23 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/DatabaseMgr.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/DatabaseMgr.java
@@ -21,10 +21,11 @@ package org.seaborne.tdb2;
 import org.apache.jena.query.Dataset ;
 import org.apache.jena.sparql.core.DatasetGraph ;
 import org.seaborne.dboe.base.file.Location ;
-import org.seaborne.tdb2.repack.DatasetGraphSwitchable ;
+import org.seaborne.tdb2.store.DatasetGraphSwitchable ;
 import org.seaborne.tdb2.store.DatasetGraphTDB ;
 import org.seaborne.tdb2.sys.DatabaseConnection ;
 import org.seaborne.tdb2.sys.DatabaseOps ;
+import org.seaborne.tdb2.sys.TDBInternal ;
 
 /** Operations for TDBS DatasetGraph, including admin operations 
  * See {@link TDB2Factory} for creating API-level {@link Dataset Datasets}.
@@ -86,10 +87,7 @@ public class DatabaseMgr {
 
     /** Return the location of a dataset if it is backed by TDB, else null */ 
     public static boolean isBackedByTDB(DatasetGraph datasetGraph) {
-        DatasetGraphTDB dsg = requireDirect(datasetGraph);
-        if ( dsg == null )
-            return false ;
-        return true;
+        return TDBInternal.isBackedByTDB(datasetGraph);
     }
 
     /** Return the location of a DatasetGraph if it is backed by TDB, else null */
@@ -107,12 +105,10 @@ public class DatabaseMgr {
     }
 
     static DatasetGraphTDB requireDirect(DatasetGraph datasetGraph) {
-        DatasetGraph dsg = datasetGraph;
-        if ( datasetGraph instanceof DatasetGraphSwitchable )
-            dsg = ((DatasetGraphSwitchable)datasetGraph).get();
-        if ( datasetGraph instanceof DatasetGraphTDB )
-            return ((DatasetGraphTDB)datasetGraph);
-        throw new TDBException("Not a TDB database (argument is neither a switchable nor direct TDB DatasetGraph)");
+        DatasetGraphTDB dsg = TDBInternal.getDatasetGraphTDB(datasetGraph);
+        if ( dsg == null )
+            throw new TDBException("Not a TDB database (argument is neither a switchable nor direct TDB DatasetGraph)");
+        return dsg;
     }
 
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/main/java/org/seaborne/tdb2/TDB2.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/TDB2.java b/tdb2/src/main/java/org/seaborne/tdb2/TDB2.java
index 3d2ea09..6a18faa 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/TDB2.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/TDB2.java
@@ -40,8 +40,8 @@ import org.seaborne.tdb2.modify.UpdateEngineTDB ;
 import org.seaborne.tdb2.solver.QueryEngineTDB ;
 import org.seaborne.tdb2.solver.StageGeneratorDirectTDB ;
 import org.seaborne.tdb2.sys.EnvTDB ;
-import org.seaborne.tdb2.sys.StoreConnection ;
 import org.seaborne.tdb2.sys.SystemTDB ;
+import org.seaborne.tdb2.sys.TDBInternal ;
 import org.slf4j.Logger ;
 import org.slf4j.LoggerFactory ;
 
@@ -94,11 +94,11 @@ public class TDB2 {
     }
 
     /**
-     * Release any and all system resources held by TDB. This does NOT close or
-     * release datasets or graphs held by client code.
+     * Release any and all system resources held by TDB.
+     * All release datasets or graphs held by client code are no longer valid. 
      */
     public static void closedown() {
-        StoreConnection.reset() ;
+        TDBInternal.reset() ;
     }
 
     /** Sync a TDB-backed Model. Do nothing if not TDB-backed. */

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/main/java/org/seaborne/tdb2/repack/DatasetGraphRecorder.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/repack/DatasetGraphRecorder.java b/tdb2/src/main/java/org/seaborne/tdb2/repack/DatasetGraphRecorder.java
deleted file mode 100644
index 2cca0ae..0000000
--- a/tdb2/src/main/java/org/seaborne/tdb2/repack/DatasetGraphRecorder.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- *  Licensed 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.
- *
- *  See the NOTICE file distributed with this work for additional
- *  information regarding copyright ownership.
- */
-
-package org.seaborne.tdb2.repack;
-
-import java.io.File ;
-
-import org.apache.jena.sparql.core.DatasetChanges ;
-import org.apache.jena.sparql.core.DatasetGraph ;
-import org.apache.jena.sparql.core.DatasetGraphMonitor ;
-
-public class DatasetGraphRecorder extends DatasetGraphMonitor {
-
-    DatasetChanges monitor ; 
-    
-    public DatasetGraphRecorder(DatasetGraph dsg, String directory) {
-        super(dsg, createMonitor(directory));
-    }
-    
-    public void startLogging() { System.err.println("Not implemented"); }
-    public void stopLogging() { System.err.println("Not implemented"); }
-    
-
-    private static DatasetChanges createMonitor(String directory) {
-        File place = new File(directory) ;
-        if ( ! place.exists() ) {
-            
-        }
-        if ( ! place.isDirectory() ) {
-            
-        }
-        if ( ! place.canWrite() ) {
-            
-        }
-        return null ;
-    }
-
-}
-

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/main/java/org/seaborne/tdb2/repack/DatasetGraphSwitchable.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/repack/DatasetGraphSwitchable.java b/tdb2/src/main/java/org/seaborne/tdb2/repack/DatasetGraphSwitchable.java
deleted file mode 100644
index 642fb61..0000000
--- a/tdb2/src/main/java/org/seaborne/tdb2/repack/DatasetGraphSwitchable.java
+++ /dev/null
@@ -1,214 +0,0 @@
-/*
- *  Licensed 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.
- *
- *  See the NOTICE file distributed with this work for additional
- *  information regarding copyright ownership.
- */
-
-package org.seaborne.tdb2.repack;
-
-import java.nio.file.Path;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.apache.jena.graph.Graph;
-import org.apache.jena.graph.Node;
-import org.apache.jena.shared.PrefixMapping;
-import org.apache.jena.shared.impl.PrefixMappingImpl;
-import org.apache.jena.sparql.core.*;
-import org.seaborne.tdb2.store.DatasetGraphTDB;
-
-final
-public class DatasetGraphSwitchable extends DatasetGraphWrapper
-{
-    // QueryEngineFactoryWrapper has a QueryEngineFactory that is always loaded that
-    // executes on the unwrapped DSG (recursively). Unwrapping is via getBase, calling
-    // getWrapped() which is implemented with get().
-    
-//    static { 
-//        // QueryEngineRegistry.addFactory(factory());
-//    }
-    
-    private final AtomicReference<DatasetGraph> dsgx = new AtomicReference<>();
-    // Null for in-memory datasets.
-    private final Path basePath;
-    
-    public DatasetGraphSwitchable(Path base, DatasetGraph dsg) {
-        // Don't use the slot in datasetGraphWrapper - use the AtomicReference
-        super(null) ;
-        dsgx.set(dsg);
-        this.basePath = base;  
-    }
-
-    /** Is this {@code DatasetGraphSwitchable} just a holder for a {@code DatasetGraph}?
-     *  If so, it does not have a location on disk.
-     */
-    public boolean hasContainerPath() { return basePath != null; } 
-    
-    public Path getContainerPath() { return basePath; }
-    
-    /** The dataset to use for redirection - can be overridden.
-     *  It is also guaranteed that this is called only once per
-     *  delegated call.  Changes to the wrapped object can be
-     *  made based on that contract.
-     */
-    @Override
-    public DatasetGraph get() { return dsgx.get(); }
-
-    /** Set the base {@link DatasetGraph}.
-     * Returns the old value.
-     */ 
-    public DatasetGraph set(DatasetGraph dsg) { 
-        return dsgx.getAndSet(dsg);
-    }
-    
-    /** If and only if the current value is the given old value, set the base {@link DatasetGraph}  
-     * Returns true if a swap happened.
-     */ 
-    public boolean change(DatasetGraph oldDSG, DatasetGraph newDSG) { 
-        return dsgx.compareAndSet(oldDSG, newDSG);
-    }
-
-    // Used directly, Does not get prefix mapping right.
-    // GraphView has a private, empty prefix mapping
-
-    Graph dft = new GraphView(this, Quad.defaultGraphNodeGenerated) {
-        @Override
-        protected PrefixMapping createPrefixMapping() {
-            return prefixMapping(null);
-        } 
-    };
-    
-    @Override
-    public Graph getDefaultGraph() {
-        // Via the switchable.
-        return new GraphView(this, Quad.defaultGraphNodeGenerated) {
-            @Override
-            protected PrefixMapping createPrefixMapping() {
-                return prefixMapping(null);
-            }
-        };
-    }
-    
-    @Override
-    public Graph getGraph(Node gn) {
-        if ( Quad.isUnionGraph(gn) )
-            return GraphView.createUnionGraph(get());
-        
-        return new GraphView(this, gn) {
-            @Override
-            protected PrefixMapping createPrefixMapping() {
-                return prefixMapping(gn);
-            }
-        };
-    }
-
-    // TDB2 specific.
-    // Does not cope with blank nodes.
-    // A PrefixMapping sending operations via the switchable.
-    private PrefixMapping prefixMapping(Node graphName) {
-        
-        String gn = (graphName == null) ? "" : graphName.getURI(); 
-        
-        return new PrefixMappingImpl() {
-            
-            DatasetPrefixStorage dps() {
-                return ((DatasetGraphTDB)dsgx.get()).getPrefixes();
-            }
-            
-            Graph graph() {
-                DatasetGraphTDB dsg = (DatasetGraphTDB)dsgx.get();
-                if ( gn == null )
-                    return dsg.getDefaultGraph();
-                else
-                    return dsg.getGraph(graphName);
-            }
-            
-            PrefixMapping prefixMapping() {
-                if ( gn == null )
-                    return dps().getPrefixMapping();
-                else
-                    return dps().getPrefixMapping(gn); 
-            }
-
-            @Override
-            protected void set(String prefix, String uri) {
-                dps().insertPrefix(gn, prefix, uri);
-                super.set(prefix, uri);
-            }
-
-            @Override
-            protected String get(String prefix) {
-                return dps().readPrefix(gn, prefix);
-            }
-
-            @Override
-            protected void remove(String prefix) {
-                dps().getPrefixMapping().removeNsPrefix(prefix);
-                super.remove(prefix);
-            }
-            
-            @Override
-            public Map<String, String> getNsPrefixMap() {
-                return prefixMapping().getNsPrefixMap();
-                //return graph().getPrefixMapping().getNsPrefixMap();
-            }
-        };
-    }
-    
-    //static { register() ; }
-    
-    
-//    static QueryEngineFactory factory() {
-//        return new QueryEngineFactory() {
-//            @Override
-//            public boolean accept(Op op, DatasetGraph dataset, Context context) {
-//                DatasetGraphSwitchable dsg = extract(dataset) ;
-//                if ( dsg == null ) return false;
-//                QueryEngineFactory f = QueryEngineRegistry.findFactory(op, dsg.get(), context);
-//                return f.accept(op, dataset, context);
-//            }
-//
-//            @Override
-//            public Plan create(Op op, DatasetGraph dataset, Binding inputBinding, Context context) {
-//                DatasetGraphSwitchable dsg = extract(dataset) ;
-//                if ( dsg == null ) return null;
-//                QueryEngineFactory f = QueryEngineRegistry.findFactory(op, dsg.get(), context);
-//                return f.create(op, dataset, inputBinding, context);
-//            }
-//
-//            private DatasetGraphSwitchable extract(DatasetGraph dataset) {
-//                if ( dataset instanceof DatasetGraphSwitchable )
-//                    return (DatasetGraphSwitchable)dataset;
-//                return null;
-//            }
-//
-//            @Override
-//            public boolean accept(Query query, DatasetGraph dataset, Context context) {
-//                DatasetGraphSwitchable dsg = extract(dataset) ;
-//                if ( dsg == null ) return false;
-//                QueryEngineFactory f = QueryEngineRegistry.findFactory(query, dsg.get(), context);
-//                return f.accept(query, dataset, context);
-//            }
-//
-//            @Override
-//            public Plan create(Query query, DatasetGraph dataset, Binding inputBinding, Context context) {
-//                DatasetGraphSwitchable dsg = extract(dataset) ;
-//                if ( dsg == null ) return null;
-//                QueryEngineFactory f = QueryEngineRegistry.findFactory(query, dsg.get(), context);
-//                return f.create(query, dataset, inputBinding, context);
-//            }
-//        };
-//    }
-}
-

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/main/java/org/seaborne/tdb2/repack/sys/CopyDSG.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/repack/sys/CopyDSG.java b/tdb2/src/main/java/org/seaborne/tdb2/repack/sys/CopyDSG.java
deleted file mode 100644
index 6701f88..0000000
--- a/tdb2/src/main/java/org/seaborne/tdb2/repack/sys/CopyDSG.java
+++ /dev/null
@@ -1,53 +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.seaborne.tdb2.repack.sys;
-
-import java.util.Iterator;
-import java.util.List;
-
-import org.apache.jena.atlas.iterator.Iter;
-import org.apache.jena.graph.Graph;
-import org.apache.jena.graph.Node;
-import org.apache.jena.sparql.core.DatasetGraph;
-import org.apache.jena.sparql.core.Quad;
-import org.seaborne.dboe.jenax.Txn;
-
-/** Copy operations of any {@link DatasetGraph} */
-public class CopyDSG {
-
-    public static void copy(DatasetGraph dsgSrc, DatasetGraph dsgDst) {
-        Txn.executeRead(dsgSrc, ()->{
-            Txn.executeWrite(dsgDst, () -> {
-                Iterator<Quad> iter = dsgSrc.find();
-                iter.forEachRemaining(dsgDst::add);
-                copyPrefixes(dsgSrc, dsgDst);
-            });
-        });
-    }
-
-    public static void copyPrefixes(DatasetGraph dsgSrc, DatasetGraph dsgDst) {
-        List<Node> graphNames = Iter.toList(dsgSrc.listGraphNodes());
-        copyPrefixes(dsgSrc.getDefaultGraph(), dsgDst.getDefaultGraph());
-        graphNames.forEach((gn)->copyPrefixes(dsgSrc.getGraph(gn), dsgDst.getGraph(gn)));
-    }
-
-    public static void copyPrefixes(Graph srcGraph, Graph dstGraph) {
-        dstGraph.getPrefixMapping().setNsPrefixes(srcGraph.getPrefixMapping());
-    }
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/main/java/org/seaborne/tdb2/repack/sys/FilenameUtils.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/repack/sys/FilenameUtils.java b/tdb2/src/main/java/org/seaborne/tdb2/repack/sys/FilenameUtils.java
deleted file mode 100644
index f0330ac..0000000
--- a/tdb2/src/main/java/org/seaborne/tdb2/repack/sys/FilenameUtils.java
+++ /dev/null
@@ -1,102 +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.seaborne.tdb2.repack.sys;
-
-import java.io.IOException;
-import java.nio.file.DirectoryStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
-import java.util.regex.Pattern;
-
-import org.apache.jena.atlas.logging.FmtLog;
-import org.seaborne.dboe.DBOpEnvException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class FilenameUtils {
-
-    static Logger LOG = LoggerFactory.getLogger(FilenameUtils.class);
-
-    /** Find the files in this directory that have namebase as a prefix and 
-     *  are then numbered.
-     *  <p>
-     *  Returns a sorted list from, low to high index.
-     */
-    public static List<Path> scanForDirByPattern(Path directory, String namebase, String nameSep) {
-        Pattern pattern = Pattern.compile(Pattern.quote(namebase)+
-                                          Pattern.quote(nameSep)+
-                                          "[\\d]+");
-        List<Path> paths = new ArrayList<>();
-        try (DirectoryStream<Path> stream = Files.newDirectoryStream(directory, namebase + nameSep + "*")) {
-            for ( Path entry : stream ) {
-                if ( !pattern.matcher(entry.getFileName().toString()).matches() ) {
-                    throw new DBOpEnvException("Invalid filename for matching: "+entry.getFileName());
-                    // Alternative: Skip bad trailing parts but more likely there is a naming problem.  
-                    //   LOG.warn("Invalid filename for matching: {} skipped", entry.getFileName());
-                    //   continue;
-                }
-                // Follows symbolic links.
-                if ( !Files.isDirectory(entry) )
-                    throw new DBOpEnvException("Not a directory: "+entry);
-                paths.add(entry);
-            }
-        }
-        catch (IOException ex) {
-            FmtLog.warn(LOG, "Can't inspect directory: (%s, %s)", directory, namebase);
-            throw new DBOpEnvException(ex);
-        }
-        Comparator<Path> comp = (f1, f2) -> {
-            int num1 = extractIndex(f1.getFileName().toString(), namebase, nameSep);
-            int num2 = extractIndex(f2.getFileName().toString(), namebase, nameSep);
-            return Integer.compare(num1, num2);
-        };
-        paths.sort(comp);
-        //indexes.sort(Long::compareTo);
-        return paths;
-    }
-
-    /**
-     * Extract the inde from a version-ed filename. (Base-NNNN format). 
-     * @param name
-     * @param namebase
-     * @param nameSep
-     */
-    public static int extractIndex(String name, String namebase, String nameSep) {
-        int i = namebase.length()+nameSep.length();
-        String numStr = name.substring(i);
-        int num = Integer.parseInt(numStr);
-        return num;
-    }
-    
-    /** Construct a filename */
-    public static String filename(String prefix, String sep, int N) {
-        return String.format("%s%s%04d", prefix, sep, N);
-    }
-    
-    /** Construct a filename */
-    public static String filename(String prefix, String sep, String index) {
-        return String.format("%s%s%s", prefix, sep, index);
-    }
-    
-
-
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/main/java/org/seaborne/tdb2/repack/sys/Util.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/repack/sys/Util.java b/tdb2/src/main/java/org/seaborne/tdb2/repack/sys/Util.java
deleted file mode 100644
index 7b69090..0000000
--- a/tdb2/src/main/java/org/seaborne/tdb2/repack/sys/Util.java
+++ /dev/null
@@ -1,49 +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.seaborne.tdb2.repack.sys;
-
-import java.util.List;
-import java.util.NoSuchElementException;
-
-/** Utility code that should go somewhere */
-public class Util {
-    
-    /** Get the last element of a list, or throw NoSuchElementException
-     * @param list
-     * @return T
-     * @throws NoSuchElementException
-     */
-    public static <T> T getLast(List<T> list) {
-        T elt = getLastOrNull(list);
-        if ( elt == null )
-            throw new NoSuchElementException();
-        return elt;  
-    }
-    
-    /**
-     * Get the last element of a list, or return null.
-     * @param list
-     * @return T
-     */
-    public static <T> T getLastOrNull(List<T> list) {
-        if ( list.isEmpty() )
-            return null;
-        return list.get(list.size()-1);  
-    }
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/main/java/org/seaborne/tdb2/solver/QueryEngineTDB.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/solver/QueryEngineTDB.java b/tdb2/src/main/java/org/seaborne/tdb2/solver/QueryEngineTDB.java
index b6f4a65..dfece29 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/solver/QueryEngineTDB.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/solver/QueryEngineTDB.java
@@ -39,7 +39,7 @@ import org.seaborne.tdb2.TDB2 ;
 import org.seaborne.tdb2.TDBException ;
 import org.seaborne.tdb2.migrate.A2 ;
 import org.seaborne.tdb2.store.DatasetGraphTDB ;
-import org.seaborne.tdb2.store.DatasetGraphTxn ;
+import org.seaborne.tdb2.sys.TDBInternal ;
 
 // This exists to intercept the query execution setup.
 //  e.g choose the transformation optimizations
@@ -140,21 +140,17 @@ public class QueryEngineTDB extends QueryEngineMain
         
     protected static class QueryEngineFactoryTDB implements QueryEngineFactory
     {
-        // If a DatasetGraphTransaction is passed in, we are outside a transaction.
-        
-        private static boolean isHandledByTDB(DatasetGraph dataset)
-        {
-            if (dataset instanceof DatasetGraphTxn) return true ;
-            if (dataset instanceof DatasetGraphTDB) return true ;
-            //if (dataset instanceof DatasetGraphTransaction ) return true ;
-            return false ;
+        private static boolean isHandledByTDB(DatasetGraph dataset) {
+            return TDBInternal.isBackedByTDB(dataset);
         }
         
-        protected DatasetGraphTDB dsgToQuery(DatasetGraph dataset)
-        {
-            if (dataset instanceof DatasetGraphTDB) 
-                return (DatasetGraphTDB)dataset ;
-            throw new TDBException("Internal inconsistency: trying to execute query on unrecognized kind of DatasetGraph: "+Lib.className(dataset)) ;
+        protected DatasetGraphTDB dsgToQuery(DatasetGraph dataset) {
+            try { 
+                return TDBInternal.requireStorage(dataset);
+            } catch (TDBException ex) {
+                // Check to a more specific message. 
+                throw new TDBException("Internal inconsistency: trying to execute query on unrecognized kind of DatasetGraph: "+Lib.className(dataset)) ;
+            }
         }
         
         @Override

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetGraphSwitchable.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetGraphSwitchable.java b/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetGraphSwitchable.java
new file mode 100644
index 0000000..115b304
--- /dev/null
+++ b/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetGraphSwitchable.java
@@ -0,0 +1,197 @@
+/*
+ *  Licensed 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.
+ *
+ *  See the NOTICE file distributed with this work for additional
+ *  information regarding copyright ownership.
+ */
+
+package org.seaborne.tdb2.store;
+
+import java.nio.file.Path;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.jena.atlas.lib.Cache ;
+import org.apache.jena.atlas.lib.CacheFactory ;
+import org.apache.jena.graph.Graph;
+import org.apache.jena.graph.Node;
+import org.apache.jena.shared.PrefixMapping;
+import org.apache.jena.shared.impl.PrefixMappingImpl;
+import org.apache.jena.sparql.core.*;
+
+final
+public class DatasetGraphSwitchable extends DatasetGraphWrapperTxn /* Until ARQ catches up with promote */ 
+{
+    // QueryEngineFactoryWrapper has a QueryEngineFactory that is always loaded that
+    // executes on the unwrapped DSG (recursively). Unwrapping is via getBase, calling
+    // getWrapped() which is implemented with get().
+    
+//    static { 
+//        // QueryEngineRegistry.addFactory(factory());
+//    }
+    
+    private final AtomicReference<DatasetGraph> dsgx = new AtomicReference<>();
+    // Null for in-memory datasets.
+    private final Path basePath;
+    
+    public DatasetGraphSwitchable(Path base, DatasetGraph dsg) {
+        // Don't use the slot in datasetGraphWrapper - use the AtomicReference
+        super(null) ;
+        dsgx.set(dsg);
+        this.basePath = base;  
+    }
+
+    /** Is this {@code DatasetGraphSwitchable} just a holder for a {@code DatasetGraph}?
+     *  If so, it does not have a location on disk.
+     */
+    public boolean hasContainerPath() { return basePath != null; } 
+    
+    public Path getContainerPath() { return basePath; }
+    
+    /** The dataset to use for redirection - can be overridden.
+     *  It is also guaranteed that this is called only once per
+     *  delegated call.  Changes to the wrapped object can be
+     *  made based on that contract.
+     */
+    @Override
+    public DatasetGraph get() { return dsgx.get(); }
+
+    /** Set the base {@link DatasetGraph}.
+     * Returns the old value.
+     */ 
+    public DatasetGraph set(DatasetGraph dsg) { 
+        return dsgx.getAndSet(dsg);
+    }
+    
+    /** If and only if the current value is the given old value, set the base {@link DatasetGraph}  
+     * Returns true if a swap happened.
+     */ 
+    public boolean change(DatasetGraph oldDSG, DatasetGraph newDSG) { 
+        // No need to clear. ngCache.clear();
+        return dsgx.compareAndSet(oldDSG, newDSG);
+    }
+
+    private Graph dftGraph = GraphViewSwitchable.createDefaultGraph(this);
+    
+    @Override
+    public Graph getDefaultGraph() {
+        return dftGraph;
+    }
+    
+//    private Cache<Node, Graph> ngCache = CacheFactory.createCache(10);
+    private Cache<Node, Graph> ngCache = CacheFactory.createOneSlotCache();
+    
+    @Override
+    public Graph getGraph(Node gn) {
+        return ngCache.getOrFill(gn, ()->GraphViewSwitchable.createNamedGraph(this, gn));
+    }
+
+    // TDB2 specific.
+    // Does not cope with blank nodes.
+    // A PrefixMapping sending operations via the switchable.
+    private PrefixMapping prefixMapping(Node graphName) {
+        
+        String gn = (graphName == null) ? "" : graphName.getURI(); 
+        
+        return new PrefixMappingImpl() {
+            
+            DatasetPrefixStorage dps() {
+                return ((DatasetGraphTDB)dsgx.get()).getPrefixes();
+            }
+            
+            Graph graph() {
+                DatasetGraphTDB dsg = (DatasetGraphTDB)dsgx.get();
+                if ( gn == null )
+                    return dsg.getDefaultGraph();
+                else
+                    return dsg.getGraph(graphName);
+            }
+            
+            PrefixMapping prefixMapping() {
+                if ( gn == null )
+                    return dps().getPrefixMapping();
+                else
+                    return dps().getPrefixMapping(gn); 
+            }
+
+            @Override
+            protected void set(String prefix, String uri) {
+                dps().insertPrefix(gn, prefix, uri);
+                super.set(prefix, uri);
+            }
+
+            @Override
+            protected String get(String prefix) {
+                return dps().readPrefix(gn, prefix);
+            }
+
+            @Override
+            protected void remove(String prefix) {
+                dps().getPrefixMapping().removeNsPrefix(prefix);
+                super.remove(prefix);
+            }
+            
+            @Override
+            public Map<String, String> getNsPrefixMap() {
+                return prefixMapping().getNsPrefixMap();
+                //return graph().getPrefixMapping().getNsPrefixMap();
+            }
+        };
+    }
+    
+    //static { register() ; }
+    
+    
+//    static QueryEngineFactory factory() {
+//        return new QueryEngineFactory() {
+//            @Override
+//            public boolean accept(Op op, DatasetGraph dataset, Context context) {
+//                DatasetGraphSwitchable dsg = extract(dataset) ;
+//                if ( dsg == null ) return false;
+//                QueryEngineFactory f = QueryEngineRegistry.findFactory(op, dsg.get(), context);
+//                return f.accept(op, dataset, context);
+//            }
+//
+//            @Override
+//            public Plan create(Op op, DatasetGraph dataset, Binding inputBinding, Context context) {
+//                DatasetGraphSwitchable dsg = extract(dataset) ;
+//                if ( dsg == null ) return null;
+//                QueryEngineFactory f = QueryEngineRegistry.findFactory(op, dsg.get(), context);
+//                return f.create(op, dataset, inputBinding, context);
+//            }
+//
+//            private DatasetGraphSwitchable extract(DatasetGraph dataset) {
+//                if ( dataset instanceof DatasetGraphSwitchable )
+//                    return (DatasetGraphSwitchable)dataset;
+//                return null;
+//            }
+//
+//            @Override
+//            public boolean accept(Query query, DatasetGraph dataset, Context context) {
+//                DatasetGraphSwitchable dsg = extract(dataset) ;
+//                if ( dsg == null ) return false;
+//                QueryEngineFactory f = QueryEngineRegistry.findFactory(query, dsg.get(), context);
+//                return f.accept(query, dataset, context);
+//            }
+//
+//            @Override
+//            public Plan create(Query query, DatasetGraph dataset, Binding inputBinding, Context context) {
+//                DatasetGraphSwitchable dsg = extract(dataset) ;
+//                if ( dsg == null ) return null;
+//                QueryEngineFactory f = QueryEngineRegistry.findFactory(query, dsg.get(), context);
+//                return f.create(query, dataset, inputBinding, context);
+//            }
+//        };
+//    }
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetGraphTxn.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetGraphTxn.java b/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetGraphTxn.java
index 47bc8e0..0915ff0 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetGraphTxn.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetGraphTxn.java
@@ -23,7 +23,7 @@ import org.seaborne.dboe.transaction.Transactional ;
 /** Transaction representation of a DatasetGraph */ 
 public interface DatasetGraphTxn extends DatasetGraph, Transactional {
     // Combines org.seaborne.dboe.transaction.Transactional and org.apache.jena.sparql.core.Transactional.
-    // o.s.dboe.Transactional add promote() ... and  
+    //    o.s.dboe.Transactional add promote()
     
     @Override
     public default boolean supportsTransactions()       { return true ; }

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetGraphWrapperTxn.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetGraphWrapperTxn.java b/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetGraphWrapperTxn.java
new file mode 100644
index 0000000..e172c90
--- /dev/null
+++ b/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetGraphWrapperTxn.java
@@ -0,0 +1,34 @@
+/*
+ * 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.seaborne.tdb2.store;
+
+import org.apache.jena.sparql.core.DatasetGraphWrapper ;
+
+public class DatasetGraphWrapperTxn extends DatasetGraphWrapper implements DatasetGraphTxn {
+
+    public DatasetGraphWrapperTxn(DatasetGraphTxn dsg) {
+        super(dsg) ;
+    }
+
+    @Override
+    public boolean promote() {
+        return ((DatasetGraphTxn)get()).promote() ;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/main/java/org/seaborne/tdb2/store/GraphViewSwitchable.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/GraphViewSwitchable.java b/tdb2/src/main/java/org/seaborne/tdb2/store/GraphViewSwitchable.java
new file mode 100644
index 0000000..d52f7a0
--- /dev/null
+++ b/tdb2/src/main/java/org/seaborne/tdb2/store/GraphViewSwitchable.java
@@ -0,0 +1,139 @@
+/*
+ * 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.seaborne.tdb2.store;
+
+import java.util.Map ;
+
+import org.apache.jena.graph.Graph ;
+import org.apache.jena.graph.Node ;
+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 ;
+
+/** A GraphView that is sensitive to {@link DatasetGraphSwitchable} switching.
+ */
+public class GraphViewSwitchable extends GraphView {
+    // Fixups for GraphView
+    //   Prefixes.
+    //   Transaction handler.
+    // Long term - ensure that GraphView uses get() always, inc prefixes, transaction handlers
+    
+    // Factory style.
+    public static GraphViewSwitchable createDefaultGraph(DatasetGraphSwitchable dsg)
+    { return new GraphViewSwitchable(dsg, Quad.defaultGraphNodeGenerated) ; }
+    
+    public static GraphView createNamedGraph(DatasetGraphSwitchable dsg, Node graphIRI)
+    { return new GraphViewSwitchable(dsg, graphIRI) ; }
+    
+    public static GraphViewSwitchable createUnionGraph(DatasetGraphSwitchable dsg)
+    { return new GraphViewSwitchable(dsg, Quad.unionGraph) ; }
+    
+    private final DatasetGraphSwitchable dsgx;
+    protected DatasetGraphSwitchable getx() { return dsgx; }
+    
+    protected GraphViewSwitchable(DatasetGraphSwitchable dsg, Node gn) {
+        super(dsg, gn) ;
+        this.dsgx = dsg;
+    }
+
+//    @Override
+//    public TransactionHandler getTransactionHandler() {
+//        // XXX Awiting for promote to be enabled.
+//        return super.getTransactionHandler();
+//    }
+    
+    @Override
+    protected PrefixMapping createPrefixMapping() {
+        Node gn = super.getGraphName();
+        if ( gn == Quad.defaultGraphNodeGenerated )
+            gn = null;
+        if ( Quad.isUnionGraph(gn) ) {
+            // Read-only wrapper would be better that a copy.
+            PrefixMapping pmap = new PrefixMappingImpl();
+            pmap.setNsPrefixes(prefixMapping(null));
+            return pmap; 
+        }
+        return prefixMapping(gn);
+    }
+    
+    /** Return the {@code DatasetGraphSwitchable} we are viewing. */
+    @Override
+    public DatasetGraphSwitchable getDataset() {
+        return getx() ;
+    }
+
+    // TDB2 specific.
+    // Does not cope with blank nodes.
+    // A PrefixMapping sending operations via the switchable.
+    // Long term, rework as PrefixMapping over PrefixMap over DatasetPrefixStorage
+    private PrefixMapping prefixMapping(Node graphName) {
+        
+        String gn = (graphName == null) ? "" : graphName.getURI(); 
+        
+        return new PrefixMappingImpl() {
+            
+            DatasetPrefixStorage dps() {
+                return ((DatasetGraphTDB)(getx().get())).getPrefixes();
+            }
+            
+            Graph graph() {
+                DatasetGraphTDB dsg = (DatasetGraphTDB)getx().get();
+                if ( gn == null )
+                    return dsg.getDefaultGraph();
+                else
+                    return dsg.getGraph(graphName);
+            }
+            
+            PrefixMapping prefixMapping() {
+                if ( gn == null )
+                    return dps().getPrefixMapping();
+                else
+                    return dps().getPrefixMapping(gn); 
+            }
+
+            @Override
+            protected void set(String prefix, String uri) {
+                dps().insertPrefix(gn, prefix, uri);
+                super.set(prefix, uri);
+            }
+
+            @Override
+            protected String get(String prefix) {
+                //Ignore. String x = super.get(prefix);
+                // (Ignore more?)
+                return dps().readPrefix(gn, prefix);
+            }
+
+            @Override
+            protected void remove(String prefix) {
+                dps().getPrefixMapping().removeNsPrefix(prefix);
+                super.remove(prefix);
+            }
+            
+            @Override
+            public Map<String, String> getNsPrefixMap() {
+                return prefixMapping().getNsPrefixMap();
+                //return graph().getPrefixMapping().getNsPrefixMap();
+            }
+        };
+    }
+    
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/main/java/org/seaborne/tdb2/sys/CopyDSG.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/sys/CopyDSG.java b/tdb2/src/main/java/org/seaborne/tdb2/sys/CopyDSG.java
new file mode 100644
index 0000000..4e53ea3
--- /dev/null
+++ b/tdb2/src/main/java/org/seaborne/tdb2/sys/CopyDSG.java
@@ -0,0 +1,53 @@
+/*
+ * 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.seaborne.tdb2.sys;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.jena.atlas.iterator.Iter;
+import org.apache.jena.graph.Graph;
+import org.apache.jena.graph.Node;
+import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.core.Quad;
+import org.seaborne.dboe.jenax.Txn;
+
+/** Copy operations of any {@link DatasetGraph} */
+public class CopyDSG {
+
+    public static void copy(DatasetGraph dsgSrc, DatasetGraph dsgDst) {
+        Txn.executeRead(dsgSrc, ()->{
+            Txn.executeWrite(dsgDst, () -> {
+                Iterator<Quad> iter = dsgSrc.find();
+                iter.forEachRemaining(dsgDst::add);
+                copyPrefixes(dsgSrc, dsgDst);
+            });
+        });
+    }
+
+    public static void copyPrefixes(DatasetGraph dsgSrc, DatasetGraph dsgDst) {
+        List<Node> graphNames = Iter.toList(dsgSrc.listGraphNodes());
+        copyPrefixes(dsgSrc.getDefaultGraph(), dsgDst.getDefaultGraph());
+        graphNames.forEach((gn)->copyPrefixes(dsgSrc.getGraph(gn), dsgDst.getGraph(gn)));
+    }
+
+    public static void copyPrefixes(Graph srcGraph, Graph dstGraph) {
+        dstGraph.getPrefixMapping().setNsPrefixes(srcGraph.getPrefixMapping());
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseConnection.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseConnection.java b/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseConnection.java
index 62c5949..0e22f8f 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseConnection.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseConnection.java
@@ -33,8 +33,8 @@ import org.seaborne.dboe.base.file.Location ;
 import org.seaborne.dboe.base.file.ProcessFileLock ;
 import org.seaborne.dboe.sys.Names ;
 import org.seaborne.tdb2.TDBException ;
-import org.seaborne.tdb2.repack.DatasetGraphSwitchable ;
 import org.seaborne.tdb2.setup.StoreParams ;
+import org.seaborne.tdb2.store.DatasetGraphSwitchable ;
 
 // StoreConnection, DatabaseConnection < Connection<X> 
 
@@ -108,15 +108,18 @@ public class DatabaseConnection {
         return ProcessFileLock.create(lockFilename);
     }
     
-    /** Stop managing a location. Use with great care (testing only). */
-    public static synchronized void expel(Location location, boolean force) {
+    /** Use via {@link TDBInternal#expel} wherever possible.
+     * <p>
+     * Stop managing a location. Use with great care (testing only).<br/>
+     * Does not expel from {@link StoreConnection}.
+     */
+    public static synchronized void internalExpel(Location location, boolean force) {
         DatabaseConnection dbConn = cache.get(location) ;
         if (dbConn == null) return ;
         cache.remove(location) ;
         dbConn.isValid = false;
         //dbConn.datasetGraph = null;
         //dbConn.datasetGraphSwitchable = null;
-        StoreConnection.expel(location, force);
         
         //    if (!force && sConn.transactionManager.activeTransactions()) 
         //        throw new TDBTransactionException("Can't expel: Active transactions for location: " + location) ;
@@ -136,13 +139,12 @@ public class DatabaseConnection {
      * Use with extreme care.
      * This is intended to support internal testing.
      */
-    public static synchronized void reset() {
+    public static synchronized void internalReset() {
         // Copy to avoid potential CME.
         Set<Location> x = new HashSet<>(cache.keySet()) ;
         for (Location loc : x)
-            expel(loc, true) ;
+            internalExpel(loc, true) ;
         cache.clear() ;
-        StoreConnection.reset();
     }
     
     

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseOps.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseOps.java b/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseOps.java
index 82dc579..b985798 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseOps.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseOps.java
@@ -37,11 +37,8 @@ import org.seaborne.dboe.jenax.Txn ;
 import org.seaborne.dboe.transaction.txn.TransactionCoordinator;
 import org.seaborne.dboe.transaction.txn.TransactionalSystem;
 import org.seaborne.tdb2.TDBException;
-import org.seaborne.tdb2.repack.DatasetGraphSwitchable ;
-import org.seaborne.tdb2.repack.sys.CopyDSG;
-import org.seaborne.tdb2.repack.sys.FilenameUtils;
-import org.seaborne.tdb2.repack.sys.Util;
 import org.seaborne.tdb2.setup.StoreParams;
+import org.seaborne.tdb2.store.DatasetGraphSwitchable ;
 import org.seaborne.tdb2.store.DatasetGraphTDB;
 import org.slf4j.Logger ;
 import org.slf4j.LoggerFactory ;

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/main/java/org/seaborne/tdb2/sys/FilenameUtils.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/sys/FilenameUtils.java b/tdb2/src/main/java/org/seaborne/tdb2/sys/FilenameUtils.java
new file mode 100644
index 0000000..623ac99
--- /dev/null
+++ b/tdb2/src/main/java/org/seaborne/tdb2/sys/FilenameUtils.java
@@ -0,0 +1,102 @@
+/*
+ * 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.seaborne.tdb2.sys;
+
+import java.io.IOException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.apache.jena.atlas.logging.FmtLog;
+import org.seaborne.dboe.DBOpEnvException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FilenameUtils {
+
+    static Logger LOG = LoggerFactory.getLogger(FilenameUtils.class);
+
+    /** Find the files in this directory that have namebase as a prefix and 
+     *  are then numbered.
+     *  <p>
+     *  Returns a sorted list from, low to high index.
+     */
+    public static List<Path> scanForDirByPattern(Path directory, String namebase, String nameSep) {
+        Pattern pattern = Pattern.compile(Pattern.quote(namebase)+
+                                          Pattern.quote(nameSep)+
+                                          "[\\d]+");
+        List<Path> paths = new ArrayList<>();
+        try (DirectoryStream<Path> stream = Files.newDirectoryStream(directory, namebase + nameSep + "*")) {
+            for ( Path entry : stream ) {
+                if ( !pattern.matcher(entry.getFileName().toString()).matches() ) {
+                    throw new DBOpEnvException("Invalid filename for matching: "+entry.getFileName());
+                    // Alternative: Skip bad trailing parts but more likely there is a naming problem.  
+                    //   LOG.warn("Invalid filename for matching: {} skipped", entry.getFileName());
+                    //   continue;
+                }
+                // Follows symbolic links.
+                if ( !Files.isDirectory(entry) )
+                    throw new DBOpEnvException("Not a directory: "+entry);
+                paths.add(entry);
+            }
+        }
+        catch (IOException ex) {
+            FmtLog.warn(LOG, "Can't inspect directory: (%s, %s)", directory, namebase);
+            throw new DBOpEnvException(ex);
+        }
+        Comparator<Path> comp = (f1, f2) -> {
+            int num1 = extractIndex(f1.getFileName().toString(), namebase, nameSep);
+            int num2 = extractIndex(f2.getFileName().toString(), namebase, nameSep);
+            return Integer.compare(num1, num2);
+        };
+        paths.sort(comp);
+        //indexes.sort(Long::compareTo);
+        return paths;
+    }
+
+    /**
+     * Extract the inde from a version-ed filename. (Base-NNNN format). 
+     * @param name
+     * @param namebase
+     * @param nameSep
+     */
+    public static int extractIndex(String name, String namebase, String nameSep) {
+        int i = namebase.length()+nameSep.length();
+        String numStr = name.substring(i);
+        int num = Integer.parseInt(numStr);
+        return num;
+    }
+    
+    /** Construct a filename */
+    public static String filename(String prefix, String sep, int N) {
+        return String.format("%s%s%04d", prefix, sep, N);
+    }
+    
+    /** Construct a filename */
+    public static String filename(String prefix, String sep, String index) {
+        return String.format("%s%s%s", prefix, sep, index);
+    }
+    
+
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/main/java/org/seaborne/tdb2/sys/IOX.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/sys/IOX.java b/tdb2/src/main/java/org/seaborne/tdb2/sys/IOX.java
index 049fbb3..ed78f88 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/sys/IOX.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/sys/IOX.java
@@ -34,7 +34,6 @@ import org.apache.jena.atlas.RuntimeIOException;
 import org.apache.jena.atlas.logging.FmtLog;
 import org.seaborne.dboe.DBOpEnvException;
 import org.seaborne.dboe.base.file.Location;
-import org.seaborne.tdb2.repack.sys.Util ;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/main/java/org/seaborne/tdb2/sys/StoreConnection.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/sys/StoreConnection.java b/tdb2/src/main/java/org/seaborne/tdb2/sys/StoreConnection.java
index 3ef596f..54d863b 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/sys/StoreConnection.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/sys/StoreConnection.java
@@ -101,22 +101,26 @@ public class StoreConnection
      * Use with extreme care.
      * This is intended to support internal testing.
      */
-    public static synchronized void reset() {
+    public static synchronized void internalReset() {
         // Copy to avoid potential CME.
         Set<Location> x = new HashSet<>(cache.keySet()) ;
         for (Location loc : x)
-            expel(loc, true) ;
+            internalExpel(loc, true) ;
         cache.clear() ;
         ChannelManager.reset(); 
     }
 
     /** Stop managing a location. There should be no transactions running. */
     public static synchronized void release(Location location) {
-        expel(location, false) ;
+        internalExpel(location, false) ;
     }
 
-    /** Stop managing a location. Use with great care (testing only). */
-    public static synchronized void expel(Location location, boolean force) {
+    /** Use via {@link TDBInternal#expel} wherever possible.
+     * <p>
+     * Stop managing a location.<br/> 
+     * Use with great care (testing only).
+     */
+    public static synchronized void internalExpel(Location location, boolean force) {
         StoreConnection sConn = cache.get(location) ;
         if (sConn == null) return ;
 

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/main/java/org/seaborne/tdb2/sys/TDBInternal.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/sys/TDBInternal.java b/tdb2/src/main/java/org/seaborne/tdb2/sys/TDBInternal.java
index 466e1d2..25c1688 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/sys/TDBInternal.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/sys/TDBInternal.java
@@ -20,12 +20,20 @@ package org.seaborne.tdb2.sys;
 import org.apache.jena.graph.Node;
 import org.apache.jena.query.Dataset;
 import org.apache.jena.sparql.core.DatasetGraph;
+import org.seaborne.dboe.base.file.Location ;
+import org.seaborne.tdb2.TDBException ;
+import org.seaborne.tdb2.store.DatasetGraphSwitchable ;
 import org.seaborne.tdb2.store.DatasetGraphTDB;
 import org.seaborne.tdb2.store.NodeId;
 import org.seaborne.tdb2.store.nodetable.NodeTable;
 
 /**
- * A collection of helpers to abstract away from internal details of TDB. Use with care.
+ * A collection of helpers to abstract away from calling code knowing the 
+ * internal details of TDB. 
+ * <p>
+ * Use with care.
+ * <p>{@link DatabaseOps#compact Compaction} invalidates any previous objects.
+ * 
  */
 public class TDBInternal {
     /**
@@ -88,17 +96,59 @@ public class TDBInternal {
 
     /**
      * Return the DatasetGraphTDB for a Dataset, or null.
+     * Use the {@link DatasetGraphTDB} with care.
      */
     public static DatasetGraphTDB getDatasetGraphTDB(Dataset ds) {
         return getDatasetGraphTDB(ds.asDatasetGraph());
     }
 
     /**
-     * Return the DatasetGraphTDB for a DatasetGraph, or null. May not be up-to-date.
+     * Return the DatasetGraphTDB for a DatasetGraph, or null.
+     * Use the {@link DatasetGraphTDB} with care.
      */
     public static DatasetGraphTDB getDatasetGraphTDB(DatasetGraph dsg) {
+        return unwrap(dsg);
+    }
+    
+    /**
+     * Return the DatasetGraphTDB for a DatasetGraph, or throw an exception.
+     */
+    public static DatasetGraphTDB requireStorage(DatasetGraph dsg) {
+        DatasetGraphTDB dsgtdb = unwrap(dsg);
+        if ( dsgtdb == null )
+            throw new TDBException("Not a TDB database (argument is neither a switchable nor direct TDB DatasetGraph)");
+        return dsgtdb;
+    }
+    
+    private static DatasetGraphTDB unwrap(DatasetGraph datasetGraph) {
+        DatasetGraph dsg = datasetGraph;
+        if ( dsg instanceof DatasetGraphSwitchable )
+            dsg = ((DatasetGraphSwitchable)datasetGraph).get();
         if ( dsg instanceof DatasetGraphTDB )
-            return (DatasetGraphTDB)dsg;
+            return ((DatasetGraphTDB)dsg);
         return null;
     }
+
+    /** Stop managing a location. Use with great care (testing only). */
+    public static synchronized void expel(Location location, boolean force) {
+        DatabaseConnection.internalExpel(location, force);
+        StoreConnection.internalExpel(location, force);
+    }
+    
+    /** 
+     * Reset the whole TDB system.      
+     * Use with great care.
+     */
+    public static void reset() {
+        DatabaseConnection.internalReset();
+        StoreConnection.internalReset();
+    }
+
+    public static boolean isBackedByTDB(DatasetGraph datasetGraph) {
+        if ( datasetGraph instanceof DatasetGraphSwitchable )
+            return true;
+        if ( datasetGraph instanceof DatasetGraphTDB )
+            return true;
+        return false;
+    }
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/main/java/org/seaborne/tdb2/sys/Util.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/sys/Util.java b/tdb2/src/main/java/org/seaborne/tdb2/sys/Util.java
new file mode 100644
index 0000000..3ac6336
--- /dev/null
+++ b/tdb2/src/main/java/org/seaborne/tdb2/sys/Util.java
@@ -0,0 +1,49 @@
+/*
+ * 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.seaborne.tdb2.sys;
+
+import java.util.List;
+import java.util.NoSuchElementException;
+
+/** Utility code that should go somewhere */
+public class Util {
+    
+    /** Get the last element of a list, or throw NoSuchElementException
+     * @param list
+     * @return T
+     * @throws NoSuchElementException
+     */
+    public static <T> T xgetLast(List<T> list) {
+        T elt = getLastOrNull(list);
+        if ( elt == null )
+            throw new NoSuchElementException();
+        return elt;  
+    }
+    
+    /**
+     * Get the last element of a list, or return null.
+     * @param list
+     * @return T
+     */
+    public static <T> T getLastOrNull(List<T> list) {
+        if ( list.isEmpty() )
+            return null;
+        return list.get(list.size()-1);  
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/test/java/org/seaborne/tdb2/TC_TDB.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/TC_TDB.java b/tdb2/src/test/java/org/seaborne/tdb2/TC_TDB.java
index 5c97542..8d1a695 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/TC_TDB.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/TC_TDB.java
@@ -50,10 +50,10 @@ import org.seaborne.tdb2.sys.TS_Sys ;
     , TS_TDBSetup.class
     , TS_Store.class
     , TS_SolverTDB.class
-    , TS_Sys.class
     , TS_Graph.class
     , TS_Factory.class
     , TS_TDBAssembler.class
+    , TS_Sys.class
 } )
 
 public class TC_TDB

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/test/java/org/seaborne/tdb2/TS_Factory.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/TS_Factory.java b/tdb2/src/test/java/org/seaborne/tdb2/TS_Factory.java
index d963ad3..b6dc979 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/TS_Factory.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/TS_Factory.java
@@ -26,9 +26,6 @@ import org.junit.runners.Suite;
     TestDatabaseMgr.class
     , TestTDBFactory.class
 
-    // Move to TS_Sys
-    , TestDatabaseOps.class
-    , TestDatabaseConnection.class
 })
 
 public class TS_Factory

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseConnection.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseConnection.java b/tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseConnection.java
deleted file mode 100644
index a720b11..0000000
--- a/tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseConnection.java
+++ /dev/null
@@ -1,68 +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.seaborne.tdb2;
-
-import static org.junit.Assert.assertNotSame ;
-import static org.junit.Assert.assertSame ;
-
-import org.apache.jena.atlas.lib.FileOps ;
-import org.apache.jena.sparql.core.DatasetGraph ;
-import org.junit.Test ;
-import org.seaborne.dboe.base.file.Location ;
-import org.seaborne.tdb2.sys.DatabaseConnection ;
-
-public class TestDatabaseConnection {
-    
-    @Test
-    public void testStoreConnectionTxn1() {
-        DatabaseConnection.reset() ;
-        // Only do disk things for test that need them (disk takes time!).
-        String DIRx = ConfigTest.getCleanDir() ;
-        Location DIR = Location.create(DIRx);
-        FileOps.clearDirectory(DIRx) ;
-        try {
-            DatasetGraph dg1 = DatabaseConnection.connectCreate(DIR).getDatasetGraph() ;
-            DatasetGraph dg2 = DatabaseConnection.connectCreate(DIR).getDatasetGraph() ;
-            assertSame(dg1, dg2) ;
-        }
-        finally {
-            FileOps.clearDirectory(DIRx) ;
-        }
-    }
-    
-    @Test
-    public void testStoreConnectionTxn2() {
-        // Named memory locations
-        DatabaseConnection.reset() ;
-        DatasetGraph dg1 = DatabaseConnection.connectCreate(Location.mem("FOO")).getDatasetGraph() ;
-        DatasetGraph dg2 = DatabaseConnection.connectCreate(Location.mem("FOO")).getDatasetGraph() ;
-        assertSame(dg1, dg2) ;
-    }
-    
-    @Test
-    public void testStoreConnectionTxn3() {
-        // Un-named memory locations
-        DatabaseConnection.reset() ;
-        DatasetGraph dg1 = DatabaseConnection.connectCreate(Location.mem()).getDatasetGraph() ;
-        DatasetGraph dg2 = DatabaseConnection.connectCreate(Location.mem()).getDatasetGraph() ;
-        assertNotSame(dg1, dg2) ;
-    }
-
-
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseMgr.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseMgr.java b/tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseMgr.java
index 5251b8b..552aa56 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseMgr.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseMgr.java
@@ -25,7 +25,7 @@ import org.apache.jena.sparql.sse.SSE ;
 import org.junit.Test ;
 import org.seaborne.dboe.base.file.Location ;
 import org.seaborne.dboe.jenax.Txn ;
-import org.seaborne.tdb2.sys.DatabaseConnection ;
+import org.seaborne.tdb2.sys.TDBInternal ;
 
 /** Test of DatabaseMgr - the DatasetGraph level API to TDB2 **/
 public class TestDatabaseMgr extends BaseTest
@@ -38,7 +38,7 @@ public class TestDatabaseMgr extends BaseTest
     
     @Test
     public void testDatabaseMgr1() {
-        DatabaseConnection.reset() ;
+        TDBInternal.reset() ; 
         DatasetGraph dg1 = DatabaseMgr.connectDatasetGraph(Location.mem("FOO")) ;
         DatasetGraph dg2 = DatabaseMgr.connectDatasetGraph(Location.mem("FOO")) ;
         Txn.executeWrite(dg1, ()->{
@@ -51,7 +51,7 @@ public class TestDatabaseMgr extends BaseTest
     
     @Test
     public void testDatabaseMgr2() {
-        DatabaseConnection.reset() ;
+        TDBInternal.reset() ;
         // The unnamed location is unique each time.
         DatasetGraph dg1 = DatabaseMgr.connectDatasetGraph(Location.mem()) ;
         DatasetGraph dg2 = DatabaseMgr.connectDatasetGraph(Location.mem()) ;
@@ -65,7 +65,7 @@ public class TestDatabaseMgr extends BaseTest
 
     @Test
     public void testDatabaseMgrDisk() {
-        DatabaseConnection.reset() ;
+        TDBInternal.reset() ;
         // The named disk location
         String DIRx = ConfigTest.getCleanDir() ;
         Location LOC = Location.create(DIRx) ;

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseOps.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseOps.java b/tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseOps.java
deleted file mode 100644
index 406758b..0000000
--- a/tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseOps.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- *  Licensed 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.
- *
- *  See the NOTICE file distributed with this work for additional
- *  information regarding copyright ownership.
- */
-
-package org.seaborne.tdb2;
-
-import static org.junit.Assert.* ;
-
-import org.apache.commons.io.FileUtils ;
-import org.apache.jena.atlas.lib.FileOps ;
-import org.apache.jena.riot.RDFDataMgr ;
-import org.apache.jena.sparql.core.DatasetGraph ;
-import org.apache.jena.sparql.core.Quad ;
-import org.apache.jena.sparql.sse.SSE ;
-import org.junit.After ;
-import org.junit.Before ;
-import org.junit.Test ;
-import org.seaborne.dboe.base.file.Location ;
-import org.seaborne.dboe.jenax.Txn ;
-import org.seaborne.tdb2.repack.DatasetGraphSwitchable ;
-import org.seaborne.tdb2.store.DatasetGraphTDB ;
-import org.seaborne.tdb2.sys.DatabaseConnection ;
-import org.seaborne.tdb2.sys.IOX ;
-import org.seaborne.tdb2.sys.StoreConnection ;
-
-public class TestDatabaseOps
-{
-    static String DIRx = ConfigTest.getCleanDir() ;
-    static Location DIR = Location.create(DIRx);
-    
-    static Quad quad1 = SSE.parseQuad("(_ _:a <p> 1)") ;
-    static Quad quad2 = SSE.parseQuad("(_ <s> <p> 1)") ;
-    
-    @Before
-    public void before() {
-        FileUtils.deleteQuietly(IOX.asFile(DIR));
-        FileOps.ensureDir(DIR.getDirectoryPath());
-    }
-
-    @After  
-    public void after() {
-        DatabaseConnection.reset();
-        FileUtils.deleteQuietly(IOX.asFile(DIR));
-    }
-
-    @Test public void compact_1() {
-        DatasetGraph dsg = DatabaseMgr.connectDatasetGraph(DIR);
-        DatasetGraphSwitchable dsgs = (DatasetGraphSwitchable)dsg;
-        DatasetGraph dsg1 = dsgs.get();
-        Location loc1 = ((DatasetGraphTDB)dsg1).getLocation();
-        
-        Txn.executeWrite(dsg, ()-> {
-            dsg.add(quad1) ;
-            dsg.add(quad2) ;
-        }) ;
-        DatabaseMgr.compact(dsg);
-        DatasetGraph dsg2 = dsgs.get();
-        Location loc2 = ((DatasetGraphTDB)dsg2).getLocation();
-
-        assertNotEquals(dsg1, dsg2);
-        assertNotEquals(loc1, loc2);
-
-        Txn.executeRead(dsg, ()-> {
-            assertTrue(dsg.contains(quad1)) ;
-            assertTrue(dsg.contains(quad2)) ;
-        }) ;
-        
-        // Naughty!
-        // dsg1 was closed and expelled. We must reopen its storage only.
-        DatasetGraph dsgOld = StoreConnection.connectCreate(loc1).getDatasetGraph();
-        
-        Txn.executeWrite(dsgOld, ()->dsgOld.delete(quad1));
-
-        Txn.executeWrite(dsg, ()->assertTrue(dsg.contains(quad1)) );
-
-        Txn.executeWrite(dsg2, ()->assertTrue(dsg2.contains(quad1)) ) ;
-        
-        StoreConnection.expel(loc1, true);
-    }
-
-//    @Test public void compact_2() {
-//    }
-
-    @Test public void backup_1() {
-        DatasetGraph dsg = DatabaseMgr.connectDatasetGraph(DIR);
-        Txn.executeWrite(dsg, ()-> {
-            dsg.add(quad1) ;
-            dsg.add(quad2) ;
-        }) ;
-        String file1 = DatabaseMgr.backup(dsg);
-        DatasetGraph dsg2 = RDFDataMgr.loadDatasetGraph(file1);
-        Txn.executeRead(dsg, ()-> {
-            assertTrue(dsg.contains(quad2)) ;
-            assertEquals(2, dsg.getDefaultGraph().size());
-            assertTrue(dsg2.getDefaultGraph().isIsomorphicWith(dsg.getDefaultGraph()));
-        }) ;
-        String file2 = DatabaseMgr.backup(dsg);
-        assertNotEquals(file1, file2);
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/test/java/org/seaborne/tdb2/TestTDBFactory.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/TestTDBFactory.java b/tdb2/src/test/java/org/seaborne/tdb2/TestTDBFactory.java
index 7d1c780..795c871 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/TestTDBFactory.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/TestTDBFactory.java
@@ -28,7 +28,7 @@ import org.apache.jena.rdf.model.ResourceFactory ;
 import org.junit.Test ;
 import org.seaborne.dboe.base.file.Location ;
 import org.seaborne.dboe.jenax.Txn ;
-import org.seaborne.tdb2.sys.DatabaseConnection ;
+import org.seaborne.tdb2.sys.TDBInternal ;
 
 /** Test of TDB2Factory - the Dataset level API to TDB2 **/
 public class TestTDBFactory extends BaseTest
@@ -41,21 +41,21 @@ public class TestTDBFactory extends BaseTest
     
     @Test
     public void testTDBFactory1() {
-        DatabaseConnection.reset() ;
+        TDBInternal.reset() ;
         Dataset ds = TDB2Factory.connectDataset(Location.mem()) ;
         test(ds);
     }
     
     @Test
     public void testTDBFactory2() {
-        DatabaseConnection.reset() ;
+        TDBInternal.reset() ;
         Dataset ds = TDB2Factory.connectDataset(Location.mem("MEMORY")) ;
         test(ds);
     }
     
     @Test
     public void testTDBFactory3() {
-        DatabaseConnection.reset() ;
+        TDBInternal.reset() ;
         // Only do disk things for tests that need them (disk takes time!).
         String DIRx = ConfigTest.getCleanDir() ;
         Location DIR = Location.create(DIRx);
@@ -68,7 +68,7 @@ public class TestTDBFactory extends BaseTest
     
     @Test
     public void testTDBFactory2DS_1() {
-        DatabaseConnection.reset() ;
+        TDBInternal.reset() ;
         Dataset ds1 = TDB2Factory.connectDataset(Location.mem("FOO")) ;
         Dataset ds2 = TDB2Factory.connectDataset(Location.mem("FOO")) ;
         Txn.executeWrite(ds1, ()->{
@@ -81,7 +81,7 @@ public class TestTDBFactory extends BaseTest
     
     @Test
     public void testTDBFactory2DS_2() {
-        DatabaseConnection.reset() ;
+        TDBInternal.reset() ;
         // The unnamed location is unique each time.
         Dataset ds1 = TDB2Factory.connectDataset(Location.mem()) ;
         Dataset ds2 = TDB2Factory.connectDataset(Location.mem()) ;
@@ -95,8 +95,8 @@ public class TestTDBFactory extends BaseTest
     
     @Test
     public void testTDBFactory2DS_3() {
-        DatabaseConnection.reset() ;
-        DatabaseConnection.reset() ;
+        TDBInternal.reset() ;
+        TDBInternal.reset() ;
         String DIRx = ConfigTest.getCleanDir() ;
         Location DIR = Location.create(DIRx);
         try {

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/test/java/org/seaborne/tdb2/assembler/TestTDBAssembler.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/assembler/TestTDBAssembler.java b/tdb2/src/test/java/org/seaborne/tdb2/assembler/TestTDBAssembler.java
index cf22821..581af56 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/assembler/TestTDBAssembler.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/assembler/TestTDBAssembler.java
@@ -32,11 +32,9 @@ import org.junit.Before ;
 import org.junit.BeforeClass ;
 import org.junit.Test ;
 import org.seaborne.tdb2.ConfigTest ;
-import org.seaborne.tdb2.assembler.VocabTDB2 ;
-import org.seaborne.tdb2.store.DatasetGraphTDB ;
-import org.seaborne.tdb2.store.DatasetGraphTxn ;
-import org.seaborne.tdb2.store.GraphTDB ;
-import org.seaborne.tdb2.sys.StoreConnection ;
+import org.seaborne.tdb2.store.DatasetGraphSwitchable ;
+import org.seaborne.tdb2.store.GraphViewSwitchable ;
+import org.seaborne.tdb2.sys.TDBInternal ;
 
 public class TestTDBAssembler extends BaseTest
 {
@@ -52,13 +50,13 @@ public class TestTDBAssembler extends BaseTest
 
     @Before
     public void before() {
-        StoreConnection.reset() ;
+        TDBInternal.reset() ;
         FileOps.clearDirectory(dirDB) ;
     }
 
     @AfterClass
     static public void afterClass() {
-        StoreConnection.reset() ;
+        TDBInternal.reset() ;
         FileOps.clearDirectory(dirDB) ;
     }
 
@@ -76,7 +74,7 @@ public class TestTDBAssembler extends BaseTest
         Object thing = AssemblerUtils.build(filename, type) ;
         assertTrue(thing instanceof Dataset) ;
         Dataset ds = (Dataset)thing ;
-        assertTrue(ds.asDatasetGraph() instanceof DatasetGraphTxn) ;
+        assertTrue(ds.asDatasetGraph() instanceof DatasetGraphSwitchable) ;
         assertTrue(ds.supportsTransactions()) ;
         ds.close() ;
     }
@@ -88,11 +86,6 @@ public class TestTDBAssembler extends BaseTest
 
     @Test
     public void createGraphEmbed() {
-        // Jena 3.1.0 needs ... remove for Jena 3.1.1 or later. 
-//        if ( true ) {
-//            System.err.println("TestTDBAssembler.createGraphEmbed disabled for Jena 3.1.0") ;
-//            return ;
-//        }
         String f = dirAssem + "/tdb-graph-embed.ttl" ;
         Object thing = null ;
         try {
@@ -105,9 +98,9 @@ public class TestTDBAssembler extends BaseTest
 
         assertTrue(thing instanceof Model) ;
         Graph graph = ((Model)thing).getGraph() ;
-        assertTrue(graph instanceof GraphTDB) ;
+        assertTrue(graph instanceof GraphViewSwitchable) ;
 
-        DatasetGraphTDB ds = ((GraphTDB)graph).getDSG() ;
+        DatasetGraphSwitchable ds = ((GraphViewSwitchable)graph).getDataset() ;
         if ( ds != null )
             ds.close() ;
     }
@@ -140,10 +133,10 @@ public class TestTDBAssembler extends BaseTest
         assertTrue(thing instanceof Model) ;
         Graph graph = ((Model)thing).getGraph() ;
 
-        assertTrue(graph instanceof GraphTDB) ;
+        assertTrue(graph instanceof GraphViewSwitchable) ;
 
-        DatasetGraphTDB ds = ((GraphTDB)graph).getDSG() ;
-        if ( ds != null )
-            ds.close() ;
+        DatasetGraphSwitchable dsg = ((GraphViewSwitchable)graph).getDataset();
+        if ( dsg != null )
+            dsg.close() ;
     }
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/test/java/org/seaborne/tdb2/graph/TestPrefixMappingTDB.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/graph/TestPrefixMappingTDB.java b/tdb2/src/test/java/org/seaborne/tdb2/graph/TestPrefixMappingTDB.java
index 27fbbb1..7deaea4 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/graph/TestPrefixMappingTDB.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/graph/TestPrefixMappingTDB.java
@@ -25,16 +25,16 @@ import org.junit.* ;
 import org.seaborne.dboe.base.file.Location ;
 import org.seaborne.tdb2.ConfigTest ;
 import org.seaborne.tdb2.junit.BuildTestLib ;
-import org.seaborne.tdb2.sys.StoreConnection ;
+import org.seaborne.tdb2.sys.TDBInternal ;
 
 public class TestPrefixMappingTDB extends AbstractTestPrefixMapping2
 {
     static DatasetPrefixStorage last = null ;
     
     @BeforeClass public static void beforeClass() {}
-    @AfterClass public static void afterClass()   { StoreConnection.reset() ; ConfigTest.deleteTestingDir() ; }
+    @AfterClass public static void afterClass()   { TDBInternal.reset() ; ConfigTest.deleteTestingDir() ; }
 
-    @Before public void before() { StoreConnection.reset() ; }
+    @Before public void before() { TDBInternal.reset() ; }
     @After public  void after()  { }
 
     
@@ -78,7 +78,6 @@ public class TestPrefixMappingTDB extends AbstractTestPrefixMapping2
     // Persistent.
     @Test
     public void persistent1() {
-        StoreConnection.reset();
         String dir = ConfigTest.getTestingDir() ;
         FileOps.clearDirectory(dir) ;
 
@@ -93,7 +92,6 @@ public class TestPrefixMappingTDB extends AbstractTestPrefixMapping2
     // Persistent.
     @Test
     public void persistent2() {
-        StoreConnection.reset();
         String dir = ConfigTest.getTestingDir() ;
         FileOps.clearDirectory(dir) ;
 

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/test/java/org/seaborne/tdb2/junit/TL.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/junit/TL.java b/tdb2/src/test/java/org/seaborne/tdb2/junit/TL.java
index cd58c36..7385354 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/junit/TL.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/junit/TL.java
@@ -25,8 +25,8 @@ import org.seaborne.dboe.base.file.Location ;
 import org.seaborne.tdb2.ConfigTest ;
 import org.seaborne.tdb2.DatabaseMgr ;
 import org.seaborne.tdb2.TDB2Factory ;
-import org.seaborne.tdb2.sys.DatabaseConnection ;
 import org.seaborne.tdb2.sys.SystemTDB ;
+import org.seaborne.tdb2.sys.TDBInternal ;
 
 /** Execute a test with a fresh dataset in a write transaction */
 public class TL {
@@ -35,14 +35,14 @@ public class TL {
         Dataset dataset = createTestDataset() ;
         action.accept(dataset);
         releaseDataset(dataset) ;
-        DatabaseConnection.reset() ;
+        TDBInternal.reset() ;
     }
     
     public static void execMem(Consumer<Dataset> action) {
         Dataset dataset = createTestDatasetMem() ;
         action.accept(dataset);
         releaseDataset(dataset);
-        DatabaseConnection.reset();
+        TDBInternal.reset();
     }
 
     // Or use these for @Before, @After style.
@@ -58,7 +58,7 @@ public class TL {
     
     public static void releaseDataset(Dataset dataset) {
         dataset.abort() ;
-        DatabaseConnection.reset() ;
+        TDBInternal.reset() ;
     }
 
     public static Dataset createTestDataset() {

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParamsCreate.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParamsCreate.java b/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParamsCreate.java
index ce811a9..fbac48e 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParamsCreate.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParamsCreate.java
@@ -31,7 +31,9 @@ import org.junit.Test ;
 import org.seaborne.dboe.base.file.Location ;
 import org.seaborne.dboe.sys.Names;
 import org.seaborne.tdb2.ConfigTest ;
+import org.seaborne.tdb2.sys.DatabaseConnection ;
 import org.seaborne.tdb2.sys.StoreConnection ;
+import org.seaborne.tdb2.sys.TDBInternal ;
 
 /**
  * This test suite uses on-disk structures, does a lot of clean/create/sync
@@ -52,12 +54,12 @@ public class TestStoreParamsCreate extends BaseTest {
     
     @Before public void clearupTest() { 
         // Flush and clean.
-        StoreConnection.expel(loc, true) ;
+        DatabaseConnection.internalExpel(loc, true);
         FileOps.clearAll(DB_DIR);
     }
 
     @After public void expelDatabase() { 
-        StoreConnection.expel(loc, true) ;
+        TDBInternal.expel(loc, true) ;
     }
     
     @Test public void params_create_01() {
@@ -81,7 +83,7 @@ public class TestStoreParamsCreate extends BaseTest {
         // Create.
         StoreConnection.connectCreate(loc) ;
         // Drop.
-        StoreConnection.expel(loc, true) ;
+        TDBInternal.expel(loc, true) ;
         // Reconnect
         StoreConnection.connectCreate(loc, null) ;
         StoreParams pLoc = StoreParamsCodec.read(loc) ;
@@ -98,7 +100,7 @@ public class TestStoreParamsCreate extends BaseTest {
         // Create.
         StoreConnection.connectCreate(loc, null) ;
         // Drop.
-        StoreConnection.expel(loc, true) ;
+        TDBInternal.expel(loc, true) ;
         // Reconnect
         StoreConnection.connectCreate(loc, pSpecial) ;
         //StoreParams pLoc = StoreParamsCodec.read(loc) ;
@@ -123,7 +125,7 @@ public class TestStoreParamsCreate extends BaseTest {
         // Create.
         StoreConnection.connectCreate(loc, pApp) ;
         // Drop.
-        StoreConnection.expel(loc, true) ;
+        TDBInternal.expel(loc, true) ;
         // Reconnect
         StoreConnection.connectCreate(loc, pSpecial) ;
         //StoreParams pLoc = StoreParamsCodec.read(loc) ;

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/test/java/org/seaborne/tdb2/solver/TestStats.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/solver/TestStats.java b/tdb2/src/test/java/org/seaborne/tdb2/solver/TestStats.java
index db63eeb..e31263b 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/solver/TestStats.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/solver/TestStats.java
@@ -32,10 +32,11 @@ import org.seaborne.tdb2.store.DatasetGraphTDB ;
 import org.seaborne.tdb2.store.NodeId ;
 import org.seaborne.tdb2.store.nodetable.NodeTable ;
 import org.seaborne.tdb2.store.nodetupletable.NodeTupleTable ;
+import org.seaborne.tdb2.sys.TDBInternal ;
 
 public class TestStats extends BaseTest
 {
-    static DatasetGraphTDB dsg      = ((DatasetGraphTDB)TL.createTestDatasetGraphMem()) ;
+    static DatasetGraphTDB dsg      = TDBInternal.getDatasetGraphTDB(TL.createTestDatasetGraphMem()) ;
     static NodeTupleTable quads     = dsg.getQuadTable().getNodeTupleTable() ;
     static NodeTupleTable triples   = dsg.getTripleTable().getNodeTupleTable() ;
     static NodeTable nt             = quads.getNodeTable() ;

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/test/java/org/seaborne/tdb2/store/AbstractTestStoreConnectionBasics.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/store/AbstractTestStoreConnectionBasics.java b/tdb2/src/test/java/org/seaborne/tdb2/store/AbstractTestStoreConnectionBasics.java
index 854402b..c23dc0e 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/store/AbstractTestStoreConnectionBasics.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/store/AbstractTestStoreConnectionBasics.java
@@ -26,8 +26,8 @@ import org.junit.Before ;
 import org.junit.Test ;
 import org.seaborne.dboe.base.file.Location ;
 import org.seaborne.dboe.jenax.Txn ;
-import org.seaborne.tdb2.sys.DatabaseConnection ;
 import org.seaborne.tdb2.sys.StoreConnection ;
+import org.seaborne.tdb2.sys.TDBInternal ;
 
 /** StoreConnection and transactions - basic wiring.
  *  These tests are slow on rotational disk.
@@ -53,11 +53,11 @@ public abstract class AbstractTestStoreConnectionBasics extends BaseTest
     
     @Before public void before()
     {
-        DatabaseConnection.reset() ;
+        TDBInternal.reset() ;
         location = getLocation() ;
     }
 
-    @After public void after() {} 
+    @After public void after() { TDBInternal.reset() ; } 
 
     @Test
     public void store_01()


[05/50] [abbrv] jena git commit: Migrate tests to DatasetGraphSwitchable

Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/test/java/org/seaborne/tdb2/store/TestDatasetTDBPersist.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/store/TestDatasetTDBPersist.java b/tdb2/src/test/java/org/seaborne/tdb2/store/TestDatasetTDBPersist.java
index 45e73a6..fe928a0 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/store/TestDatasetTDBPersist.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/store/TestDatasetTDBPersist.java
@@ -17,11 +17,14 @@
 
 package org.seaborne.tdb2.store;
 
+import static org.junit.Assert.assertEquals ;
+import static org.junit.Assert.assertFalse ;
+import static org.junit.Assert.assertTrue ;
+
 import java.util.Arrays ;
 import java.util.List ;
 
 import org.apache.jena.atlas.iterator.Iter ;
-import org.apache.jena.atlas.junit.BaseTest ;
 import org.apache.jena.graph.Graph ;
 import org.apache.jena.graph.Node ;
 import org.apache.jena.graph.Triple ;
@@ -31,7 +34,7 @@ import org.junit.Test ;
 import org.seaborne.tdb2.junit.TL ;
 
 /** Testing persistence  */ 
-public class TestDatasetTDBPersist extends BaseTest
+public class TestDatasetTDBPersist
 {
     static Node n0 = NodeFactoryExtra.parseNode("<http://example/n0>") ; 
     static Node n1 = NodeFactoryExtra.parseNode("<http://example/n1>") ;
@@ -40,8 +43,8 @@ public class TestDatasetTDBPersist extends BaseTest
     @Test
     public void dataset1() {
         TL.exec((ds) -> {
-            assertTrue(ds.getDefaultModel().getGraph() instanceof GraphTDB) ;
-            assertTrue(ds.getNamedModel("http://example/").getGraph() instanceof GraphTDB) ;
+            assertTrue(ds.getDefaultModel().getGraph() instanceof GraphViewSwitchable) ;
+            assertTrue(ds.getNamedModel("http://example/").getGraph() instanceof GraphViewSwitchable) ;
         }) ;
     }
     

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/test/java/org/seaborne/tdb2/store/TestTransactions.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/store/TestTransactions.java b/tdb2/src/test/java/org/seaborne/tdb2/store/TestTransactions.java
index bfae615..5442c79 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/store/TestTransactions.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/store/TestTransactions.java
@@ -37,7 +37,6 @@ import org.seaborne.dboe.base.file.Location ;
 import org.seaborne.dboe.jenax.Txn ;
 import org.seaborne.dboe.transaction.txn.TransactionException;
 import org.seaborne.tdb2.TDB2Factory ;
-import org.seaborne.tdb2.sys.StoreConnection ;
 import org.seaborne.tdb2.sys.TDBInternal;
 
 /** Transactions and store connections - extended tests assuming the
@@ -84,7 +83,7 @@ public class TestTransactions extends BaseTest
 
     @After public void after() {
         dataset.close() ;
-        StoreConnection.expel(location, true);
+        TDBInternal.expel(location, true);
     }
 
     // Models across transactions
@@ -135,7 +134,7 @@ public class TestTransactions extends BaseTest
         load(data2);
         
         dataset.begin(ReadWrite.READ);
-        Iterator<Quad> iter = TDBInternal.getDatasetGraphTDB(dataset).find();
+        Iterator<Quad> iter = dataset.asDatasetGraph().find();
         dataset.end();
         iter.next();
     }
@@ -169,7 +168,7 @@ public class TestTransactions extends BaseTest
         load(data2);
         
         dataset.begin(ReadWrite.READ);
-        Iterator<Quad> iter = TDBInternal.getDatasetGraphTDB(dataset).find();
+        Iterator<Quad> iter = dataset.asDatasetGraph().find();
         dataset.end();
         
         dataset.begin(ReadWrite.READ);

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/test/java/org/seaborne/tdb2/sys/TS_Sys.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/sys/TS_Sys.java b/tdb2/src/test/java/org/seaborne/tdb2/sys/TS_Sys.java
index 2c93a96..7a37acc 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/sys/TS_Sys.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/sys/TS_Sys.java
@@ -20,14 +20,14 @@ package org.seaborne.tdb2.sys;
 
 import org.junit.runner.RunWith;
 import org.junit.runners.Suite;
-import org.seaborne.tdb2.sys.TestSys ;
 
 @RunWith(Suite.class)
 @Suite.SuiteClasses( {
-    TestSys.class
+    TestDatabaseOps.class
+    , TestDatabaseConnection.class
+    , TestSys.class
+    //, TestOps.class
 })
 
 public class TS_Sys
-{
-
-}
+{}

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/test/java/org/seaborne/tdb2/sys/TestDatabaseConnection.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/sys/TestDatabaseConnection.java b/tdb2/src/test/java/org/seaborne/tdb2/sys/TestDatabaseConnection.java
new file mode 100644
index 0000000..8e6bdcc
--- /dev/null
+++ b/tdb2/src/test/java/org/seaborne/tdb2/sys/TestDatabaseConnection.java
@@ -0,0 +1,69 @@
+/*
+ * 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.seaborne.tdb2.sys;
+
+import static org.junit.Assert.assertNotSame ;
+import static org.junit.Assert.assertSame ;
+
+import org.apache.jena.atlas.lib.FileOps ;
+import org.apache.jena.sparql.core.DatasetGraph ;
+import org.junit.Test ;
+import org.seaborne.dboe.base.file.Location ;
+import org.seaborne.tdb2.ConfigTest ;
+import org.seaborne.tdb2.sys.DatabaseConnection ;
+
+public class TestDatabaseConnection {
+    
+    @Test
+    public void testStoreConnectionTxn1() {
+        TDBInternal.reset() ;
+        // Only do disk things for test that need them (disk takes time!).
+        String DIRx = ConfigTest.getCleanDir() ;
+        Location DIR = Location.create(DIRx);
+        FileOps.clearDirectory(DIRx) ;
+        try {
+            DatasetGraph dg1 = DatabaseConnection.connectCreate(DIR).getDatasetGraph() ;
+            DatasetGraph dg2 = DatabaseConnection.connectCreate(DIR).getDatasetGraph() ;
+            assertSame(dg1, dg2) ;
+        }
+        finally {
+            FileOps.clearDirectory(DIRx) ;
+        }
+    }
+    
+    @Test
+    public void testStoreConnectionTxn2() {
+        // Named memory locations
+        TDBInternal.reset() ;
+        DatasetGraph dg1 = DatabaseConnection.connectCreate(Location.mem("FOO")).getDatasetGraph() ;
+        DatasetGraph dg2 = DatabaseConnection.connectCreate(Location.mem("FOO")).getDatasetGraph() ;
+        assertSame(dg1, dg2) ;
+    }
+    
+    @Test
+    public void testStoreConnectionTxn3() {
+        // Un-named memory locations
+        TDBInternal.reset() ;
+        DatasetGraph dg1 = DatabaseConnection.connectCreate(Location.mem()).getDatasetGraph() ;
+        DatasetGraph dg2 = DatabaseConnection.connectCreate(Location.mem()).getDatasetGraph() ;
+        assertNotSame(dg1, dg2) ;
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/218d10c5/tdb2/src/test/java/org/seaborne/tdb2/sys/TestDatabaseOps.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/sys/TestDatabaseOps.java b/tdb2/src/test/java/org/seaborne/tdb2/sys/TestDatabaseOps.java
new file mode 100644
index 0000000..67883a4
--- /dev/null
+++ b/tdb2/src/test/java/org/seaborne/tdb2/sys/TestDatabaseOps.java
@@ -0,0 +1,117 @@
+/*
+ *  Licensed 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.
+ *
+ *  See the NOTICE file distributed with this work for additional
+ *  information regarding copyright ownership.
+ */
+
+package org.seaborne.tdb2.sys;
+
+import static org.junit.Assert.assertEquals ;
+import static org.junit.Assert.assertFalse ;
+import static org.junit.Assert.assertNotEquals ;
+import static org.junit.Assert.assertTrue ;
+
+import org.apache.commons.io.FileUtils ;
+import org.apache.jena.atlas.lib.FileOps ;
+import org.apache.jena.riot.RDFDataMgr ;
+import org.apache.jena.sparql.core.DatasetGraph ;
+import org.apache.jena.sparql.core.Quad ;
+import org.apache.jena.sparql.sse.SSE ;
+import org.junit.After ;
+import org.junit.Before ;
+import org.junit.Test ;
+import org.seaborne.dboe.base.file.Location ;
+import org.seaborne.dboe.jenax.Txn ;
+import org.seaborne.tdb2.ConfigTest ;
+import org.seaborne.tdb2.DatabaseMgr ;
+import org.seaborne.tdb2.store.DatasetGraphSwitchable ;
+import org.seaborne.tdb2.store.DatasetGraphTDB ;
+
+public class TestDatabaseOps
+{
+    static String DIRx = ConfigTest.getCleanDir() ;
+    static Location DIR = Location.create(DIRx);
+    
+    static Quad quad1 = SSE.parseQuad("(_ _:a <p> 1)") ;
+    static Quad quad2 = SSE.parseQuad("(_ <s> <p> 1)") ;
+    
+    @Before
+    public void before() {
+        FileUtils.deleteQuietly(IOX.asFile(DIR));
+        FileOps.ensureDir(DIR.getDirectoryPath());
+    }
+
+    @After  
+    public void after() {
+        TDBInternal.reset();
+        FileUtils.deleteQuietly(IOX.asFile(DIR));
+    }
+
+    @Test public void compact_1() {
+        DatasetGraph dsg = DatabaseMgr.connectDatasetGraph(DIR);
+        DatasetGraphSwitchable dsgs = (DatasetGraphSwitchable)dsg;
+        DatasetGraph dsg1 = dsgs.get();
+        Location loc1 = ((DatasetGraphTDB)dsg1).getLocation();
+        
+        Txn.executeWrite(dsg, ()-> {
+            dsg.add(quad1) ;
+            dsg.add(quad2) ;
+        }) ;
+        DatabaseMgr.compact(dsg);
+        
+        assertFalse(StoreConnection.isSetup(loc1));
+
+        DatasetGraph dsg2 = dsgs.get();
+        Location loc2 = ((DatasetGraphTDB)dsg2).getLocation();
+
+        assertNotEquals(dsg1, dsg2);
+        assertNotEquals(loc1, loc2);
+
+        Txn.executeRead(dsg, ()-> {
+            assertTrue(dsg.contains(quad1)) ;
+            assertTrue(dsg.contains(quad2)) ;
+        }) ;
+        
+        // dsg1 was closed and expelled. We must carefully reopen its storage only.
+        
+        DatasetGraph dsgOld = StoreConnection.connectCreate(loc1).getDatasetGraph();
+        
+        Txn.executeWrite(dsgOld, ()->dsgOld.delete(quad1));
+        Txn.executeRead(dsg,     ()->assertTrue(dsg.contains(quad1)) );
+        Txn.executeRead(dsg2,    ()->assertTrue(dsg2.contains(quad1)) ) ;
+        
+        TDBInternal.expel(loc1, true);
+    }
+
+//    @Test public void compact_2() {
+//    }
+
+    @Test public void backup_1() {
+        DatasetGraph dsg = DatabaseMgr.connectDatasetGraph(DIR);
+        Txn.executeWrite(dsg, ()-> {
+            dsg.add(quad1) ;
+            dsg.add(quad2) ;
+        }) ;
+        String file1 = DatabaseMgr.backup(dsg);
+        DatasetGraph dsg2 = RDFDataMgr.loadDatasetGraph(file1);
+        Txn.executeRead(dsg, ()-> {
+            assertTrue(dsg.contains(quad2)) ;
+            assertEquals(2, dsg.getDefaultGraph().size());
+            assertTrue(dsg2.getDefaultGraph().isIsomorphicWith(dsg.getDefaultGraph()));
+        }) ;
+        String file2 = DatabaseMgr.backup(dsg);
+        assertNotEquals(file1, file2);
+    }
+    
+}


[26/50] [abbrv] jena git commit: JENA-1396: Merge //github/afs/mantis as subdirectory jena-db/

Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestStoreConnectionLock.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestStoreConnectionLock.java
index 0000000,0000000..ad87de7
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestStoreConnectionLock.java
@@@ -1,0 -1,0 +1,71 @@@
++/*
++ * 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.seaborne.tdb2.store;
++
++import static org.junit.Assert.*;
++import org.junit.Rule;
++import org.junit.Test;
++import org.junit.rules.TemporaryFolder;
++import org.seaborne.dboe.base.file.AlreadyLocked;
++import org.seaborne.dboe.base.file.Location;
++import org.seaborne.dboe.base.file.ProcessFileLock;
++import org.seaborne.tdb2.sys.StoreConnection;
++
++/**
++ * Tests for {@link ProcessFileLock} in conjunction with {@link StoreConnection}s 
++ */
++public class TestStoreConnectionLock {
++    @Rule
++    public TemporaryFolder tempDir = new TemporaryFolder();
++    
++    @Test
++    public void lock_store_connection_01() {
++        Location dir = Location.create(tempDir.getRoot().getAbsolutePath());
++        ProcessFileLock lock = StoreConnection.lockForLocation(dir);
++        assertFalse(lock.isLockedHere());
++        
++        StoreConnection sConn = StoreConnection.connectCreate(dir);
++        assertEquals(dir, sConn.getLocation());
++        assertEquals(lock, sConn.getLock());
++        assertTrue(lock.isLockedHere());
++        
++        StoreConnection.release(dir);
++        assertFalse(lock.isLockedHere());
++    }
++    
++    @Test(expected=AlreadyLocked.class)
++    public void lock_store_connection_02() {
++        Location dir = Location.create(tempDir.getRoot().getAbsolutePath());
++        ProcessFileLock lock = StoreConnection.lockForLocation(dir);
++        lock.lockEx();
++        StoreConnection sConn = StoreConnection.connectCreate(dir);
++    }
++//        Location dir = Location.create(tempDir.getRoot().getAbsolutePath());
++//        // Creating a StoreConnection on the location will obtain the lock
++//         StoreConnection.make(dir);
++//        // Releasing the connection releases the lock
++//        StoreConnection.release(dir);
++//    }
++//
++//    @Test(expected = TDBException.class)
++//    public void location_lock_store_connection_02() throws IOException {
++//        Location dir = Location.create(tempDir.getRoot().getAbsolutePath());
++//        StoreConnection.make(dir);
++//    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestStoreConnectionMapped.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestStoreConnectionMapped.java
index 0000000,0000000..ac3127a
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestStoreConnectionMapped.java
@@@ -1,0 -1,0 +1,50 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store;
++
++import org.junit.AfterClass ;
++import org.junit.BeforeClass ;
++import org.seaborne.dboe.base.block.FileMode ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.tdb2.ConfigTest ;
++import org.seaborne.tdb2.sys.SystemTDB ;
++import org.seaborne.tdb2.sys.TestOps ;
++
++/** Slow tests - complete cleaning of disk areas each time */
++public class TestStoreConnectionMapped extends AbstractTestStoreConnectionBasics
++{
++    static FileMode mode ;   
++
++    @BeforeClass
++    public static void beforeClassFileMode()
++    {
++        mode = SystemTDB.fileMode() ;
++        TestOps.setFileMode(FileMode.mapped) ;
++    }
++
++    @AfterClass
++    public static void afterClassFileMode()
++    {
++        TestOps.setFileMode(mode) ;
++    }
++
++    @Override
++    protected Location getLocation() {
++        return Location.create(ConfigTest.getCleanDir()) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestStoreConnectionMem.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestStoreConnectionMem.java
index 0000000,0000000..6da2c57
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestStoreConnectionMem.java
@@@ -1,0 -1,0 +1,28 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store;
++
++import org.seaborne.dboe.base.file.Location ;
++
++public class TestStoreConnectionMem extends AbstractTestStoreConnectionBasics
++{
++    @Override
++    protected Location getLocation() {
++        return Location.mem("TestStoreConnection") ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestSuiteGraphTDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestSuiteGraphTDB.java
index 0000000,0000000..cacbb35
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestSuiteGraphTDB.java
@@@ -1,0 -1,0 +1,40 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store;
++
++import junit.framework.TestSuite ;
++
++import org.junit.runner.RunWith ;
++import org.junit.runners.AllTests ;
++import org.seaborne.tdb2.ConfigTest ;
++import org.seaborne.tdb2.junit.TestFactoryTDB ;
++
++/** Scripted test generation */
++
++@RunWith(AllTests.class)
++public class TestSuiteGraphTDB extends TestSuite
++{
++    static public TestSuite suite() { return new TestSuiteGraphTDB() ; }
++    
++    private TestSuiteGraphTDB()
++    {
++        super("TDB-Scripts") ;
++        String manifestMain = ConfigTest.getTestingDataRoot()+"/manifest.ttl" ;
++        TestFactoryTDB.make(this, manifestMain, "TDB-") ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestTransPromoteTDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestTransPromoteTDB.java
index 0000000,0000000..2814f32
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestTransPromoteTDB.java
@@@ -1,0 -1,0 +1,72 @@@
++/*
++ * 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.seaborne.tdb2.store;
++
++import org.apache.jena.sparql.core.DatasetGraph;
++import org.apache.log4j.Logger;
++import org.seaborne.dboe.transaction.txn.Transaction;
++import org.seaborne.dboe.transaction.txn.TransactionCoordinator;
++import org.seaborne.dboe.transaction.txn.TransactionException;
++import org.seaborne.tdb2.DatabaseMgr ;
++
++public class TestTransPromoteTDB extends AbstractTestTransPromoteTDB2 {
++
++    public TestTransPromoteTDB() {
++        super(getLoggers());
++    }
++
++    private static Logger[] getLoggers() {
++        return new Logger[]{ Logger.getLogger(Transaction.class) } ;
++    }
++    
++    @Override
++    protected void setPromotion(boolean b) {
++        TransactionCoordinator.promotion = b ;
++    }
++
++    @Override
++    protected boolean getPromotion() {
++        return TransactionCoordinator.promotion ;
++    }
++
++    @Override
++    protected void setReadCommitted(boolean b) {
++        TransactionCoordinator.readCommittedPromotion = b ;
++    }
++
++    @Override
++    protected boolean getReadCommitted() {
++        return TransactionCoordinator.readCommittedPromotion ;
++    }
++
++    @Override
++    protected Class<? extends Exception> getTransactionExceptionClass() {
++        return TransactionException.class;
++    }
++
++    @Override
++    protected DatasetGraph create() {
++        return DatabaseMgr.createDatasetGraph();
++    }
++
++    @Override
++    protected boolean supportsReadCommitted() {
++        return true;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestTransactionLifecycleTDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestTransactionLifecycleTDB.java
index 0000000,0000000..7f1add3
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestTransactionLifecycleTDB.java
@@@ -1,0 -1,0 +1,33 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store;
++
++import org.apache.jena.query.Dataset ;
++import org.apache.jena.sparql.transaction.AbstractTestTransactionLifecycle;
++import org.seaborne.tdb2.TDB2Factory ;
++
++/** Transaction tests - from ARQ */
++public class TestTransactionLifecycleTDB extends AbstractTestTransactionLifecycle
++{
++    @Override
++    protected Dataset create() {
++        return TDB2Factory.createDataset() ;
++    }
++}
++
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestTransactions.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestTransactions.java
index 0000000,0000000..1aed0a6
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestTransactions.java
@@@ -1,0 -1,0 +1,204 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store;
++
++import java.io.StringReader ;
++import java.util.Iterator;
++
++import org.apache.jena.atlas.iterator.Iter ;
++import static org.junit.Assert.*;
++import org.apache.jena.atlas.lib.StrUtils ;
++import org.apache.jena.query.Dataset ;
++import org.apache.jena.query.ReadWrite;
++import org.apache.jena.rdf.model.Model ;
++import org.apache.jena.rdf.model.Statement;
++import org.apache.jena.riot.Lang ;
++import org.apache.jena.riot.RDFDataMgr ;
++import org.apache.jena.sparql.core.Quad;
++import org.junit.After ;
++import org.junit.Before ;
++import org.junit.Test ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.dboe.jenax.Txn ;
++import org.seaborne.dboe.transaction.txn.TransactionException;
++import org.seaborne.tdb2.TDB2Factory ;
++import org.seaborne.tdb2.sys.TDBInternal;
++
++/** Transactions and store connections - extended tests assuming the
++ * basics work. Hence these tests use memory databases. 
++ * 
++ * For tests of StoreConnection basics:
++ * @see AbstractTestStoreConnectionBasics
++ * @see TestStoreConnectionDirect
++ * @see TestStoreConnectionMapped
++ * @see TestStoreConnectionMem
++ */
++public class TestTransactions
++{
++    // Per-test unique-ish.
++    static int count = 0 ;
++    long x = System.currentTimeMillis()+(count++) ;
++    
++    String ns = "http://example/TestTransactions#" ;
++    String data1 = StrUtils.strjoinNL
++        ("prefix : <"+ns+">"
++        ,":s :p '000-"+x+"' ."
++        ,":s :p '111-"+x+"' ."
++        ," :g {"
++        ,"    :s :p '222-"+x+"' ."
++        ,"    :s :p '333-"+x+"' ."
++        ,"    :s :p '444-"+x+"' ."
++        ,"}"
++        ) ;
++    String data2 = StrUtils.strjoinNL
++        ("prefix : <"+ns+">"
++        ,":s :p 'AAA-"+x+"' ."
++        ,":s :p 'BBB-"+x+"' ."
++        ,":s :p 'CCC-"+x+"' ."
++        ,":s :p 'DDD-"+x+"' ."
++        ) ;
++    
++    Dataset dataset ; 
++    Location location ;
++    
++    @Before public void before() {
++        location = Location.mem() ;
++        dataset = TDB2Factory.connectDataset(location) ;
++    }
++
++    @After public void after() {
++        dataset.close() ;
++        TDBInternal.expel(dataset.asDatasetGraph());
++    }
++
++    // Models across transactions
++    @Test public void trans_01() {
++        Model named = dataset.getNamedModel(ns+"g") ;
++        Txn.executeWrite(dataset, ()->{
++            RDFDataMgr.read(dataset, new StringReader(data1), null, Lang.TRIG) ;
++        }) ;
++
++        Txn.executeRead(dataset, ()->{
++            long x1 = Iter.count(dataset.getDefaultModel().listStatements()) ;
++            assertEquals(2, x1) ;
++            long x2 = Iter.count(named.listStatements()) ;
++            assertEquals(3, x2) ;
++        }) ;
++        
++    }
++    
++    @Test public void trans_02() {
++        Model model = dataset.getDefaultModel() ;
++        Txn.executeWrite(dataset, ()->{
++            RDFDataMgr.read(model, new StringReader(data2), null, Lang.TURTLE) ;
++        }) ;
++        Txn.executeRead(dataset, ()->{
++            assertEquals(4, model.size()) ;
++        }) ;        
++    }
++    
++    // Iterators and trasnaxction scope.
++    
++    private void load(String data) { 
++        Txn.executeWrite(dataset, ()->{
++            RDFDataMgr.read(dataset, new StringReader(data), null, Lang.TURTLE) ;
++        }) ;
++    }
++    
++    public void iterator_01() {
++        load(data2);
++        
++        dataset.begin(ReadWrite.READ);
++        Iterator<Quad> iter = TDBInternal.getDatasetGraphTDB(dataset).find();
++        Iter.consume(iter);
++        dataset.end();
++    }
++    
++    @Test(expected=TransactionException.class)
++    public void iterator_02() {
++        load(data2);
++        
++        dataset.begin(ReadWrite.READ);
++        Iterator<Quad> iter = dataset.asDatasetGraph().find();
++        dataset.end();
++        iter.next();
++    }
++    
++    @Test(expected=TransactionException.class)
++    public void iterator_03() {
++        load(data2);
++        
++        dataset.begin(ReadWrite.READ);
++        Iterator<Quad> iter = TDBInternal.getDatasetGraphTDB(dataset).find();
++        dataset.end();
++        iter.next();
++    }
++
++    @Test(expected=TransactionException.class)
++    public void iterator_04() {
++        load(data2);
++        Iterator<Statement> iter = Txn.calculateRead(dataset, ()->dataset.getDefaultModel().listStatements());
++        iter.next();
++    }
++    
++    @Test(expected=TransactionException.class)
++    public void iterator_05() {
++        load(data2);
++        Iterator<Statement> iter = Txn.calculateWrite(dataset, ()->dataset.getDefaultModel().listStatements());
++        iter.next();
++    }
++
++    @Test(expected=TransactionException.class)
++    public void iterator_06() {
++        load(data2);
++        
++        dataset.begin(ReadWrite.READ);
++        Iterator<Quad> iter = dataset.asDatasetGraph().find();
++        dataset.end();
++        
++        dataset.begin(ReadWrite.READ);
++        iter.next();
++        dataset.end();
++    }
++    
++//    @Test
++//    public void iterator_07() {
++//        load(data2);
++//        
++//        // Isolated by copy.
++//        Iterator<Quad> iter = TDBInternal.getDatasetGraphTDB(dataset).find();
++//        
++//        dataset.begin(ReadWrite.READ);
++//        iter.next();
++//        dataset.end();
++//    }
++//
++//    @Test
++//    public void iterator_08() {
++//        load(data2);
++//        
++//        // Isolated by copy.
++//        Iterator<Quad> iter = TDBInternal.getDatasetGraphTDB(dataset).find();
++//        
++//        dataset.begin(ReadWrite.READ);
++//        dataset.end();
++//        iter.next();
++//    }
++}
++
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestTripleTable.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestTripleTable.java
index 0000000,0000000..98e48d1
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestTripleTable.java
@@@ -1,0 -1,0 +1,132 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store;
++
++import java.util.Iterator ;
++
++import static org.junit.Assert.*;
++import org.apache.jena.graph.Node ;
++import org.apache.jena.graph.Triple ;
++import org.apache.jena.query.ReadWrite ;
++import org.apache.jena.sparql.util.NodeFactoryExtra ;
++import org.apache.log4j.Level ;
++import org.apache.log4j.Logger ;
++import org.junit.Test ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.tdb2.setup.TDBBuilder ;
++
++public class TestTripleTable
++{
++    static {
++        Logger.getLogger("org.apache.jena.tdb.info").setLevel(Level.WARN) ;
++        Logger.getLogger("org.apache.jena.tdb.exec").setLevel(Level.WARN) ;
++    }
++
++    private static void add(TripleTable table, Node s, Node p, Node o)
++    {
++        table.add(new Triple(s,p,o)) ;
++    }
++
++    private static void notMatch(TripleTable table, Node s, Node p, Node o)
++    {
++        Iterator<Triple> iter = table.find(s, p, o) ;
++        assertNotNull(iter) ;
++        assertFalse(iter.hasNext()) ;
++    }
++
++    private static void match(TripleTable table, Node s, Node p, Node o)
++    {
++        Iterator<Triple> iter = table.find(s, p, o) ;
++        assertNotNull(iter) ;
++        assertTrue(iter.hasNext()) ;
++    }
++    
++    
++    private static void contains(TripleTable table, Node s, Node p, Node o)
++    {
++        Iterator<Triple> iter = table.find(s, p, o) ;
++        assertNotNull(iter) ;
++        assertTrue(iter.hasNext()) ;
++        assertEquals(new Triple(s, p, o), iter.next()) ;
++        assertFalse(iter.hasNext()) ;
++    }
++    
++    static Node n1 = NodeFactoryExtra.parseNode("<http://example/n1>") ;
++    static Node n2 = NodeFactoryExtra.parseNode("<http://example/n2>") ;
++    static Node n3 = NodeFactoryExtra.parseNode("<http://example/n3>") ;
++    static Node n4 = NodeFactoryExtra.parseNode("<http://example/n4>") ;
++    static Node n5 = NodeFactoryExtra.parseNode("<http://example/n5>") ;
++    static Node n6 = NodeFactoryExtra.parseNode("<http://example/n6>") ;
++    
++    @Test public void createTripleTable1()
++    { 
++        TripleTable table = createTripleTableMem() ; 
++        notMatch(table, n1, n2, n3) ;
++    }
++    
++    @Test public void add1()
++    { 
++        TripleTable table = createTripleTableMem() ;
++        table.add(new Triple(n1,n2,n3)) ;
++    }
++    
++    @Test public void find1()
++    { 
++        TripleTable table = createTripleTableMem() ;
++        add(table, n1, n2, n3) ;
++        contains(table, n1, n2, n3) ;
++        notMatch(table, n1, n2, n4) ;
++    }
++
++    @Test public void find2()
++    { 
++        TripleTable table = createTripleTableMem() ;
++        add(table, n1, n2, n3) ;
++        add(table, n1, n2, n4) ;
++        contains(table, n1, n2, n3) ;
++        contains(table, n1, n2, n4) ;
++    }
++
++    @Test public void find3()
++    { 
++        TripleTable table = createTripleTableMem() ;
++        add(table, n1, n2, n3) ;
++        add(table, n4, n5, n6) ;
++        contains(table, n1, n2, n3) ;
++        contains(table, n4, n5, n6) ;
++        notMatch(table, n1, n2, n4) ;
++    }
++
++    @Test public void find4()
++    { 
++        TripleTable table = createTripleTableMem() ;
++        add(table, n1, n2, n3) ;
++        add(table, n4, n5, n6) ;
++        match(table, Node.ANY, n2, n3) ;
++        match(table, null, n2, n3) ;
++        match(table, null, null, null) ;
++    }
++    
++    private TripleTable createTripleTableMem()
++    {
++        DatasetGraphTxn dsx = TDBBuilder.build(Location.mem()) ;
++        dsx.begin(ReadWrite.WRITE);
++        DatasetGraphTDB ds = (DatasetGraphTDB)dsx ;
++        return ds.getTripleTable() ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/Test_SPARQL_TDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/Test_SPARQL_TDB.java
index 0000000,0000000..0ab4cb4
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/Test_SPARQL_TDB.java
@@@ -1,0 -1,0 +1,221 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store;
++
++import static org.junit.Assert.*;
++import org.apache.jena.atlas.lib.StrUtils ;
++import org.apache.jena.graph.Graph ;
++import org.apache.jena.graph.NodeFactory ;
++import org.apache.jena.graph.Triple ;
++import org.apache.jena.query.* ;
++import org.apache.jena.rdf.model.Model ;
++import org.apache.jena.sparql.sse.SSE ;
++import org.apache.jena.update.* ;
++import org.junit.Test ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.dboe.jenax.Txn ;
++import org.seaborne.tdb2.TDB2 ;
++import org.seaborne.tdb2.TDB2Factory ;
++
++/**
++ * Test SPARQL
++ */
++public class Test_SPARQL_TDB
++{
++    private static Dataset create() {
++        return TDB2Factory.createDataset() ;
++    }
++
++    private static Dataset create(Location location) {
++        return TDB2Factory.connectDataset(location) ;
++    }
++
++    private static String graphName = "http://example/" ;
++    private static Triple triple = SSE.parseTriple("(<x> <y> 123)") ;
++
++    // Standalone graph.
++    @Test public void sparql1()
++    {
++        // Test OpExecutor.execute(OpBGP) for a named graph used as a standalone model
++        Dataset ds = create() ;
++        add(ds, graphName, triple) ;
++        Txn.executeRead(ds, ()->{
++            Model m = ds.getNamedModel(graphName) ;
++            String qs = "SELECT * { ?s ?p ?o . }" ;
++            Query query = QueryFactory.create(qs) ;
++            QueryExecution qexec = QueryExecutionFactory.create(query, m) ;
++            ResultSet rs = qexec.execSelect() ;
++            ResultSetFormatter.consume(rs) ;
++        }) ;
++    }
++    
++    // Standalone graph.
++    @Test public void sparql2()
++    {
++        // Test OpExecutor.execute(OpFilter)for a named graph used as a standalone model
++        Dataset ds = create() ;
++        add(ds, graphName, triple) ;
++        
++        Txn.executeRead(ds, ()->{
++            Model m = ds.getNamedModel(graphName) ;
++            String qs = "SELECT * { ?s ?p ?o . FILTER ( ?o < 456 ) }" ;
++            Query query = QueryFactory.create(qs) ;
++            try(QueryExecution qexec = QueryExecutionFactory.create(query, m)) {
++                ResultSet rs = qexec.execSelect() ;
++                ResultSetFormatter.consume(rs) ;
++            }
++        }) ;
++    }
++    
++    // Requires OpDatasetNames 
++    @Test public void sparql3()
++    {
++        Dataset dataset = create() ;
++        // No triple added
++        Txn.executeRead(dataset, ()->{
++            Query query = QueryFactory.create("SELECT ?g { GRAPH ?g {} }") ;
++            QueryExecution qExec = QueryExecutionFactory.create(query, dataset) ;
++            ResultSet rs = qExec.execSelect() ;
++            int n = ResultSetFormatter.consume(rs) ;
++            assertEquals(0, n) ;
++        }) ;
++    }
++    
++    @Test public void sparql4()
++    {
++        Dataset dataset = create() ;
++        add(dataset, graphName, triple) ;
++        Txn.executeRead(dataset, ()->{
++            Query query = QueryFactory.create("SELECT ?g { GRAPH ?g {} }") ;
++            QueryExecution qExec = QueryExecutionFactory.create(query, dataset) ;
++            ResultSet rs = qExec.execSelect() ;
++            int n = ResultSetFormatter.consume(rs) ;
++            assertEquals(1, n) ;
++        }) ;
++    }
++    
++    @Test public void sparql5()
++    {
++        Dataset dataset = create() ;
++        add(dataset, graphName, triple);
++        Txn.executeRead(dataset, ()->{
++            Query query = QueryFactory.create("ASK { GRAPH <"+graphName+"> {} }") ;
++            boolean b = QueryExecutionFactory.create(query, dataset).execAsk() ;
++            assertEquals(true, b) ;
++        }) ;
++    }
++    
++    @Test public void sparql6()
++    {
++        Dataset dataset = create() ;
++        add(dataset, graphName, triple);
++        Txn.executeRead(dataset, ()->{
++            Query query = QueryFactory.create("ASK { GRAPH <http://example/x> {} }") ;
++            boolean b = QueryExecutionFactory.create(query, dataset).execAsk() ;
++            assertEquals(false, b) ;
++        }) ;
++    }
++
++    private static void add(Dataset dataset, String graphName, Triple triple) {
++        Txn.executeWrite(dataset, ()->{
++            Graph g2 = dataset.asDatasetGraph().getGraph(NodeFactory.createURI(graphName)) ;
++            g2.add(triple) ;
++        });
++    }
++    
++    // Test transactions effective.
++    
++    @Test public void sparql_txn_1()
++    {
++        Dataset dataset = create() ;
++        Txn.executeWrite(dataset, ()->{
++            update(dataset, "INSERT DATA { <x:s> <x:p> <x:o> }") ;
++        }) ;
++        // Explicit trasnaction steps.
++        dataset.begin(ReadWrite.READ) ;
++        try {
++            int n = count(dataset) ;
++            assertEquals(1, n) ;
++            n = count(dataset, "SELECT * { <x:s> <x:p> <x:o>}") ;
++            assertEquals(1, n) ;
++        } finally { dataset.end() ; }
++    }
++
++    @Test public void sparql_txn_2()
++    {
++        Dataset dataset1 = create(Location.mem("foo")) ;
++        Dataset dataset2 = create(Location.mem("foo")) ;
++        
++        Txn.executeWrite(dataset1, ()->{
++            update(dataset1, "INSERT DATA { <x:s> <x:p> <x:o> }") ;
++        }) ;
++        
++        Txn.executeRead(dataset1, ()->{
++            assertEquals(1, count(dataset1)) ;
++        }) ;
++        
++        // Same location.
++        Txn.executeRead(dataset2, ()->{
++            assertEquals(1, count(dataset2)) ;
++        }) ;
++    }
++
++    @Test public void sparql_update_unionGraph()
++    {
++        Dataset ds = TDB2Factory.createDataset() ;
++        // Update concrete default graph
++        Txn.executeWrite(ds, ()->{
++            ds.asDatasetGraph().add(SSE.parseQuad("(<g> <s> <p> 123)")) ;
++        }) ;
++        ds.getContext().setTrue(TDB2.symUnionDefaultGraph) ;
++            
++        Txn.executeWrite(ds, ()->{
++            // Update by looking in union graph
++            String us = StrUtils.strjoinNL(
++                "INSERT { GRAPH <http://example/g2> { ?s ?p 'NEW' } }",
++                "WHERE { ",
++                     "?s ?p 123",
++                " }" ) ;
++            UpdateRequest req = UpdateFactory.create(us) ;
++            UpdateAction.execute(req, ds) ;
++        }) ;
++                
++        Txn.executeRead(ds, ()->{
++            Model m = ds.getNamedModel("http://example/g2") ;
++            assertEquals("Did not find 1 statement in named graph", 1, m.size()) ;
++        }) ;
++    }
++    
++    private int count(Dataset dataset)
++    { return count(dataset, "SELECT * { ?s ?p ?o }") ; }
++    
++    private int count(Dataset dataset, String queryString)
++    
++    {
++        Query query = QueryFactory.create(queryString) ;
++        QueryExecution qExec = QueryExecutionFactory.create(query, dataset) ;
++        ResultSet rs = qExec.execSelect() ;
++        return ResultSetFormatter.consume(rs) ;
++    }
++    private void update(Dataset dataset, String string)
++    {
++        UpdateRequest req = UpdateFactory.create(string) ;
++        UpdateProcessor proc = UpdateExecutionFactory.create(req, dataset) ;
++        proc.execute() ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/nodetable/AbstractTestNodeTable.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/nodetable/AbstractTestNodeTable.java
index 0000000,0000000..158e2ff
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/nodetable/AbstractTestNodeTable.java
@@@ -1,0 -1,0 +1,66 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.nodetable;
++
++import static org.junit.Assert.assertEquals ;
++import static org.junit.Assert.assertNotEquals ;
++import static org.junit.Assert.assertNotNull ;
++
++import org.apache.jena.graph.Node ;
++import org.apache.jena.sparql.util.NodeFactoryExtra ;
++import org.junit.Test ;
++import org.seaborne.tdb2.store.NodeId ;
++
++public abstract class AbstractTestNodeTable
++{
++    protected abstract NodeTable createEmptyNodeTable() ;
++    
++    protected void testNode(String str) {
++        testNode(NodeFactoryExtra.parseNode(str)) ;
++    }
++
++    protected void testNode(Node n) {
++        NodeTable nt = createEmptyNodeTable() ;
++        writeNode(nt, n) ;
++    }
++
++    protected static void writeNode(NodeTable nt, String str) {
++        writeNode(nt, NodeFactoryExtra.parseNode(str)) ;
++    }
++    
++    protected static void writeNode(NodeTable nt, Node n) {
++        NodeId nodeId = nt.getAllocateNodeId(n) ;
++        assertNotNull(nodeId) ;
++        assertNotEquals(NodeId.NodeDoesNotExist, nodeId) ;
++        assertNotEquals(NodeId.NodeIdAny, nodeId) ;
++
++        Node n2 = nt.getNodeForNodeId(nodeId) ;
++        assertEquals(n, n2) ;
++
++        NodeId nodeId2 = nt.getNodeIdForNode(n) ;
++        assertEquals(nodeId, nodeId2) ;
++    }
++
++    @Test public void nodetable_01()    { testNode("<http://example/x>") ; }
++    @Test public void nodetable_02()    { testNode("1") ; }
++    @Test public void nodetable_03()    { testNode("_:x") ; }
++    @Test public void nodetable_04()    { testNode("'x'") ; }
++    @Test public void nodetable_05()    { testNode("'x'@en") ; }
++    @Test public void nodetable_06()    { testNode("'x'^^<http://example/dt>") ; }
++    @Test public void nodetable_07()    { testNode("'نواف'") ; }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/nodetable/TS_NodeTable.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/nodetable/TS_NodeTable.java
index 0000000,0000000..62f9388
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/nodetable/TS_NodeTable.java
@@@ -1,0 -1,0 +1,33 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.nodetable;
++
++import org.junit.runner.RunWith ;
++import org.junit.runners.Suite ;
++
++@RunWith(Suite.class)
++@Suite.SuiteClasses( {
++    TestNodeTableBase.class
++    , TestNodeTableStoredBase.class
++    , TestNodeTableStored.class
++    , TestNodeTable.class
++})
++public class TS_NodeTable
++{
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/nodetable/TestNodeTable.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/nodetable/TestNodeTable.java
index 0000000,0000000..9b7eda2
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/nodetable/TestNodeTable.java
@@@ -1,0 -1,0 +1,37 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.nodetable;
++
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.tdb2.junit.BuildTestLib ;
++import org.seaborne.tdb2.setup.StoreParams ;
++import org.seaborne.tdb2.setup.StoreParamsBuilder ;
++
++public class TestNodeTable extends AbstractTestNodeTable
++{
++    @Override
++    protected NodeTable createEmptyNodeTable()
++    {
++        StoreParams params = 
++            StoreParamsBuilder.create()
++                .nodeId2NodeCacheSize(10)
++                .node2NodeIdCacheSize(10)
++                .nodeMissCacheSize(10).build() ;
++        return BuildTestLib.makeNodeTable(Location.mem(), "test", params) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/nodetable/TestNodeTableBase.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/nodetable/TestNodeTableBase.java
index 0000000,0000000..d4bf131
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/nodetable/TestNodeTableBase.java
@@@ -1,0 -1,0 +1,39 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.nodetable;
++
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.tdb2.junit.BuildTestLib ;
++import org.seaborne.tdb2.setup.StoreParams ;
++import org.seaborne.tdb2.setup.StoreParamsBuilder ;
++
++public class TestNodeTableBase extends AbstractTestNodeTable
++{
++    @Override
++    protected NodeTable createEmptyNodeTable()
++    {
++        StoreParams params = 
++            StoreParamsBuilder.create()
++                .nodeId2NodeCacheSize(-1)
++                .node2NodeIdCacheSize(-1)
++                .nodeMissCacheSize(-1).build() ;
++
++        // No cache, no inlining.
++        return BuildTestLib.makeNodeTableBase(Location.mem(), "test", params) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/nodetable/TestNodeTableStored.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/nodetable/TestNodeTableStored.java
index 0000000,0000000..12063e0
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/nodetable/TestNodeTableStored.java
@@@ -1,0 -1,0 +1,45 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.nodetable;
++
++import org.apache.jena.atlas.lib.FileOps ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.tdb2.ConfigTest ;
++import org.seaborne.tdb2.junit.BuildTestLib ;
++import org.seaborne.tdb2.setup.StoreParams ;
++import org.seaborne.tdb2.setup.StoreParamsBuilder ;
++
++
++public class TestNodeTableStored extends AbstractTestNodeTable
++{
++    static String base = ConfigTest.getTestingDir() ;
++    static Location location = Location.create(base+"/nodetable-test") ;
++    
++    @Override
++    protected NodeTable createEmptyNodeTable()
++    {
++        FileOps.ensureDir(location.getDirectoryPath());
++        FileOps.clearDirectory(location.getDirectoryPath());
++        StoreParams params = 
++            StoreParamsBuilder.create()
++                .nodeId2NodeCacheSize(10)
++                .node2NodeIdCacheSize(10)
++                .nodeMissCacheSize(10).build() ;
++        return BuildTestLib.makeNodeTable(location, "test", params) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/nodetable/TestNodeTableStoredBase.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/nodetable/TestNodeTableStoredBase.java
index 0000000,0000000..edbc2d8
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/nodetable/TestNodeTableStoredBase.java
@@@ -1,0 -1,0 +1,46 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.nodetable;
++
++import org.apache.jena.atlas.lib.FileOps ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.tdb2.ConfigTest ;
++import org.seaborne.tdb2.junit.BuildTestLib ;
++import org.seaborne.tdb2.setup.StoreParams ;
++import org.seaborne.tdb2.setup.StoreParamsBuilder ;
++
++
++public class TestNodeTableStoredBase extends AbstractTestNodeTable
++{
++    
++    static String base = ConfigTest.getTestingDir() ;
++    static Location location = Location.create(base+"/nodetable-test") ;
++    
++    @Override
++    protected NodeTable createEmptyNodeTable()
++    {
++        FileOps.ensureDir(location.getDirectoryPath());
++        FileOps.clearDirectory(location.getDirectoryPath());
++        StoreParams params = 
++            StoreParamsBuilder.create()
++                .nodeId2NodeCacheSize(-1)
++                .node2NodeIdCacheSize(-1)
++                .nodeMissCacheSize(-1).build() ;
++        return BuildTestLib.makeNodeTableBase(location, "test", params) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/tupletable/AbstractTestTupleIndex.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/tupletable/AbstractTestTupleIndex.java
index 0000000,0000000..6a2e138
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/tupletable/AbstractTestTupleIndex.java
@@@ -1,0 -1,0 +1,264 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.tupletable;
++
++import static org.apache.jena.atlas.lib.tuple.TupleFactory.tuple ;
++
++import java.util.Iterator ;
++import java.util.Set ;
++
++import org.apache.jena.atlas.iterator.Iter ;
++import static org.junit.Assert.*;
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.junit.Test ;
++import org.seaborne.tdb2.store.NodeId ;
++import org.seaborne.tdb2.store.tupletable.TupleIndex ;
++import static org.seaborne.tdb2.store.tupletable.NData.*;
++
++/** Test TupleIndexes (general) */
++public abstract class AbstractTestTupleIndex
++{
++    protected abstract TupleIndex create(String description) ;
++    
++    protected static void add(TupleIndex index, NodeId x1, NodeId x2, NodeId x3)
++    {
++        Tuple<NodeId> tuple = tuple(x1, x2, x3) ;
++        index.add(tuple) ;
++    }
++    
++    @Test public void TupleIndex_1()
++    {
++        TupleIndex index = create("SPO") ;
++        add(index, n1, n2, n3) ;
++    }
++    
++    @Test public void TupleIndexRecordSPO_1()
++    {
++        TupleIndex index = create("SPO") ;
++        add(index, n1, n2, n3) ;
++        
++        Tuple<NodeId> tuple2 = tuple(n1, n2, n3) ;
++        Iterator<Tuple<NodeId>> iter = index.find(tuple2) ;
++        assertTrue(iter.hasNext()) ;
++        iter.next();
++        assertFalse(iter.hasNext()) ;
++    }
++ 
++    @Test public void TupleIndexRecordSPO_2()
++    {
++        TupleIndex index = create("SPO") ;
++        add(index, n1, n2, n3) ;
++        
++        Tuple<NodeId> tuple2 = tuple(n1, n2, null) ;
++        Iterator<Tuple<NodeId>> iter = index.find(tuple2) ;
++        assertTrue(iter.hasNext()) ;
++        iter.next();
++        assertFalse(iter.hasNext()) ;
++    }
++    
++    @Test public void TupleIndexRecordSPO_3()
++    {
++        TupleIndex index = create("SPO") ;
++        add(index, n1, n2, n3) ;
++        
++        Tuple<NodeId> tuple2 = tuple(n1, null, n3) ;
++        Iterator<Tuple<NodeId>> iter = index.find(tuple2) ;
++        assertTrue(iter.hasNext()) ;
++        iter.next();
++        assertFalse(iter.hasNext()) ;
++    }
++    
++    @Test public void TupleIndexRecordSPO_4()
++    {
++        TupleIndex index = create("SPO") ;
++        add(index, n1, n2, n3) ;
++        
++        Tuple<NodeId> tuple2 = tuple(n1, NodeId.NodeIdAny, NodeId.NodeIdAny) ;
++        Iterator<Tuple<NodeId>> iter = index.find(tuple2) ;
++        assertTrue(iter.hasNext()) ;
++        iter.next();
++        assertFalse(iter.hasNext()) ;
++    }
++    
++    @Test public void TupleIndexRecordSPO_5()
++    {
++        TupleIndex index = create("SPO") ;
++        add(index, n1, n2, n3) ;
++        add(index, n1, n2, n4) ;
++        
++        Tuple<NodeId> tuple2 = tuple(n1, n2, n3) ;
++        Iterator<Tuple<NodeId>> iter = index.find(tuple2) ;
++        Set<Tuple<NodeId>> x = Iter.toSet(iter) ;
++        assertEquals(1, x.size()) ;
++        assertTrue(x.contains(tuple(n1, n2, n3))) ;
++        assertFalse(x.contains(tuple(n1, n2, n4))) ;
++    }
++
++    @Test public void TupleIndexRecordSPO_6()
++    {
++        TupleIndex index = create("SPO") ;
++        add(index, n1, n2, n3) ;
++        add(index, n1, n2, n4) ;
++        
++        Tuple<NodeId> tuple2 = tuple(n1, n2, NodeId.NodeIdAny) ;
++        Iterator<Tuple<NodeId>> iter = index.find(tuple2) ;
++        Set<Tuple<NodeId>> x = Iter.toSet(iter) ;
++        assertEquals(2, x.size()) ;
++        assertTrue(x.contains(tuple(n1, n2, n3))) ;
++        assertTrue(x.contains(tuple(n1, n2, n4))) ;
++    }
++
++    @Test public void TupleIndexRecordSPO_7()
++    {
++        TupleIndex index = create("SPO") ;
++        add(index, n1, n2, n3) ;
++        add(index, n1, n2, n4) ;
++        
++        Tuple<NodeId> tuple2 = tuple(n1, NodeId.NodeIdAny, NodeId.NodeIdAny) ;
++        Iterator<Tuple<NodeId>> iter = index.find(tuple2) ;
++        Set<Tuple<NodeId>> x = Iter.toSet(iter) ;
++        assertEquals(2, x.size()) ;
++        assertTrue(x.contains(tuple(n1, n2, n3))) ;
++        assertTrue(x.contains(tuple(n1, n2, n4))) ;
++    }
++
++    @Test public void TupleIndexRecordSPO_8()
++    {
++        TupleIndex index = create("SPO") ;
++        add(index, n1, n2, n3) ;
++        add(index, n2, n3, n4) ;
++
++        {
++            Tuple<NodeId> tuple2 = tuple(n1, NodeId.NodeIdAny, NodeId.NodeIdAny) ;
++            Iterator<Tuple<NodeId>> iter = index.find(tuple2) ;
++            Set<Tuple<NodeId>> x = Iter.toSet(iter) ;
++            assertEquals(1, x.size()) ;
++            assertTrue(x.contains(tuple(n1, n2, n3))) ;
++        }
++
++        {
++            Tuple<NodeId> tuple2 = tuple(n2, NodeId.NodeIdAny, NodeId.NodeIdAny) ;
++            Iterator<Tuple<NodeId>> iter = index.find(tuple2) ;
++            Set<Tuple<NodeId>> x = Iter.toSet(iter) ;
++            assertEquals(1, x.size()) ;
++            assertTrue(x.contains(tuple(n2, n3, n4))) ;
++        }
++    }
++
++    @Test public void TupleIndexRecordPOS_1()
++    {
++        TupleIndex index = create("POS") ;
++        add(index, n1, n2, n3) ;
++        Tuple<NodeId> tuple2 = tuple(n1, n2, n3) ;
++        Iterator<Tuple<NodeId>> iter = index.find(tuple2) ;
++        assertTrue("Can't find tuple", iter.hasNext()) ;
++        iter.next();
++        assertFalse(iter.hasNext()) ;
++    }
++ 
++    @Test public void TupleIndexRecordPOS_2()
++    {
++        TupleIndex index = create("POS") ;
++        add(index, n1, n2, n3) ;
++        
++        Tuple<NodeId> tuple2 = tuple(null, n2, null) ;
++        Iterator<Tuple<NodeId>> iter = index.find(tuple2) ;
++        assertTrue("Can't find tuple",iter.hasNext()) ;
++        iter.next();
++        assertFalse(iter.hasNext()) ;
++    }
++    
++
++    @Test public void TupleIndexRecordPOS_3()
++    {
++        TupleIndex index = create("POS") ;
++        add(index, n1, n2, n3) ;
++        
++        Tuple<NodeId> tuple2 = tuple(null, n2, n3) ;
++        Iterator<Tuple<NodeId>> iter = index.find(tuple2) ;
++        assertTrue("Can't find tuple", iter.hasNext()) ;
++        iter.next();
++        assertFalse(iter.hasNext()) ;
++    }
++
++    @Test public void TupleIndexRecordFindNot_1()
++    {
++        TupleIndex index = create("SPO") ;
++        add(index, n1, n2, n3) ;
++        
++        Tuple<NodeId> tuple2 = tuple(n4, n5, n6) ;
++        Iterator<Tuple<NodeId>> iter = index.find(tuple2) ;
++        assertNotNull(iter) ;
++        assertFalse(iter.hasNext()) ;
++   }
++    
++    @Test public void TupleIndexRecordFindNot_2()
++    {
++        TupleIndex index = create("SPO") ;
++        add(index, n1, n2, n3) ;
++        
++        Tuple<NodeId> tuple2 = tuple(n1, n5, n6) ;
++        Iterator<Tuple<NodeId>> iter = index.find(tuple2) ;
++        assertFalse(iter.hasNext()) ;
++   }
++
++    @Test public void TupleIndexRecordFindNot_3()
++    {
++        TupleIndex index = create("SPO") ;
++        add(index, n1, n2, n3) ;
++        
++        Tuple<NodeId> tuple2 = tuple(n1, null, n6) ;
++        Iterator<Tuple<NodeId>> iter = index.find(tuple2) ;
++        assertFalse(iter.hasNext()) ;
++   }
++
++    @Test public void TupleIndexRecordFindNot_4()
++    {
++        TupleIndex index = create("SPO") ;
++        add(index, n1, n2, n3) ;
++        add(index, n1, n5, n6) ;
++        
++        Tuple<NodeId> tuple2 = tuple(n4, n5, n6) ;
++        Iterator<Tuple<NodeId>> iter = index.find(tuple2) ;
++        assertFalse(iter.hasNext()) ;
++   }
++    
++    @Test public void TupleIndexRecordFindNot_5()
++    {
++        TupleIndex index = create("SPO") ;
++        add(index, n1, n2, n3) ;
++        add(index, n1, n5, n6) ;
++        
++        Tuple<NodeId> tuple2 = tuple(n2, n5, n6) ;
++        Iterator<Tuple<NodeId>> iter = index.find(tuple2) ;
++        assertFalse(iter.hasNext()) ;
++   }
++
++    @Test public void TupleIndexRecordFindNot_6()
++    {
++        TupleIndex index = create("SPO") ;
++        add(index, n1, n2, n3) ;
++        add(index, n4, n5, n6) ;
++        
++        Tuple<NodeId> tuple2 = tuple(n1, null, n6) ;
++        Iterator<Tuple<NodeId>> iter = index.find(tuple2) ;
++        assertFalse(iter.hasNext()) ;
++   }
++
++    
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/tupletable/NData.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/tupletable/NData.java
index 0000000,0000000..c22451f
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/tupletable/NData.java
@@@ -1,0 -1,0 +1,37 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.tupletable;
++
++import org.apache.jena.atlas.lib.BitsLong;
++import org.seaborne.tdb2.store.NodeId;
++import org.seaborne.tdb2.store.NodeIdFactory;
++import org.seaborne.tdb2.store.NodeIdType;
++
++public class NData {
++    public static NodeId n1 = create(1) ;
++    public static NodeId n2 = create(2) ;
++    public static NodeId n3 = create(3) ;
++    public static NodeId n4 = NodeIdFactory.createValue(NodeIdType.XSD_INTEGER, 1);
++    public static NodeId n5 = NodeIdFactory.createValue(NodeIdType.XSD_INTEGER, 2);
++    // -2 as inlined.
++    public static NodeId n6 = NodeIdFactory.createValue(NodeIdType.XSD_INTEGER, BitsLong.clear(-2L, 56, 64));
++    
++    private static NodeId create(long v) {
++        return NodeIdFactory.createPtr(v);
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/tupletable/TS_TupleTable.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/tupletable/TS_TupleTable.java
index 0000000,0000000..607ffe8
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/tupletable/TS_TupleTable.java
@@@ -1,0 -1,0 +1,31 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.tupletable;
++
++import org.junit.runner.RunWith;
++import org.junit.runners.Suite;
++
++@RunWith(Suite.class)
++@Suite.SuiteClasses( {
++    TestTupleIndexRecord.class,
++    TestTupleIndexRecordDirect.class,
++    TestTupleTable.class
++} )
++
++public class TS_TupleTable
++{ }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/tupletable/TestTupleIndexRecord.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/tupletable/TestTupleIndexRecord.java
index 0000000,0000000..ce62f16
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/tupletable/TestTupleIndexRecord.java
@@@ -1,0 -1,0 +1,42 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.tupletable;
++
++import org.apache.jena.atlas.lib.tuple.TupleMap ;
++import org.seaborne.dboe.base.file.FileSet ;
++import org.seaborne.dboe.base.record.RecordFactory ;
++import org.seaborne.dboe.index.IndexParams ;
++import org.seaborne.dboe.index.RangeIndex ;
++import org.seaborne.tdb2.junit.BuildTestLib ;
++import org.seaborne.tdb2.setup.StoreParams ;
++import org.seaborne.tdb2.sys.SystemTDB ;
++
++public class TestTupleIndexRecord extends AbstractTestTupleIndex
++{
++    static RecordFactory factory = new RecordFactory(3*SystemTDB.SizeOfNodeId, 0) ;
++    
++    @Override
++    protected TupleIndexRecord create(String description)
++    {
++        IndexParams indexParams = StoreParams.getDftStoreParams() ; 
++        RangeIndex rIdx = BuildTestLib.buildRangeIndex(FileSet.mem(), factory, indexParams) ;
++        TupleMap tmap = TupleMap.create("SPO", description) ;
++        TupleIndexRecord index = new TupleIndexRecord(3, tmap, description, factory, rIdx) ;
++        return index ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/tupletable/TestTupleIndexRecordDirect.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/tupletable/TestTupleIndexRecordDirect.java
index 0000000,0000000..a48c87b
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/tupletable/TestTupleIndexRecordDirect.java
@@@ -1,0 -1,0 +1,314 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.tupletable;
++
++import static org.seaborne.tdb2.store.tupletable.NData.*;
++import static org.apache.jena.atlas.lib.tuple.TupleFactory.tuple ;
++
++import java.util.Iterator ;
++import java.util.Set ;
++
++import org.apache.jena.atlas.iterator.Iter ;
++import static org.junit.Assert.*;
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.atlas.lib.tuple.TupleMap ;
++import org.junit.Test ;
++import org.seaborne.dboe.base.file.FileSet ;
++import org.seaborne.dboe.base.record.RecordFactory ;
++import org.seaborne.dboe.index.IndexParams ;
++import org.seaborne.dboe.index.RangeIndex ;
++import org.seaborne.tdb2.junit.BuildTestLib ;
++import org.seaborne.tdb2.setup.StoreParams ;
++import org.seaborne.tdb2.store.NodeId ;
++import org.seaborne.tdb2.sys.SystemTDB ;
++
++public class TestTupleIndexRecordDirect
++{
++    static RecordFactory factory = new RecordFactory(3*SystemTDB.SizeOfNodeId, 0) ;
++    
++    static TupleIndexRecord create(String description)
++    {
++        IndexParams indexParams = StoreParams.getDftStoreParams() ; 
++        RangeIndex rIdx = BuildTestLib.buildRangeIndex(FileSet.mem(), factory, indexParams) ;
++        TupleMap tmap = TupleMap.create("SPO", description) ;
++        TupleIndexRecord index = new TupleIndexRecord(3, tmap, description, factory, rIdx) ;
++        return index ;
++    }
++    
++    static void add(TupleIndexRecord index, NodeId x1, NodeId x2, NodeId x3)
++    {
++        Tuple<NodeId> tuple = tuple(x1, x2, x3) ;
++        index.add(tuple) ;
++    }
++    
++    @Test public void TupleIndexRecord_1()
++    {
++        TupleIndexRecord index = create("SPO") ;
++        add(index, n1, n2, n3) ;
++    }
++    
++    @Test public void TupleIndexRecordSPO_1()
++    {
++        TupleIndexRecord index = create("SPO") ;
++        add(index, n1, n2, n3) ;
++        
++        Tuple<NodeId> tuple2 = tuple(n1, n2, n3) ;
++        Iterator<Tuple<NodeId>> iter = index.findByIndex(tuple2) ;
++        assertTrue(iter.hasNext()) ;
++        iter.next();
++        assertFalse(iter.hasNext()) ;
++    }
++ 
++    @Test public void TupleIndexRecordSPO_2()
++    {
++        TupleIndexRecord index = create("SPO") ;
++        add(index, n1, n2, n3) ;
++        
++        Tuple<NodeId> tuple2 = tuple(n1, n2, null) ;
++        Iterator<Tuple<NodeId>> iter = index.findByIndex(tuple2) ;
++        assertTrue(iter.hasNext()) ;
++        iter.next();
++        assertFalse(iter.hasNext()) ;
++    }
++    
++    @Test public void TupleIndexRecordSPO_3()
++    {
++        TupleIndexRecord index = create("SPO") ;
++        add(index, n1, n2, n3) ;
++        
++        Tuple<NodeId> tuple2 = tuple(n1, null, n3) ;
++        Iterator<Tuple<NodeId>> iter = index.findByIndex(tuple2) ;
++        assertNull(iter) ;
++        iter = index.findOrPartialScan(tuple2) ;
++        assertTrue(iter.hasNext()) ;
++        iter.next();
++        assertFalse(iter.hasNext()) ;
++    }
++    
++    @Test public void TupleIndexRecordSPO_4()
++    {
++        TupleIndexRecord index = create("SPO") ;
++        add(index, n1, n2, n3) ;
++        
++        Tuple<NodeId> tuple2 = tuple(n1, NodeId.NodeIdAny, NodeId.NodeIdAny) ;
++        Iterator<Tuple<NodeId>> iter = index.findByIndex(tuple2) ;
++        assertTrue(iter.hasNext()) ;
++        iter.next();
++        assertFalse(iter.hasNext()) ;
++    }
++    
++    @Test public void TupleIndexRecordSPO_5()
++    {
++        TupleIndexRecord index = create("SPO") ;
++        add(index, n1, n2, n3) ;
++        add(index, n1, n2, n4) ;
++        
++        Tuple<NodeId> tuple2 = tuple(n1, n2, n3) ;
++        Iterator<Tuple<NodeId>> iter = index.findByIndex(tuple2) ;
++        Set<Tuple<NodeId>> x = Iter.toSet(iter) ;
++        assertEquals(1, x.size()) ;
++        assertTrue(x.contains(tuple(n1, n2, n3))) ;
++        assertFalse(x.contains(tuple(n1, n2, n4))) ;
++    }
++
++    @Test public void TupleIndexRecordSPO_6()
++    {
++        TupleIndexRecord index = create("SPO") ;
++        add(index, n1, n2, n3) ;
++        add(index, n1, n2, n4) ;
++        
++        Tuple<NodeId> tuple2 = tuple(n1, n2, NodeId.NodeIdAny) ;
++        Iterator<Tuple<NodeId>> iter = index.findByIndex(tuple2) ;
++        Set<Tuple<NodeId>> x = Iter.toSet(iter) ;
++        assertEquals(2, x.size()) ;
++        assertTrue(x.contains(tuple(n1, n2, n3))) ;
++        assertTrue(x.contains(tuple(n1, n2, n4))) ;
++    }
++
++    @Test public void TupleIndexRecordSPO_7()
++    {
++        TupleIndexRecord index = create("SPO") ;
++        add(index, n1, n2, n3) ;
++        add(index, n1, n2, n4) ;
++        
++        Tuple<NodeId> tuple2 = tuple(n1, NodeId.NodeIdAny, NodeId.NodeIdAny) ;
++        Iterator<Tuple<NodeId>> iter = index.findByIndex(tuple2) ;
++        Set<Tuple<NodeId>> x = Iter.toSet(iter) ;
++        assertEquals(2, x.size()) ;
++        assertTrue(x.contains(tuple(n1, n2, n3))) ;
++        assertTrue(x.contains(tuple(n1, n2, n4))) ;
++    }
++
++    @Test public void TupleIndexRecordSPO_8()
++    {
++        TupleIndexRecord index = create("SPO") ;
++        add(index, n1, n2, n3) ;
++        add(index, n2, n3, n4) ;
++
++        {
++            Tuple<NodeId> tuple2 = tuple(n1, NodeId.NodeIdAny, NodeId.NodeIdAny) ;
++            Iterator<Tuple<NodeId>> iter = index.findByIndex(tuple2) ;
++            Set<Tuple<NodeId>> x = Iter.toSet(iter) ;
++            assertEquals(1, x.size()) ;
++            assertTrue(x.contains(tuple(n1, n2, n3))) ;
++        }
++
++        {
++            Tuple<NodeId> tuple2 = tuple(n2, NodeId.NodeIdAny, NodeId.NodeIdAny) ;
++            Iterator<Tuple<NodeId>> iter = index.findByIndex(tuple2) ;
++            Set<Tuple<NodeId>> x = Iter.toSet(iter) ;
++            assertEquals(1, x.size()) ;
++            assertTrue(x.contains(tuple(n2, n3, n4))) ;
++        }
++    }
++
++    @Test public void TupleIndexRecordPOS_1()
++    {
++        TupleIndexRecord index = create("POS") ;
++        add(index, n1, n2, n3) ;
++        Tuple<NodeId> tuple2 = tuple(n1, n2, n3) ;
++        Iterator<Tuple<NodeId>> iter = index.findByIndex(tuple2) ;
++        assertTrue("Can't find tuple", iter.hasNext()) ;
++        iter.next();
++        assertFalse(iter.hasNext()) ;
++    }
++ 
++    @Test public void TupleIndexRecordPOS_2()
++    {
++        TupleIndexRecord index = create("POS") ;
++        add(index, n1, n2, n3) ;
++        
++        Tuple<NodeId> tuple2 = tuple(null, n2, null) ;
++        Iterator<Tuple<NodeId>> iter = index.findByIndex(tuple2) ;
++        assertTrue("Can't find tuple",iter.hasNext()) ;
++        iter.next();
++        assertFalse(iter.hasNext()) ;
++    }
++    
++
++    @Test public void TupleIndexRecordPOS_3()
++    {
++        TupleIndexRecord index = create("POS") ;
++        add(index, n1, n2, n3) ;
++        
++        Tuple<NodeId> tuple2 = tuple(null, n2, n3) ;
++        Iterator<Tuple<NodeId>> iter = index.findByIndex(tuple2) ;
++        assertTrue("Can't find tuple", iter.hasNext()) ;
++        iter.next();
++        assertFalse(iter.hasNext()) ;
++    }
++
++    @Test public void TupleIndexRecordFindScan_1()
++    {
++        TupleIndexRecord index = create("SPO") ;
++        add(index, n1, n2, n3) ;
++        Tuple<NodeId> tuple2 = tuple(n1, null, n3) ;
++        Iterator<Tuple<NodeId>> iter = index.findByIndex(tuple2) ;
++        assertNull(iter) ;
++        iter = index.findOrPartialScan(tuple2) ;
++        assertTrue(iter.hasNext()) ;
++        iter.next();
++        assertFalse(iter.hasNext()) ;
++    }
++    
++    @Test public void TupleIndexRecordFindScan_2()
++    {
++        TupleIndexRecord index = create("SPO") ;
++        add(index, n1, n2, n3) ;
++        add(index, n1, n2, n4) ;
++        
++        
++        Tuple<NodeId> tuple2 = tuple(null, null, n3) ;
++        Iterator<Tuple<NodeId>> iter = index.findByIndex(tuple2) ;
++        assertNull(iter) ;
++        
++        iter = index.findOrPartialScan(tuple2) ;
++        assertNull(iter) ;
++        
++        iter = index.findOrScan(tuple2) ;
++        assertTrue(iter.hasNext()) ;
++        iter.next();
++        assertFalse(iter.hasNext()) ;
++    }
++    
++    @Test public void TupleIndexRecordFindNot_1()
++    {
++        TupleIndexRecord index = create("SPO") ;
++        add(index, n1, n2, n3) ;
++        
++        Tuple<NodeId> tuple2 = tuple(n4, n5, n6) ;
++        Iterator<Tuple<NodeId>> iter = index.findByIndex(tuple2) ;
++        assertNotNull(iter) ;
++        assertFalse(iter.hasNext()) ;
++   }
++    
++    @Test public void TupleIndexRecordFindNot_2()
++    {
++        TupleIndexRecord index = create("SPO") ;
++        add(index, n1, n2, n3) ;
++        
++        Tuple<NodeId> tuple2 = tuple(n1, n5, n6) ;
++        Iterator<Tuple<NodeId>> iter = index.findByIndex(tuple2) ;
++        assertFalse(iter.hasNext()) ;
++   }
++
++    @Test public void TupleIndexRecordFindNot_3()
++    {
++        TupleIndexRecord index = create("SPO") ;
++        add(index, n1, n2, n3) ;
++        
++        Tuple<NodeId> tuple2 = tuple(n1, null, n6) ;
++        Iterator<Tuple<NodeId>> iter = index.findOrPartialScan(tuple2) ;
++        assertFalse(iter.hasNext()) ;
++   }
++
++    @Test public void TupleIndexRecordFindNot_4()
++    {
++        TupleIndexRecord index = create("SPO") ;
++        add(index, n1, n2, n3) ;
++        add(index, n1, n5, n6) ;
++        
++        Tuple<NodeId> tuple2 = tuple(n4, n5, n6) ;
++        Iterator<Tuple<NodeId>> iter = index.findByIndex(tuple2) ;
++        assertFalse(iter.hasNext()) ;
++   }
++    
++    @Test public void TupleIndexRecordFindNot_5()
++    {
++        TupleIndexRecord index = create("SPO") ;
++        add(index, n1, n2, n3) ;
++        add(index, n1, n5, n6) ;
++        
++        Tuple<NodeId> tuple2 = tuple(n2, n5, n6) ;
++        Iterator<Tuple<NodeId>> iter = index.findByIndex(tuple2) ;
++        assertFalse(iter.hasNext()) ;
++   }
++
++    @Test public void TupleIndexRecordFindNot_6()
++    {
++        TupleIndexRecord index = create("SPO") ;
++        add(index, n1, n2, n3) ;
++        add(index, n4, n5, n6) ;
++        
++        Tuple<NodeId> tuple2 = tuple(n1, null, n6) ;
++        Iterator<Tuple<NodeId>> iter = index.findOrPartialScan(tuple2) ;
++        assertFalse(iter.hasNext()) ;
++   }
++
++    
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/tupletable/TestTupleTable.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/tupletable/TestTupleTable.java
index 0000000,0000000..4d27266
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/tupletable/TestTupleTable.java
@@@ -1,0 -1,0 +1,151 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.tupletable;
++
++import static org.apache.jena.atlas.lib.tuple.TupleFactory.tuple ;
++
++import java.util.Iterator ;
++import java.util.List ;
++
++import org.apache.jena.atlas.iterator.Iter ;
++import static org.junit.Assert.*;
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.junit.Test ;
++import org.seaborne.dboe.base.record.RecordFactory ;
++import org.seaborne.tdb2.store.NodeId ;
++import org.seaborne.tdb2.sys.SystemTDB ;
++import static org.seaborne.tdb2.store.tupletable.NData.*;
++
++public class TestTupleTable
++{
++    static RecordFactory factory = new RecordFactory(3*SystemTDB.SizeOfNodeId, 0) ;
++    
++    static private TupleTable create()
++    {
++        TupleIndex idxSPO = TestTupleIndexRecordDirect.create("SPO") ;
++        TupleIndex idxPOS = TestTupleIndexRecordDirect.create("POS") ;
++        TupleIndex idxOSP = TestTupleIndexRecordDirect.create("OSP") ;
++        TupleIndex x[] = { idxSPO, idxPOS, idxOSP } ;
++        TupleTable table = new TupleTable(3, x) ;
++        return table ;
++    }
++    
++    static private TupleTable create2()
++    {
++        TupleIndex idxSPO = TestTupleIndexRecordDirect.create("SPO") ;
++        TupleIndex x[] = { idxSPO } ;
++        TupleTable table = new TupleTable(3, x) ;
++        return table ;
++    }
++    
++    static void add(TupleTable table, NodeId x1, NodeId x2, NodeId x3)
++    {
++        Tuple<NodeId> tuple = tuple(x1, x2, x3) ;
++        table.add(tuple) ;
++    }
++    
++    @Test public void create1() { create() ; } 
++    
++    @Test public void createFind1()
++    { 
++        TupleTable table = create() ;
++        add(table, n1, n2, n3) ;
++        // Cast removes compile lint warning.
++        Tuple<NodeId> pat = tuple((NodeId)null, null, null) ;
++        Iterator<Tuple<NodeId>> iter = table.find(pat) ;
++        List<Tuple<NodeId>> x = Iter.toList(iter) ;
++        int z = x.size() ;
++        assertEquals(1, z) ;
++        Tuple<NodeId> e1 = x.get(0) ;
++        assertEquals(tuple(n1, n2, n3) , e1) ;
++    }
++    
++    @Test public void createFind2()
++    { 
++        TupleTable table = create() ;
++        add(table, n1, n2, n3) ;
++        add(table, n1, n2, n4) ;
++
++        Tuple<NodeId> pat = tuple(null, n2, null) ;
++        Iterator<Tuple<NodeId>> iter = table.find(pat) ;
++        assertNotNull(iter) ;
++        List<Tuple<NodeId>> x = Iter.toList(iter) ;
++        int z = x.size() ;
++        assertEquals(2, z) ;
++        
++        Tuple<NodeId> e1 = x.get(0) ;
++        Tuple<NodeId> e2 = x.get(1) ;
++        assertEquals(tuple(n1, n2, n3) , e1) ;
++        assertEquals(tuple(n1, n2, n4) , e2) ;
++    }
++    
++    @Test public void createFind3()
++    { 
++        // test scan
++        TupleTable table = create2() ;
++        add(table, n1, n2, n3) ;
++        add(table, n1, n2, n4) ;
++
++        Tuple<NodeId> pat = tuple(n1, null, n3) ;
++        Iterator<Tuple<NodeId>> iter = table.find(pat) ;
++        assertNotNull(iter) ;
++        List<Tuple<NodeId>> x = Iter.toList(iter) ;
++        int z = x.size() ;
++        assertEquals(1, z) ;
++        
++        Tuple<NodeId> e1 = x.get(0) ;
++        assertEquals(tuple(n1, n2, n3) , e1) ;
++    }
++    
++    @Test public void createFind4()
++    { 
++        // test scan
++        TupleTable table = create2() ;
++        add(table, n1, n2, n3) ;
++        add(table, n1, n2, n4) ;
++
++        Tuple<NodeId> pat = tuple(null, null, n3) ;
++        Iterator<Tuple<NodeId>> iter = table.find(pat) ;
++        assertNotNull(iter) ;
++        List<Tuple<NodeId>> x = Iter.toList(iter) ;
++        int z = x.size() ;
++        assertEquals(1, z) ;
++        
++        Tuple<NodeId> e1 = x.get(0) ;
++        assertEquals(tuple(n1, n2, n3) , e1) ;
++    }
++    
++    @Test public void createFind5()
++    { 
++        // test scan
++        TupleTable table = create2() ;
++        add(table, n1, n2, n3) ;
++        add(table, n1, n2, n4) ;
++
++        Tuple<NodeId> pat = tuple(null, NodeId.NodeIdAny, n3) ;
++        Iterator<Tuple<NodeId>> iter = table.find(pat) ;
++        assertNotNull(iter) ;
++        List<Tuple<NodeId>> x = Iter.toList(iter) ;
++        int z = x.size() ;
++        assertEquals(1, z) ;
++        
++        Tuple<NodeId> e1 = x.get(0) ;
++        assertEquals(tuple(n1, n2, n3) , e1) ;
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/value/TestDoubleNode62.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/value/TestDoubleNode62.java
index 0000000,0000000..112bd52
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/value/TestDoubleNode62.java
@@@ -1,0 -1,0 +1,123 @@@
++/*
++ * 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.seaborne.tdb2.store.value;
++
++import org.apache.jena.atlas.lib.BitsLong;
++import org.junit.Test;
++import org.seaborne.tdb2.store.value.DoubleNode62;
++
++import static org.junit.Assert.*;
++
++public class TestDoubleNode62 {
++    @Test public void double_01() { testRoundTripDouble(1d); }
++    @Test public void double_02() { testRoundTripDouble(-1d); }
++    @Test public void double_03() { testRoundTripDouble(-1111111111e50d); }
++    @Test public void double_04() { testRoundTripDouble(1111111111e50d); }
++
++    @Test public void double_05() { testNoEncoding(1e300); }
++    @Test public void double_06() { testNoEncoding(1e100); }
++    @Test public void double_07() { testNoEncoding(1e78); }
++    @Test public void double_08() { testNoEncoding(2e77); }
++    @Test public void double_09() { testRoundTripDouble(1e77); }
++    
++    @Test public void double_10() { testNoEncoding(3e-300); }
++    @Test public void double_11() { testNoEncoding(3e-100); }
++    @Test public void double_12() { testNoEncoding(3e-77); }
++    @Test public void double_13() { testRoundTripDouble(3.5e-77); }
++    @Test public void double_14() { testRoundTripDouble(4e-77); }
++    @Test public void double_15() { testRoundTripDouble(1e-76); }
++    @Test public void double_16() { testRoundTripDouble(1e-75); }
++    
++    @Test public void double_20() { testRoundTripDouble(Double.POSITIVE_INFINITY); }
++    @Test public void double_21() { testRoundTripDouble(Double.NEGATIVE_INFINITY); }
++    @Test public void double_22() { testRoundTripDouble(Double.NaN); }
++    @Test public void double_23() { testNoEncoding(Double.MAX_VALUE); }
++    @Test public void double_24() { testNoEncoding(Double.MIN_NORMAL); }
++    @Test public void double_25() { testNoEncoding(Double.MIN_VALUE); }
++
++    @Test public void double_30() { testRoundTripDouble(DoubleNode62.POSITIVE_INFINITY); }
++    @Test public void double_31() { testRoundTripDouble(DoubleNode62.NEGATIVE_INFINITY); }
++    @Test public void double_32() { testRoundTripDouble(DoubleNode62.NaN); }
++    @Test public void double_33() { testNoEncoding(DoubleNode62.MAX_VALUE); }
++    @Test public void double_34() { testNoEncoding(DoubleNode62.MIN_NORMAL); }
++    @Test public void double_35() { testNoEncoding(DoubleNode62.MIN_VALUE); }
++
++    @Test public void double_40() { sameValue(DoubleNode62.POSITIVE_INFINITY, Double.POSITIVE_INFINITY); }
++    @Test public void double_41() { sameValue(DoubleNode62.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY); }
++    @Test public void double_42() { sameValue(DoubleNode62.NaN, Double.NaN); }
++
++    @Test public void double_50() { testConst(DoubleNode62.POSITIVE_INFINITY_BITS, 0x1ff0000000000000L); }
++    @Test public void double_51() { testConst(DoubleNode62.NEGATIVE_INFINITY_BITS, 0x3ff0000000000000L); }
++    @Test public void double_52() { testConst(DoubleNode62.NaN_BITS, 0x1ff8000000000000L); }
++    @Test public void double_53() { testConst(DoubleNode62.MAX_VALUE_BITS,  0x3fefffffffffffffL); }
++    @Test public void double_54() { testConst(DoubleNode62.MIN_NORMAL_BITS, 0x0010000000000000L); }
++    @Test public void double_55() { testConst(DoubleNode62.MIN_VALUE_BITS,  0x01L); }
++    
++    private void sameValue(double d1, double d2) {
++        // Not d1 == d2 - NaN != NaN 
++        assertEquals(Double.valueOf(d1), Double.valueOf(d2));  
++    }
++    
++    private static void testConst(long x, long expected) {
++        //print(expected);
++        //print(x);
++        assertEquals(expected, x);
++        double d = DoubleNode62.unpack(x);
++        long z = DoubleNode62.pack(d);
++        assertEquals(expected, z);
++    }
++    
++    private void testNoEncoding(double d) {
++        testRoundTripDouble(d, false); 
++    }
++
++    private static void testRoundTripDouble(double d) {
++        testRoundTripDouble(d, true); 
++    }
++    
++    private static void testRoundTripDouble(double d, boolean valid) {
++        //System.out.printf("Double: %.2e\n", d);
++        long x0 = Double.doubleToRawLongBits(d);
++        //print(x0);
++        long x = DoubleNode62.pack(d);
++        //print(x);
++        if ( x == DoubleNode62.NO_ENCODING ) {
++            if ( valid )
++                fail("Expect no encoding");
++            
++            //System.out.println("No encoding");
++            //System.out.println();
++            return;
++        }
++        
++        double d2 = DoubleNode62.unpack(x);
++        
++        Double double1 = d ;
++        Double double2 = d2 ;
++        assertEquals(double1, double2);
++    }
++
++    private static void print(long x) {
++        long z[] = new long[4];
++        for ( int i = 0 ; i < 4 ; i++ ) {
++             z[3-i] = BitsLong.unpack(x, i*16, (i+1)*16) ;
++        }
++        System.out.printf("0x%04X %04X %04X %04X\n", z[0], z[1], z[2], z[3]);
++    }
++}


[10/50] [abbrv] jena git commit: Expel datasets based on container/storage location.

Posted by an...@apache.org.
Expel datasets based on container/storage location.


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

Branch: refs/heads/jena-tdb2
Commit: d8fd29aa36e5d4e5da71c7f06f149ea2aa349e7b
Parents: 10f2bb1
Author: Andy Seaborne <an...@seaborne.org>
Authored: Sun Sep 3 17:14:56 2017 +0100
Committer: Andy Seaborne <an...@seaborne.org>
Committed: Sun Sep 3 17:14:56 2017 +0100

----------------------------------------------------------------------
 .../java/org/seaborne/tdb2/DatabaseMgr.java     |   4 +-
 .../tdb2/store/DatasetGraphSwitchable.java      |  12 +-
 .../seaborne/tdb2/sys/DatabaseConnection.java   |  12 +-
 .../java/org/seaborne/tdb2/sys/DatabaseOps.java |   4 +-
 .../org/seaborne/tdb2/sys/StoreConnection.java  |  10 +-
 .../java/org/seaborne/tdb2/sys/SystemTDB.java   |  33 +++---
 .../java/org/seaborne/tdb2/sys/TDBInternal.java |  20 +++-
 .../tdb2/graph/TestDatasetGraphAccessorTDB.java |  15 ++-
 .../tdb2/graph/TestDatasetGraphTDB.java         |  18 ++-
 .../tdb2/graph/TestGraphOverDatasetTDB.java     |  20 +++-
 .../org/seaborne/tdb2/graph/TestGraphsTDB1.java |  15 ++-
 .../test/java/org/seaborne/tdb2/junit/TL.java   |  39 ++++---
 .../tdb2/setup/TestStoreParamsCreate.java       |  26 +++--
 .../org/seaborne/tdb2/solver/TestSolverTDB.java |   4 +-
 .../org/seaborne/tdb2/solver/TestStats.java     |  69 +++++------
 .../org/seaborne/tdb2/store/TestDatasetTDB.java |   6 +-
 .../tdb2/store/TestDatasetTDBPersist.java       | 114 +++++++++----------
 .../tdb2/store/TestDynamicDatasetTDB.java       |   7 +-
 .../seaborne/tdb2/store/TestGraphNamedTDB.java  |   8 +-
 .../org/seaborne/tdb2/store/TestGraphTDB.java   |   6 +-
 .../seaborne/tdb2/store/TestTransactions.java   |   2 +-
 .../org/seaborne/tdb2/sys/TestDatabaseOps.java  |   2 -
 22 files changed, 273 insertions(+), 173 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/d8fd29aa/tdb2/src/main/java/org/seaborne/tdb2/DatabaseMgr.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/DatabaseMgr.java b/tdb2/src/main/java/org/seaborne/tdb2/DatabaseMgr.java
index b4dfd23..098a646 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/DatabaseMgr.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/DatabaseMgr.java
@@ -90,9 +90,9 @@ public class DatabaseMgr {
         return TDBInternal.isBackedByTDB(datasetGraph);
     }
 
-    /** Return the location of a DatasetGraph if it is backed by TDB, else null */
+    /** Return the location of a DatasetGraph if it is backed by TDB, else null. */
     public static Location location(DatasetGraph datasetGraph) {
-        DatasetGraphTDB dsg = requireDirect(datasetGraph);
+        DatasetGraphSwitchable dsg = requireSwitchable(datasetGraph);
         if ( dsg == null )
             return null ;
         return dsg.getLocation();

http://git-wip-us.apache.org/repos/asf/jena/blob/d8fd29aa/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetGraphSwitchable.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetGraphSwitchable.java b/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetGraphSwitchable.java
index 115b304..9469a1d 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetGraphSwitchable.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/store/DatasetGraphSwitchable.java
@@ -27,7 +27,9 @@ import org.apache.jena.graph.Graph;
 import org.apache.jena.graph.Node;
 import org.apache.jena.shared.PrefixMapping;
 import org.apache.jena.shared.impl.PrefixMappingImpl;
-import org.apache.jena.sparql.core.*;
+import org.apache.jena.sparql.core.DatasetGraph ;
+import org.apache.jena.sparql.core.DatasetPrefixStorage ;
+import org.seaborne.dboe.base.file.Location ;
 
 final
 public class DatasetGraphSwitchable extends DatasetGraphWrapperTxn /* Until ARQ catches up with promote */ 
@@ -43,12 +45,14 @@ public class DatasetGraphSwitchable extends DatasetGraphWrapperTxn /* Until ARQ
     private final AtomicReference<DatasetGraph> dsgx = new AtomicReference<>();
     // Null for in-memory datasets.
     private final Path basePath;
+    private final Location location ;
     
-    public DatasetGraphSwitchable(Path base, DatasetGraph dsg) {
+    public DatasetGraphSwitchable(Path base, Location location, DatasetGraph dsg) {
         // Don't use the slot in datasetGraphWrapper - use the AtomicReference
         super(null) ;
         dsgx.set(dsg);
-        this.basePath = base;  
+        this.basePath = base;
+        this.location = location; 
     }
 
     /** Is this {@code DatasetGraphSwitchable} just a holder for a {@code DatasetGraph}?
@@ -58,6 +62,8 @@ public class DatasetGraphSwitchable extends DatasetGraphWrapperTxn /* Until ARQ
     
     public Path getContainerPath() { return basePath; }
     
+    public Location getLocation() { return location; }
+
     /** The dataset to use for redirection - can be overridden.
      *  It is also guaranteed that this is called only once per
      *  delegated call.  Changes to the wrapped object can be

http://git-wip-us.apache.org/repos/asf/jena/blob/d8fd29aa/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseConnection.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseConnection.java b/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseConnection.java
index 0e22f8f..e9e6c27 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseConnection.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseConnection.java
@@ -114,16 +114,13 @@ public class DatabaseConnection {
      * Does not expel from {@link StoreConnection}.
      */
     public static synchronized void internalExpel(Location location, boolean force) {
+        // ** Check it is a container location 
         DatabaseConnection dbConn = cache.get(location) ;
-        if (dbConn == null) return ;
-        cache.remove(location) ;
+        if ( dbConn == null )
+            return ;
         dbConn.isValid = false;
         //dbConn.datasetGraph = null;
         //dbConn.datasetGraphSwitchable = null;
-        
-        //    if (!force && sConn.transactionManager.activeTransactions()) 
-        //        throw new TDBTransactionException("Can't expel: Active transactions for location: " + location) ;
-
         cache.remove(location) ;
         // Release the lock after the cache is emptied.
         if (SystemTDB.DiskLocationMultiJvmUsagePrevention && ! location.isMem() ) {
@@ -144,10 +141,11 @@ public class DatabaseConnection {
         Set<Location> x = new HashSet<>(cache.keySet()) ;
         for (Location loc : x)
             internalExpel(loc, true) ;
+        if ( ! cache.isEmpty() )
+            System.err.println("DatabaseConnection: Cache not empty!");
         cache.clear() ;
     }
     
-    
     // One of the other.
     private final DatasetGraphSwitchable   datasetGraphSwitchable;
     private final DatasetGraph             datasetGraph;

http://git-wip-us.apache.org/repos/asf/jena/blob/d8fd29aa/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseOps.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseOps.java b/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseOps.java
index b985798..bb34ed3 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseOps.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/sys/DatabaseOps.java
@@ -81,7 +81,7 @@ public class DatabaseOps {
     private static DatasetGraphSwitchable createSwitchable(Location location) {
         if ( location.isMem() ) {
             DatasetGraph dsg = StoreConnection.connectCreate(location).getDatasetGraph();
-            return new DatasetGraphSwitchable(null, dsg);
+            return new DatasetGraphSwitchable(null, location, dsg);
         }
         // Exists?
        if ( ! location.exists() )
@@ -97,7 +97,7 @@ public class DatabaseOps {
        }
        Location loc2 = IOX.asLocation(db);
        DatasetGraphTDB dsg = StoreConnection.connectCreate(loc2).getDatasetGraphTDB();
-       DatasetGraphSwitchable appDSG = new DatasetGraphSwitchable(path, dsg);
+       DatasetGraphSwitchable appDSG = new DatasetGraphSwitchable(path, location, dsg);
        return appDSG;
     }
     

http://git-wip-us.apache.org/repos/asf/jena/blob/d8fd29aa/tdb2/src/main/java/org/seaborne/tdb2/sys/StoreConnection.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/sys/StoreConnection.java b/tdb2/src/main/java/org/seaborne/tdb2/sys/StoreConnection.java
index 54d863b..21d5377 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/sys/StoreConnection.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/sys/StoreConnection.java
@@ -32,6 +32,8 @@ import org.seaborne.dboe.base.file.ChannelManager ;
 import org.seaborne.dboe.base.file.Location ;
 import org.seaborne.dboe.base.file.ProcessFileLock;
 import org.seaborne.dboe.sys.Names;
+import org.seaborne.dboe.transaction.txn.TransactionCoordinator ;
+import org.seaborne.dboe.transaction.txn.TransactionException ;
 import org.seaborne.tdb2.setup.StoreParams ;
 import org.seaborne.tdb2.setup.TDBBuilder ;
 import org.seaborne.tdb2.store.DatasetGraphTDB ;
@@ -124,14 +126,16 @@ public class StoreConnection
         StoreConnection sConn = cache.get(location) ;
         if (sConn == null) return ;
 
-        //    if (!force && sConn.transactionManager.activeTransactions()) 
-        //        throw new TDBTransactionException("Can't expel: Active transactions for location: " + location) ;
+        TransactionCoordinator txnCoord = sConn.getDatasetGraphTDB().getTxnSystem().getTxnMgr();
+        if (!force && txnCoord.countActive() > 0 ) 
+            throw new TransactionException("Can't expel: Active transactions for location: " + location) ;
 
         // No transactions at this point 
         // (or we don't care and are clearing up forcefully.)
         
-        sConn.getDatasetGraphTDB().getTxnSystem().getTxnMgr().shutdown();
         sConn.getDatasetGraphTDB().shutdown() ;
+        // Done by DatasetGraphTDB()
+        //txnCoord.shutdown();
         
         sConn.isValid = false ;
         cache.remove(location) ;

http://git-wip-us.apache.org/repos/asf/jena/blob/d8fd29aa/tdb2/src/main/java/org/seaborne/tdb2/sys/SystemTDB.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/sys/SystemTDB.java b/tdb2/src/main/java/org/seaborne/tdb2/sys/SystemTDB.java
index 6531310..51ef995 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/sys/SystemTDB.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/sys/SystemTDB.java
@@ -25,9 +25,6 @@ import org.apache.jena.atlas.io.IO ;
 import org.apache.jena.atlas.lib.PropertyUtils ;
 import org.apache.jena.atlas.logging.Log ;
 import org.apache.jena.query.ARQ ;
-import org.apache.jena.query.Dataset ;
-import org.apache.jena.query.ReadWrite ;
-import org.apache.jena.sparql.core.DatasetGraph ;
 import org.apache.jena.sparql.engine.optimizer.reorder.ReorderLib ;
 import org.apache.jena.sparql.engine.optimizer.reorder.ReorderTransformation ;
 import org.apache.jena.sparql.util.Symbol ;
@@ -38,7 +35,6 @@ import org.seaborne.dboe.base.record.RecordFactory ;
 import org.seaborne.dboe.sys.Sys;
 import org.seaborne.tdb2.TDB2 ;
 import org.seaborne.tdb2.TDBException ;
-import org.seaborne.tdb2.store.DatasetGraphTxn ;
 import org.seaborne.tdb2.store.NodeId ;
 import org.slf4j.Logger ;
 import org.slf4j.LoggerFactory ;
@@ -402,20 +398,19 @@ public class SystemTDB
         throw new TDBException("Unrecognized file mode (not one of 'default', 'direct' or 'mapped': "+x) ;
     }
 
-    public static Dataset setNonTransactional(Dataset dataset) {
-        if ( dataset.isInTransaction() )
-            return dataset ;    // And hope it's a write transaction.
-        dataset.begin(ReadWrite.WRITE);
-        //Or wrap DatasetGraphTDB?
-        return dataset ;
-    }
-    
-    public static DatasetGraph setNonTransactional(DatasetGraph dataset) {
-        DatasetGraphTxn dsgx = (DatasetGraphTxn)dataset ;
-        if ( dsgx.isInTransaction() )
-            return dataset ;
-        dsgx.begin(ReadWrite.WRITE);
-        return dataset ;
-    }
+//    public static Dataset setNonTransactional(Dataset dataset) {
+//        if ( dataset.isInTransaction() )
+//            return dataset ;    // And hope it's a write transaction.
+//        dataset.begin(ReadWrite.WRITE);
+//        //Or wrap DatasetGraphTDB?
+//        return dataset ;
+//    }
+//    
+//    public static DatasetGraph setNonTransactional(DatasetGraph dataset) {
+//        if ( dataset.isInTransaction() )
+//            return dataset ;
+//        dataset.begin(ReadWrite.WRITE);
+//        return dataset ;
+//    }
 
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/d8fd29aa/tdb2/src/main/java/org/seaborne/tdb2/sys/TDBInternal.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/sys/TDBInternal.java b/tdb2/src/main/java/org/seaborne/tdb2/sys/TDBInternal.java
index 25c1688..3bd7427 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/sys/TDBInternal.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/sys/TDBInternal.java
@@ -129,12 +129,22 @@ public class TDBInternal {
         return null;
     }
 
-    /** Stop managing a location. Use with great care (testing only). */
-    public static synchronized void expel(Location location, boolean force) {
-        DatabaseConnection.internalExpel(location, force);
-        StoreConnection.internalExpel(location, force);
+    /** Stop managing a DatasetGraph. Use with great care (testing only). */
+    public static synchronized void expel(DatasetGraph dsg) {
+        Location locContainer = null;
+        Location locStorage = null;
+        
+        if ( dsg instanceof DatasetGraphSwitchable ) {
+            locContainer = ((DatasetGraphSwitchable)dsg).getLocation();
+            dsg = ((DatasetGraphSwitchable)dsg).getWrapped();
+        }
+        if ( dsg instanceof DatasetGraphTDB )
+            locStorage = ((DatasetGraphTDB)dsg).getLocation();
+        
+        DatabaseConnection.internalExpel(locContainer, false);
+        StoreConnection.internalExpel(locStorage, false);
     }
-    
+
     /** 
      * Reset the whole TDB system.      
      * Use with great care.

http://git-wip-us.apache.org/repos/asf/jena/blob/d8fd29aa/tdb2/src/test/java/org/seaborne/tdb2/graph/TestDatasetGraphAccessorTDB.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/graph/TestDatasetGraphAccessorTDB.java b/tdb2/src/test/java/org/seaborne/tdb2/graph/TestDatasetGraphAccessorTDB.java
index 327ca47..8184154 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/graph/TestDatasetGraphAccessorTDB.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/graph/TestDatasetGraphAccessorTDB.java
@@ -18,17 +18,30 @@
 package org.seaborne.tdb2.graph;
 
 import org.apache.jena.query.DatasetAccessorFactory ;
+import org.apache.jena.query.ReadWrite ;
 import org.apache.jena.sparql.core.DatasetGraph ;
 import org.apache.jena.web.AbstractTestDatasetGraphAccessor ;
 import org.apache.jena.web.DatasetGraphAccessor ;
+import org.junit.After ;
+import org.junit.Before ;
 import org.seaborne.tdb2.junit.TL ;
 
 public class TestDatasetGraphAccessorTDB extends AbstractTestDatasetGraphAccessor
 {
+    DatasetGraph dsg = TL.createTestDatasetGraphMem() ;
+    @Before public void before() {
+        dsg.begin(ReadWrite.WRITE);
+    }
+    
+    @After public void after() {
+        dsg.abort();
+        dsg.end();
+        TL.expel(dsg);
+    }
+
     @Override
     protected DatasetGraphAccessor getDatasetUpdater()
     {
-        DatasetGraph dsg = TL.createTestDatasetGraphMem() ;
         return DatasetAccessorFactory.make(dsg) ;
     }
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/d8fd29aa/tdb2/src/test/java/org/seaborne/tdb2/graph/TestDatasetGraphTDB.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/graph/TestDatasetGraphTDB.java b/tdb2/src/test/java/org/seaborne/tdb2/graph/TestDatasetGraphTDB.java
index 12092fb..0cd2bca 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/graph/TestDatasetGraphTDB.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/graph/TestDatasetGraphTDB.java
@@ -17,16 +17,32 @@
 
 package org.seaborne.tdb2.graph;
 
+import org.apache.jena.query.ReadWrite ;
 import org.apache.jena.sparql.core.AbstractDatasetGraphTests ;
 import org.apache.jena.sparql.core.DatasetGraph ;
+import org.junit.After ;
+import org.junit.Before ;
 import org.junit.Test ;
 import org.seaborne.tdb2.junit.TL ;
 
 // Quad tests
 public class TestDatasetGraphTDB extends AbstractDatasetGraphTests
 {
+    DatasetGraph dsg = TL.createTestDatasetGraphMem() ;
+    @Before public void before() {
+        dsg.begin(ReadWrite.WRITE);
+    }
+    
+    @After public void after() {
+        dsg.abort();
+        dsg.end();
+        TL.expel(dsg);
+    }
+    
     @Override
-    protected DatasetGraph emptyDataset() { return TL.createTestDatasetGraphMem() ; }
+    protected DatasetGraph emptyDataset() {
+        return dsg ;
+    }
 
     @Override
     // Empty graph tests: N/A

http://git-wip-us.apache.org/repos/asf/jena/blob/d8fd29aa/tdb2/src/test/java/org/seaborne/tdb2/graph/TestGraphOverDatasetTDB.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/graph/TestGraphOverDatasetTDB.java b/tdb2/src/test/java/org/seaborne/tdb2/graph/TestGraphOverDatasetTDB.java
index 20a0577..de01a86 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/graph/TestGraphOverDatasetTDB.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/graph/TestGraphOverDatasetTDB.java
@@ -19,15 +19,33 @@ package org.seaborne.tdb2.graph;
 
 import org.apache.jena.graph.Graph ;
 import org.apache.jena.graph.Node ;
+import org.apache.jena.query.ReadWrite ;
 import org.apache.jena.sparql.core.AbstractTestGraphOverDataset ;
 import org.apache.jena.sparql.core.DatasetGraph ;
+import org.junit.After ;
 import org.seaborne.tdb2.junit.TL ;
 
 /** This is the view-graph test suite run over a TDB DatasetGraph to check compatibility */
 public class TestGraphOverDatasetTDB extends AbstractTestGraphOverDataset
 {
+    DatasetGraph dsg = null;
+    @After public void after2() {
+        if ( dsg == null )
+            return;
+        dsg.abort();
+        dsg.end();
+        TL.expel(dsg);
+    }
+    
     @Override
-    protected DatasetGraph createBaseDSG() { return TL.createTestDatasetGraphMem() ; }
+    protected DatasetGraph createBaseDSG() {
+        // Called in initialization.
+        if ( dsg == null ) {
+            dsg = TL.createTestDatasetGraphMem() ;
+            dsg.begin(ReadWrite.WRITE);
+        }
+        return dsg ;
+    }
     
     @Override
     protected Graph makeNamedGraph(DatasetGraph dsg, Node gn)

http://git-wip-us.apache.org/repos/asf/jena/blob/d8fd29aa/tdb2/src/test/java/org/seaborne/tdb2/graph/TestGraphsTDB1.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/graph/TestGraphsTDB1.java b/tdb2/src/test/java/org/seaborne/tdb2/graph/TestGraphsTDB1.java
index e6638a3..3f703e5 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/graph/TestGraphsTDB1.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/graph/TestGraphsTDB1.java
@@ -18,12 +18,25 @@
 package org.seaborne.tdb2.graph;
 
 import org.apache.jena.query.Dataset ;
+import org.apache.jena.query.ReadWrite ;
+import org.junit.After ;
+import org.junit.Before ;
 import org.seaborne.tdb2.junit.TL ;
 
 public class TestGraphsTDB1 extends AbstractTestGraphsTDB
 {
+    Dataset ds = TL.createTestDatasetMem();
+    @Before public void before() {
+        ds.begin(ReadWrite.WRITE);
+    }
+    
+    @After public void after() {
+        ds.abort();
+        ds.end();
+        TL.expel(ds);
+    }
     @Override
     protected Dataset createDataset() {
-        return TL.createTestDatasetMem() ;
+        return ds ;
     }
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/d8fd29aa/tdb2/src/test/java/org/seaborne/tdb2/junit/TL.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/junit/TL.java b/tdb2/src/test/java/org/seaborne/tdb2/junit/TL.java
index 7385354..4ff4ae6 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/junit/TL.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/junit/TL.java
@@ -21,28 +21,37 @@ import java.util.function.Consumer ;
 
 import org.apache.jena.query.Dataset ;
 import org.apache.jena.sparql.core.DatasetGraph ;
+import org.apache.jena.system.Txn ;
 import org.seaborne.dboe.base.file.Location ;
 import org.seaborne.tdb2.ConfigTest ;
 import org.seaborne.tdb2.DatabaseMgr ;
 import org.seaborne.tdb2.TDB2Factory ;
-import org.seaborne.tdb2.sys.SystemTDB ;
 import org.seaborne.tdb2.sys.TDBInternal ;
 
-/** Execute a test with a fresh dataset in a write transaction */
+/** Support for tests to be less "transaction-y" */
 public class TL {
     
     public static void exec(Consumer<Dataset> action) {
-        Dataset dataset = createTestDataset() ;
-        action.accept(dataset);
-        releaseDataset(dataset) ;
-        TDBInternal.reset() ;
+        Dataset dataset = createTestDataset();
+        try { 
+            Txn.executeWrite(dataset, ()->action.accept(dataset));
+            //TDBInternal.reset();
+            
+        } finally { expel(dataset); } 
     }
     
     public static void execMem(Consumer<Dataset> action) {
         Dataset dataset = createTestDatasetMem() ;
-        action.accept(dataset);
-        releaseDataset(dataset);
-        TDBInternal.reset();
+        Txn.executeWrite(dataset, ()->action.accept(dataset));
+        expel(dataset);
+    }
+    
+    public static void expel(Dataset dataset) {
+        expel(dataset.asDatasetGraph());
+    }
+
+    public static void expel(DatasetGraph dataset) {
+        TDBInternal.expel(dataset);
     }
 
     // Or use these for @Before, @After style.
@@ -56,30 +65,24 @@ public class TL {
         return location ;
     }
     
-    public static void releaseDataset(Dataset dataset) {
+    private static void releaseDataset(Dataset dataset) {
         dataset.abort() ;
-        TDBInternal.reset() ;
+        expel(dataset);
     }
 
-    public static Dataset createTestDataset() {
+    private static Dataset createTestDataset() {
         Location location = cleanLocation() ;
         Dataset dataset = TDB2Factory.connectDataset(location) ;
-        // Non-transactional tests
-        dataset = SystemTDB.setNonTransactional(dataset) ;
         return dataset ;
     }
     
     public static Dataset createTestDatasetMem() {
         Dataset dataset = TDB2Factory.createDataset() ;
-        // Non-transactional tests
-        dataset = SystemTDB.setNonTransactional(dataset) ;
         return dataset ;
     }
 
     public static DatasetGraph createTestDatasetGraphMem() {
         DatasetGraph dataset = DatabaseMgr.createDatasetGraph() ;
-        // Non-transactional tests
-        dataset = SystemTDB.setNonTransactional(dataset) ;
         return dataset ;
     }
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/d8fd29aa/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParamsCreate.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParamsCreate.java b/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParamsCreate.java
index 44aacfb..071b135 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParamsCreate.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParamsCreate.java
@@ -17,13 +17,19 @@
 
 package org.seaborne.tdb2.setup;
 
+import static org.junit.Assert.assertEquals ;
+import static org.junit.Assert.assertFalse ;
+import static org.junit.Assert.assertNotEquals ;
+import static org.junit.Assert.assertNotNull ;
+import static org.junit.Assert.assertNull ;
+import static org.junit.Assert.assertTrue ;
+
 import java.nio.file.Files ;
 import java.nio.file.Path ;
 import java.nio.file.Paths ;
 
 import org.apache.jena.atlas.json.JSON ;
 import org.apache.jena.atlas.json.JsonObject ;
-import static org.junit.Assert.*;
 import org.apache.jena.atlas.lib.FileOps ;
 import org.junit.After ;
 import org.junit.Before ;
@@ -31,7 +37,6 @@ import org.junit.Test ;
 import org.seaborne.dboe.base.file.Location ;
 import org.seaborne.dboe.sys.Names;
 import org.seaborne.tdb2.ConfigTest ;
-import org.seaborne.tdb2.sys.DatabaseConnection ;
 import org.seaborne.tdb2.sys.StoreConnection ;
 import org.seaborne.tdb2.sys.TDBInternal ;
 
@@ -40,6 +45,7 @@ import org.seaborne.tdb2.sys.TDBInternal ;
  * calls and can be noticably slow.
  */
 public class TestStoreParamsCreate {
+    // Thse tests work on storage locations.
     private final String DB_DIR = ConfigTest.getCleanDir() ;
     private final Path db = Paths.get(DB_DIR) ;
     private final Path cfg = Paths.get(DB_DIR, Names.TDB_CONFIG_FILE) ;
@@ -52,14 +58,18 @@ public class TestStoreParamsCreate {
         .blockReadCacheSize(4)
         .build();
     
+    private void expel() {
+        StoreConnection.internalExpel(loc, true);
+    }
+    
     @Before public void clearupTest() { 
         // Flush and clean.
-        DatabaseConnection.internalExpel(loc, true);
-        FileOps.clearAll(DB_DIR);
+        TDBInternal.reset();
+        FileOps.clearAll(loc.getDirectoryPath());
     }
 
     @After public void expelDatabase() { 
-        TDBInternal.expel(loc, true) ;
+        expel();
     }
     
     @Test public void params_create_01() {
@@ -83,7 +93,7 @@ public class TestStoreParamsCreate {
         // Create.
         StoreConnection.connectCreate(loc) ;
         // Drop.
-        TDBInternal.expel(loc, true) ;
+        expel();
         // Reconnect
         StoreConnection.connectCreate(loc, null) ;
         StoreParams pLoc = StoreParamsCodec.read(loc) ;
@@ -100,7 +110,7 @@ public class TestStoreParamsCreate {
         // Create.
         StoreConnection.connectCreate(loc, null) ;
         // Drop.
-        TDBInternal.expel(loc, true) ;
+        expel();
         // Reconnect
         StoreConnection.connectCreate(loc, pSpecial) ;
         //StoreParams pLoc = StoreParamsCodec.read(loc) ;
@@ -125,7 +135,7 @@ public class TestStoreParamsCreate {
         // Create.
         StoreConnection.connectCreate(loc, pApp) ;
         // Drop.
-        TDBInternal.expel(loc, true) ;
+        expel();
         // Reconnect
         StoreConnection.connectCreate(loc, pSpecial) ;
         //StoreParams pLoc = StoreParamsCodec.read(loc) ;

http://git-wip-us.apache.org/repos/asf/jena/blob/d8fd29aa/tdb2/src/test/java/org/seaborne/tdb2/solver/TestSolverTDB.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/solver/TestSolverTDB.java b/tdb2/src/test/java/org/seaborne/tdb2/solver/TestSolverTDB.java
index d94252e..43ef56d 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/solver/TestSolverTDB.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/solver/TestSolverTDB.java
@@ -54,6 +54,7 @@ public class TestSolverTDB
     @BeforeClass static public void beforeClass()
     { 
         dataset = TL.createTestDatasetMem() ;
+        dataset.begin(ReadWrite.WRITE);
         graphData = ConfigTest.getTestingDataRoot()+"/Data/solver-data.ttl" ;
         RDFDataMgr.read(dataset, graphData) ;
         graph = dataset.asDatasetGraph().getDefaultGraph() ;
@@ -63,7 +64,8 @@ public class TestSolverTDB
     
     
     @AfterClass static public void afterClass() {
-        TL.releaseDataset(dataset);
+        dataset.abort();
+        TL.expel(dataset);
     }
             
     static private void addAll(Graph srcGraph, Graph dstGraph)

http://git-wip-us.apache.org/repos/asf/jena/blob/d8fd29aa/tdb2/src/test/java/org/seaborne/tdb2/solver/TestStats.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/solver/TestStats.java b/tdb2/src/test/java/org/seaborne/tdb2/solver/TestStats.java
index 8bb3ae7..a9dfe92 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/solver/TestStats.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/solver/TestStats.java
@@ -17,14 +17,16 @@
 
 package org.seaborne.tdb2.solver;
 
+import static org.junit.Assert.assertEquals ;
+
 import java.util.Iterator ;
 
-import static org.junit.Assert.*;
 import org.apache.jena.atlas.lib.tuple.Tuple ;
 import org.apache.jena.graph.NodeFactory ;
 import org.apache.jena.sparql.core.Quad ;
 import org.apache.jena.sparql.sse.SSE ;
 import org.junit.Test ;
+import org.seaborne.dboe.jenax.Txn ;
 import org.seaborne.tdb2.junit.TL ;
 import org.seaborne.tdb2.solver.stats.StatsCollectorNodeId ;
 import org.seaborne.tdb2.solver.stats.StatsResults ;
@@ -46,42 +48,45 @@ public class TestStats
     static Quad q3 = SSE.parseQuad("(<g2> <s> <p> 9)") ; 
     static Quad q4 = SSE.parseQuad("(_    <s> <p> 1)") ; 
     static {
-        dsg.add(q1) ;
-        dsg.add(q2) ;
-        dsg.add(q3) ;
-        dsg.add(q4) ;
+        Txn.executeWrite(dsg, ()->{
+            dsg.add(q1) ;
+            dsg.add(q2) ;
+            dsg.add(q3) ;
+            dsg.add(q4) ;
+        });
     }
-
     
-    private StatsResults statsForGraph(NodeId gid)
-    {
-        Iterator<Tuple<NodeId>> iter = quads.find(gid, null, null, null) ;
-        
-        StatsCollectorNodeId stats = new StatsCollectorNodeId(nt) ;
-        for ( ; iter.hasNext(); )
-        {
-            Tuple<NodeId> t = iter.next() ;
-            stats.record(t.get(0), t.get(1), t.get(2), t.get(3)) ;
-        }
-        
-        return stats.results() ;
+    private StatsResults statsForGraph(NodeId gid) {
+        // StatsCollectorNodeId writes nodes for rdf:type (this is not good).
+        return Txn.calculateWrite(dsg, ()-> {
+            Iterator<Tuple<NodeId>> iter = quads.find(gid, null, null, null) ;
+
+            StatsCollectorNodeId stats = new StatsCollectorNodeId(nt) ;
+            for ( ; iter.hasNext() ; ) {
+                Tuple<NodeId> t = iter.next() ;
+                stats.record(t.get(0), t.get(1), t.get(2), t.get(3)) ;
+            }
+
+            return stats.results() ;
+        }) ;
     }
-    
-    private StatsResults statsForDftGraph()
-    {
-        Iterator<Tuple<NodeId>> iter = triples.findAll() ;
-        
-        StatsCollectorNodeId stats = new StatsCollectorNodeId(nt) ;
-        for ( ; iter.hasNext(); )
-        {
-            Tuple<NodeId> t = iter.next() ;
-            stats.record(null, t.get(0), t.get(1), t.get(2)) ;
-        }
-        
-        return stats.results() ;
+
+    private StatsResults statsForDftGraph() {
+        // StatsCollectorNodeId writes nodes for rdf:type (this is not good).
+        return Txn.calculateWrite(dsg, ()-> {
+            Iterator<Tuple<NodeId>> iter = triples.findAll() ;
+
+            StatsCollectorNodeId stats = new StatsCollectorNodeId(nt) ;
+            for ( ; iter.hasNext() ; ) {
+                Tuple<NodeId> t = iter.next() ;
+                stats.record(null, t.get(0), t.get(1), t.get(2)) ;
+            }
+
+            return stats.results() ;
+        }) ;
     }
 
-    @Test public void stats_01() { 
+    @Test public void stats_01() {
         StatsResults r = statsForDftGraph() ;
         assertEquals(1, r.getCount()) ; 
         assertEquals(1, r.getPredicates().keySet().size()) ;

http://git-wip-us.apache.org/repos/asf/jena/blob/d8fd29aa/tdb2/src/test/java/org/seaborne/tdb2/store/TestDatasetTDB.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/store/TestDatasetTDB.java b/tdb2/src/test/java/org/seaborne/tdb2/store/TestDatasetTDB.java
index 48fd7fb..1ff5387 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/store/TestDatasetTDB.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/store/TestDatasetTDB.java
@@ -40,10 +40,14 @@ public class TestDatasetTDB
     
     @Before public void before() {
         dataset = TL.createTestDatasetMem() ;
+        dataset.begin(ReadWrite.WRITE);
     }
     
     @After public void after() {
-        TL.releaseDataset(dataset);
+        dataset.abort();
+        dataset.end();
+        TL.expel(dataset);
+        
     }
     
     private Dataset dataset() {

http://git-wip-us.apache.org/repos/asf/jena/blob/d8fd29aa/tdb2/src/test/java/org/seaborne/tdb2/store/TestDatasetTDBPersist.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/store/TestDatasetTDBPersist.java b/tdb2/src/test/java/org/seaborne/tdb2/store/TestDatasetTDBPersist.java
index fe928a0..81a5dd5 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/store/TestDatasetTDBPersist.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/store/TestDatasetTDBPersist.java
@@ -17,18 +17,12 @@
 
 package org.seaborne.tdb2.store;
 
-import static org.junit.Assert.assertEquals ;
 import static org.junit.Assert.assertFalse ;
 import static org.junit.Assert.assertTrue ;
 
-import java.util.Arrays ;
-import java.util.List ;
-
-import org.apache.jena.atlas.iterator.Iter ;
 import org.apache.jena.graph.Graph ;
 import org.apache.jena.graph.Node ;
 import org.apache.jena.graph.Triple ;
-import org.apache.jena.sparql.sse.SSE ;
 import org.apache.jena.sparql.util.NodeFactoryExtra ;
 import org.junit.Test ;
 import org.seaborne.tdb2.junit.TL ;
@@ -60,58 +54,58 @@ public class TestDatasetTDBPersist
         }) ;
     }
 
-    @Test
-    public void dataset3() {
-        TL.exec((ds) -> {
-            Graph g1 = ds.getDefaultModel().getGraph() ;
-            // Sometimes, under windows, deleting the files by
-            // clearDirectory does not work.
-            // Needed for safe tests on windows.
-            g1.clear() ;
-
-            Graph g2 = ds.getNamedModel("http://example/").getGraph() ;
-            g2.add(new Triple(n0, n1, n2)) ;
-            assertTrue(g2.contains(n0, n1, n2)) ;
-            assertFalse(g1.contains(n0, n1, n2)) ;
-        }) ;
-    }
-
-    @Test
-    public void dataset4() {
-        String graphName = "http://example/" ;
-        Triple triple = SSE.parseTriple("(<x> <y> <z>)") ;
-        Node gn = org.apache.jena.graph.NodeFactory.createURI(graphName) ;
-
-        TL.exec((ds) -> {
-            // ?? See TupleLib.
-            ds.asDatasetGraph().deleteAny(gn, null, null, null) ;
-
-            Graph g2 = ds.asDatasetGraph().getGraph(gn) ;
-
-            // Graphs only exists if they have a triple in them
-            assertFalse(ds.containsNamedModel(graphName)) ;
-
-            List<String> names = Iter.toList(ds.listNames()) ;
-            assertEquals(0, names.size()) ;
-            assertEquals(0, ds.asDatasetGraph().size()) ;
-        }) ;
-    }
-    
-    @Test
-    public void dataset5() {
-        String graphName = "http://example/" ;
-        Triple triple = SSE.parseTriple("(<x> <y> <z>)") ;
-        TL.exec((ds) -> {
-            Graph g2 = ds.asDatasetGraph().getGraph(org.apache.jena.graph.NodeFactory.createURI(graphName)) ;
-            // Graphs only exists if they have a triple in them
-            g2.add(triple) ;
-
-            assertTrue(ds.containsNamedModel(graphName)) ;
-            List<String> x = Iter.toList(ds.listNames()) ;
-            List<String> y = Arrays.asList(graphName) ;
-            assertEquals(x, y) ;
-
-            assertEquals(1, ds.asDatasetGraph().size()) ;
-        }) ;
-        }
+//    @Test
+//    public void dataset3() {
+//        TL.exec((ds) -> {
+//            Graph g1 = ds.getDefaultModel().getGraph() ;
+//            // Sometimes, under windows, deleting the files by
+//            // clearDirectory does not work.
+//            // Needed for safe tests on windows.
+//            g1.clear() ;
+//
+//            Graph g2 = ds.getNamedModel("http://example/").getGraph() ;
+//            g2.add(new Triple(n0, n1, n2)) ;
+//            assertTrue(g2.contains(n0, n1, n2)) ;
+//            assertFalse(g1.contains(n0, n1, n2)) ;
+//        }) ;
+//    }
+//
+//    @Test
+//    public void dataset4() {
+//        String graphName = "http://example/" ;
+//        Triple triple = SSE.parseTriple("(<x> <y> <z>)") ;
+//        Node gn = org.apache.jena.graph.NodeFactory.createURI(graphName) ;
+//
+//        TL.exec((ds) -> {
+//            // ?? See TupleLib.
+//            ds.asDatasetGraph().deleteAny(gn, null, null, null) ;
+//
+//            Graph g2 = ds.asDatasetGraph().getGraph(gn) ;
+//
+//            // Graphs only exists if they have a triple in them
+//            assertFalse(ds.containsNamedModel(graphName)) ;
+//
+//            List<String> names = Iter.toList(ds.listNames()) ;
+//            assertEquals(0, names.size()) ;
+//            assertEquals(0, ds.asDatasetGraph().size()) ;
+//        }) ;
+//    }
+//
+//    @Test
+//    public void dataset5() {
+//        String graphName = "http://example/" ;
+//        Triple triple = SSE.parseTriple("(<x> <y> <z>)") ;
+//        TL.exec((ds) -> {
+//            Graph g2 = ds.asDatasetGraph().getGraph(org.apache.jena.graph.NodeFactory.createURI(graphName)) ;
+//            // Graphs only exists if they have a triple in them
+//            g2.add(triple) ;
+//
+//            assertTrue(ds.containsNamedModel(graphName)) ;
+//            List<String> x = Iter.toList(ds.listNames()) ;
+//            List<String> y = Arrays.asList(graphName) ;
+//            assertEquals(x, y) ;
+//
+//            assertEquals(1, ds.asDatasetGraph().size()) ;
+//        }) ;
+//    }
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/d8fd29aa/tdb2/src/test/java/org/seaborne/tdb2/store/TestDynamicDatasetTDB.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/store/TestDynamicDatasetTDB.java b/tdb2/src/test/java/org/seaborne/tdb2/store/TestDynamicDatasetTDB.java
index fbcfc48..d0376e9 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/store/TestDynamicDatasetTDB.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/store/TestDynamicDatasetTDB.java
@@ -18,6 +18,7 @@
 package org.seaborne.tdb2.store;
 
 import org.apache.jena.query.Dataset ;
+import org.apache.jena.query.ReadWrite ;
 import org.apache.jena.sparql.core.AbstractTestDynamicDataset ;
 import org.seaborne.tdb2.TDB2 ;
 import org.seaborne.tdb2.junit.TL ;
@@ -27,11 +28,13 @@ public class TestDynamicDatasetTDB extends AbstractTestDynamicDataset
     @Override
     protected Dataset createDataset()
     {
-        return TL.createTestDatasetMem() ;
+        Dataset ds = TL.createTestDatasetMem() ;
+        ds.begin(ReadWrite.WRITE);
+        return ds;
     }
     
     @Override
-    protected void releaseDataset(Dataset ds) { TL.releaseDataset(ds); }
+    protected void releaseDataset(Dataset ds) { ds.abort(); TL.expel(ds); }
 
     protected void startDynamicAndUnionTest()    { TDB2.getContext().setTrue(TDB2.symUnionDefaultGraph) ; }
 

http://git-wip-us.apache.org/repos/asf/jena/blob/d8fd29aa/tdb2/src/test/java/org/seaborne/tdb2/store/TestGraphNamedTDB.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/store/TestGraphNamedTDB.java b/tdb2/src/test/java/org/seaborne/tdb2/store/TestGraphNamedTDB.java
index 99c933e..d844c5a 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/store/TestGraphNamedTDB.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/store/TestGraphNamedTDB.java
@@ -20,6 +20,7 @@ package org.seaborne.tdb2.store;
 import org.apache.jena.graph.Graph ;
 import org.apache.jena.graph.Node ;
 import org.apache.jena.query.Dataset ;
+import org.apache.jena.query.ReadWrite ;
 import org.apache.jena.sparql.graph.AbstractTestGraph2 ;
 import org.apache.jena.sparql.util.NodeFactoryExtra ;
 import org.junit.After ;
@@ -36,14 +37,17 @@ public class TestGraphNamedTDB extends AbstractTestGraph2
     @Before
     public void before() {
         dataset = TL.createTestDatasetMem() ;
+        dataset.begin(ReadWrite.WRITE);
         graph = dataset.asDatasetGraph().getGraph(graphNode) ;
     }
 
     @After
     public void after() {
-        TL.releaseDataset(dataset) ;
+        dataset.abort();
+        dataset.end();
+        TL.expel(dataset) ;
     }
-
+    
     @Override
     protected Graph emptyGraph() {
         graph.clear() ;

http://git-wip-us.apache.org/repos/asf/jena/blob/d8fd29aa/tdb2/src/test/java/org/seaborne/tdb2/store/TestGraphTDB.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/store/TestGraphTDB.java b/tdb2/src/test/java/org/seaborne/tdb2/store/TestGraphTDB.java
index 0317cc5..7f941eb 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/store/TestGraphTDB.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/store/TestGraphTDB.java
@@ -19,6 +19,7 @@ package org.seaborne.tdb2.store;
 
 import org.apache.jena.graph.Graph ;
 import org.apache.jena.query.Dataset ;
+import org.apache.jena.query.ReadWrite ;
 import org.apache.jena.sparql.graph.AbstractTestGraph2 ;
 import org.junit.After ;
 import org.junit.Before ;
@@ -33,12 +34,15 @@ public class TestGraphTDB extends AbstractTestGraph2
     @Before
     public void before() {
         dataset = TL.createTestDatasetMem() ;
+        dataset.begin(ReadWrite.WRITE);
         graph = dataset.getDefaultModel().getGraph() ;
     }
 
     @After
     public void after() {
-        TL.releaseDataset(dataset) ;
+        dataset.abort();
+        dataset.end();
+        TL.expel(dataset) ;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/jena/blob/d8fd29aa/tdb2/src/test/java/org/seaborne/tdb2/store/TestTransactions.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/store/TestTransactions.java b/tdb2/src/test/java/org/seaborne/tdb2/store/TestTransactions.java
index 47bc3e5..1aed0a6 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/store/TestTransactions.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/store/TestTransactions.java
@@ -83,7 +83,7 @@ public class TestTransactions
 
     @After public void after() {
         dataset.close() ;
-        TDBInternal.expel(location, true);
+        TDBInternal.expel(dataset.asDatasetGraph());
     }
 
     // Models across transactions

http://git-wip-us.apache.org/repos/asf/jena/blob/d8fd29aa/tdb2/src/test/java/org/seaborne/tdb2/sys/TestDatabaseOps.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/sys/TestDatabaseOps.java b/tdb2/src/test/java/org/seaborne/tdb2/sys/TestDatabaseOps.java
index 67883a4..09b9804 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/sys/TestDatabaseOps.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/sys/TestDatabaseOps.java
@@ -90,8 +90,6 @@ public class TestDatabaseOps
         Txn.executeWrite(dsgOld, ()->dsgOld.delete(quad1));
         Txn.executeRead(dsg,     ()->assertTrue(dsg.contains(quad1)) );
         Txn.executeRead(dsg2,    ()->assertTrue(dsg2.contains(quad1)) ) ;
-        
-        TDBInternal.expel(loc1, true);
     }
 
 //    @Test public void compact_2() {


[28/50] [abbrv] jena git commit: JENA-1396: Merge //github/afs/mantis as subdirectory jena-db/

Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/TDBInternal.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/TDBInternal.java
index 0000000,0000000..0b57de8
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/TDBInternal.java
@@@ -1,0 -1,0 +1,174 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.sys;
++
++import org.apache.jena.graph.Node;
++import org.apache.jena.query.Dataset;
++import org.apache.jena.sparql.core.DatasetGraph;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.tdb2.TDBException ;
++import org.seaborne.tdb2.store.DatasetGraphSwitchable ;
++import org.seaborne.tdb2.store.DatasetGraphTDB;
++import org.seaborne.tdb2.store.NodeId;
++import org.seaborne.tdb2.store.nodetable.NodeTable;
++
++/**
++ * A collection of helpers to abstract away from calling code knowing the 
++ * internal details of TDB. 
++ * <p>
++ * Use with care.
++ * <p>{@link DatabaseOps#compact Compaction} invalidates any previous objects.
++ * 
++ */
++public class TDBInternal {
++    /**
++     * Return the NodeId for a node. Returns NodeId.NodeDoesNotExist when the node is not
++     * found. Returns null when not a TDB-backed dataset.
++     */
++    public static NodeId getNodeId(Dataset ds, Node node) {
++        return getNodeId(ds.asDatasetGraph(), node);
++    }
++
++    /**
++     * Return the NodeId for a node. Returns NodeId.NodeDoesNotExist when the node is not
++     * found. Returns null when not a TDB-backed dataset.
++     */
++    public static NodeId getNodeId(DatasetGraph ds, Node node) {
++        DatasetGraphTDB dsg = getDatasetGraphTDB(ds);
++        return getNodeId(dsg, node);
++    }
++
++    /**
++     * Return the NodeId for a node. Returns NodeId.NodeDoesNotExist when the node is not
++     * found. Returns null when not a TDB-backed dataset.
++     */
++    public static NodeId getNodeId(DatasetGraphTDB dsg, Node node) {
++        if ( dsg == null )
++            return null;
++        NodeTable nodeTable = dsg.getQuadTable().getNodeTupleTable().getNodeTable();
++        NodeId nodeId = nodeTable.getNodeIdForNode(node);
++        return nodeId;
++    }
++
++    /**
++     * Return the node for a NodeId (if any). Returns null if the NodeId does not exist in
++     * the dataset.
++     */
++    public static Node getNode(Dataset ds, NodeId nodeId) {
++        return getNode(ds.asDatasetGraph(), nodeId);
++    }
++
++    /**
++     * Return the node for a NodeId (if any). Returns null if the NodeId does not exist in
++     * the dataset.
++     */
++    public static Node getNode(DatasetGraph ds, NodeId nodeId) {
++        DatasetGraphTDB dsg = getDatasetGraphTDB(ds);
++        return getNode(dsg, nodeId);
++    }
++
++    /**
++     * Return the node for a NodeId (if any). Returns null if the NodeId does not exist in
++     * the dataset.
++     */
++    public static Node getNode(DatasetGraphTDB dsg, NodeId nodeId) {
++        if ( dsg == null )
++            return null;
++        NodeTable nodeTable = dsg.getQuadTable().getNodeTupleTable().getNodeTable();
++        Node node = nodeTable.getNodeForNodeId(nodeId);
++        return node;
++    }
++
++    /**
++     * Return the DatasetGraphTDB for a Dataset, or null.
++     * Use the {@link DatasetGraphTDB} with care.
++     */
++    public static DatasetGraphTDB getDatasetGraphTDB(Dataset ds) {
++        return getDatasetGraphTDB(ds.asDatasetGraph());
++    }
++
++    /**
++     * Return the DatasetGraphTDB for a DatasetGraph, or null.
++     * Use the {@link DatasetGraphTDB} with care.
++     */
++    public static DatasetGraphSwitchable getDatabaseContainer(DatasetGraph dsg) {
++        if ( dsg instanceof DatasetGraphSwitchable )
++            return (DatasetGraphSwitchable)dsg;
++        throw new TDBException("Not a TDB database container");
++    }
++    
++    /**
++     * Return the DatasetGraphTDB for a DatasetGraph, or null.
++     * Use the {@link DatasetGraphTDB} with care.
++     */
++    public static DatasetGraphTDB getDatasetGraphTDB(DatasetGraph dsg) {
++        return unwrap(dsg);
++    }
++
++    /**
++     * Return the DatasetGraphTDB for a DatasetGraph, or throw an exception.
++     */
++    public static DatasetGraphTDB requireStorage(DatasetGraph dsg) {
++        DatasetGraphTDB dsgtdb = unwrap(dsg);
++        if ( dsgtdb == null )
++            throw new TDBException("Not a TDB database (argument is neither a switchable nor direct TDB DatasetGraph)");
++        return dsgtdb;
++    }
++    
++    private static DatasetGraphTDB unwrap(DatasetGraph datasetGraph) {
++        DatasetGraph dsg = datasetGraph;
++        if ( dsg instanceof DatasetGraphSwitchable )
++            dsg = ((DatasetGraphSwitchable)datasetGraph).get();
++        if ( dsg instanceof DatasetGraphTDB )
++            return ((DatasetGraphTDB)dsg);
++        return null;
++    }
++
++    /** Stop managing a DatasetGraph. Use with great care (testing only). */
++    public static synchronized void expel(DatasetGraph dsg) {
++        Location locContainer = null;
++        Location locStorage = null;
++        
++        if ( dsg instanceof DatasetGraphSwitchable ) {
++            locContainer = ((DatasetGraphSwitchable)dsg).getLocation();
++            dsg = ((DatasetGraphSwitchable)dsg).getWrapped();
++        }
++        if ( dsg instanceof DatasetGraphTDB )
++            locStorage = ((DatasetGraphTDB)dsg).getLocation();
++        
++        DatabaseConnection.internalExpel(locContainer, false);
++        StoreConnection.internalExpel(locStorage, false);
++    }
++
++    /** 
++     * Reset the whole TDB system.      
++     * Use with great care.
++     */
++    public static void reset() {
++        DatabaseConnection.internalReset();
++        StoreConnection.internalReset();
++    }
++
++    public static boolean isBackedByTDB(DatasetGraph datasetGraph) {
++        if ( datasetGraph instanceof DatasetGraphSwitchable )
++            return true;
++        if ( datasetGraph instanceof DatasetGraphTDB )
++            return true;
++        return false;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/Util.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/Util.java
index 0000000,0000000..3ac6336
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/sys/Util.java
@@@ -1,0 -1,0 +1,49 @@@
++/*
++ * 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.seaborne.tdb2.sys;
++
++import java.util.List;
++import java.util.NoSuchElementException;
++
++/** Utility code that should go somewhere */
++public class Util {
++    
++    /** Get the last element of a list, or throw NoSuchElementException
++     * @param list
++     * @return T
++     * @throws NoSuchElementException
++     */
++    public static <T> T xgetLast(List<T> list) {
++        T elt = getLastOrNull(list);
++        if ( elt == null )
++            throw new NoSuchElementException();
++        return elt;  
++    }
++    
++    /**
++     * Get the last element of a list, or return null.
++     * @param list
++     * @return T
++     */
++    public static <T> T getLastOrNull(List<T> list) {
++        if ( list.isEmpty() )
++            return null;
++        return list.get(list.size()-1);  
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/resources/META-INF/LICENSE
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/resources/META-INF/LICENSE
index 0000000,0000000..67db858
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/resources/META-INF/LICENSE
@@@ -1,0 -1,0 +1,175 @@@
++
++                                 Apache License
++                           Version 2.0, January 2004
++                        http://www.apache.org/licenses/
++
++   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
++
++   1. Definitions.
++
++      "License" shall mean the terms and conditions for use, reproduction,
++      and distribution as defined by Sections 1 through 9 of this document.
++
++      "Licensor" shall mean the copyright owner or entity authorized by
++      the copyright owner that is granting the License.
++
++      "Legal Entity" shall mean the union of the acting entity and all
++      other entities that control, are controlled by, or are under common
++      control with that entity. For the purposes of this definition,
++      "control" means (i) the power, direct or indirect, to cause the
++      direction or management of such entity, whether by contract or
++      otherwise, or (ii) ownership of fifty percent (50%) or more of the
++      outstanding shares, or (iii) beneficial ownership of such entity.
++
++      "You" (or "Your") shall mean an individual or Legal Entity
++      exercising permissions granted by this License.
++
++      "Source" form shall mean the preferred form for making modifications,
++      including but not limited to software source code, documentation
++      source, and configuration files.
++
++      "Object" form shall mean any form resulting from mechanical
++      transformation or translation of a Source form, including but
++      not limited to compiled object code, generated documentation,
++      and conversions to other media types.
++
++      "Work" shall mean the work of authorship, whether in Source or
++      Object form, made available under the License, as indicated by a
++      copyright notice that is included in or attached to the work
++      (an example is provided in the Appendix below).
++
++      "Derivative Works" shall mean any work, whether in Source or Object
++      form, that is based on (or derived from) the Work and for which the
++      editorial revisions, annotations, elaborations, or other modifications
++      represent, as a whole, an original work of authorship. For the purposes
++      of this License, Derivative Works shall not include works that remain
++      separable from, or merely link (or bind by name) to the interfaces of,
++      the Work and Derivative Works thereof.
++
++      "Contribution" shall mean any work of authorship, including
++      the original version of the Work and any modifications or additions
++      to that Work or Derivative Works thereof, that is intentionally
++      submitted to Licensor for inclusion in the Work by the copyright owner
++      or by an individual or Legal Entity authorized to submit on behalf of
++      the copyright owner. For the purposes of this definition, "submitted"
++      means any form of electronic, verbal, or written communication sent
++      to the Licensor or its representatives, including but not limited to
++      communication on electronic mailing lists, source code control systems,
++      and issue tracking systems that are managed by, or on behalf of, the
++      Licensor for the purpose of discussing and improving the Work, but
++      excluding communication that is conspicuously marked or otherwise
++      designated in writing by the copyright owner as "Not a Contribution."
++
++      "Contributor" shall mean Licensor and any individual or Legal Entity
++      on behalf of whom a Contribution has been received by Licensor and
++      subsequently incorporated within the Work.
++
++   2. Grant of Copyright License. Subject to the terms and conditions of
++      this License, each Contributor hereby grants to You a perpetual,
++      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
++      copyright license to reproduce, prepare Derivative Works of,
++      publicly display, publicly perform, sublicense, and distribute the
++      Work and such Derivative Works in Source or Object form.
++
++   3. Grant of Patent License. Subject to the terms and conditions of
++      this License, each Contributor hereby grants to You a perpetual,
++      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
++      (except as stated in this section) patent license to make, have made,
++      use, offer to sell, sell, import, and otherwise transfer the Work,
++      where such license applies only to those patent claims licensable
++      by such Contributor that are necessarily infringed by their
++      Contribution(s) alone or by combination of their Contribution(s)
++      with the Work to which such Contribution(s) was submitted. If You
++      institute patent litigation against any entity (including a
++      cross-claim or counterclaim in a lawsuit) alleging that the Work
++      or a Contribution incorporated within the Work constitutes direct
++      or contributory patent infringement, then any patent licenses
++      granted to You under this License for that Work shall terminate
++      as of the date such litigation is filed.
++
++   4. Redistribution. You may reproduce and distribute copies of the
++      Work or Derivative Works thereof in any medium, with or without
++      modifications, and in Source or Object form, provided that You
++      meet the following conditions:
++
++      (a) You must give any other recipients of the Work or
++          Derivative Works a copy of this License; and
++
++      (b) You must cause any modified files to carry prominent notices
++          stating that You changed the files; and
++
++      (c) You must retain, in the Source form of any Derivative Works
++          that You distribute, all copyright, patent, trademark, and
++          attribution notices from the Source form of the Work,
++          excluding those notices that do not pertain to any part of
++          the Derivative Works; and
++
++      (d) If the Work includes a "NOTICE" text file as part of its
++          distribution, then any Derivative Works that You distribute must
++          include a readable copy of the attribution notices contained
++          within such NOTICE file, excluding those notices that do not
++          pertain to any part of the Derivative Works, in at least one
++          of the following places: within a NOTICE text file distributed
++          as part of the Derivative Works; within the Source form or
++          documentation, if provided along with the Derivative Works; or,
++          within a display generated by the Derivative Works, if and
++          wherever such third-party notices normally appear. The contents
++          of the NOTICE file are for informational purposes only and
++          do not modify the License. You may add Your own attribution
++          notices within Derivative Works that You distribute, alongside
++          or as an addendum to the NOTICE text from the Work, provided
++          that such additional attribution notices cannot be construed
++          as modifying the License.
++
++      You may add Your own copyright statement to Your modifications and
++      may provide additional or different license terms and conditions
++      for use, reproduction, or distribution of Your modifications, or
++      for any such Derivative Works as a whole, provided Your use,
++      reproduction, and distribution of the Work otherwise complies with
++      the conditions stated in this License.
++
++   5. Submission of Contributions. Unless You explicitly state otherwise,
++      any Contribution intentionally submitted for inclusion in the Work
++      by You to the Licensor shall be under the terms and conditions of
++      this License, without any additional terms or conditions.
++      Notwithstanding the above, nothing herein shall supersede or modify
++      the terms of any separate license agreement you may have executed
++      with Licensor regarding such Contributions.
++
++   6. Trademarks. This License does not grant permission to use the trade
++      names, trademarks, service marks, or product names of the Licensor,
++      except as required for reasonable and customary use in describing the
++      origin of the Work and reproducing the content of the NOTICE file.
++
++   7. Disclaimer of Warranty. Unless required by applicable law or
++      agreed to in writing, Licensor provides the Work (and each
++      Contributor provides its Contributions) on an "AS IS" BASIS,
++      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
++      implied, including, without limitation, any warranties or conditions
++      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
++      PARTICULAR PURPOSE. You are solely responsible for determining the
++      appropriateness of using or redistributing the Work and assume any
++      risks associated with Your exercise of permissions under this License.
++
++   8. Limitation of Liability. In no event and under no legal theory,
++      whether in tort (including negligence), contract, or otherwise,
++      unless required by applicable law (such as deliberate and grossly
++      negligent acts) or agreed to in writing, shall any Contributor be
++      liable to You for damages, including any direct, indirect, special,
++      incidental, or consequential damages of any character arising as a
++      result of this License or out of the use or inability to use the
++      Work (including but not limited to damages for loss of goodwill,
++      work stoppage, computer failure or malfunction, or any and all
++      other commercial damages or losses), even if such Contributor
++      has been advised of the possibility of such damages.
++
++   9. Accepting Warranty or Additional Liability. While redistributing
++      the Work or Derivative Works thereof, You may choose to offer,
++      and charge a fee for, acceptance of support, warranty, indemnity,
++      or other liability obligations and/or rights consistent with this
++      License. However, in accepting such obligations, You may act only
++      on Your own behalf and on Your sole responsibility, not on behalf
++      of any other Contributor, and only if You agree to indemnify,
++      defend, and hold each Contributor harmless for any liability
++      incurred by, or claims asserted against, such Contributor by reason
++      of your accepting any such warranty or additional liability.

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/resources/META-INF/NOTICE
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/resources/META-INF/NOTICE
index 0000000,0000000..5ba7ec0
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/resources/META-INF/NOTICE
@@@ -1,0 -1,0 +1,10 @@@
++Mantis Database Operating Environment
++
++Copyright 2013-2017 Andy Seaborne
++Copyright 2014, 2015 Epimorphics Ltd.
++
++Portions of this software derive from software from Apache Jena:
++  - Copyright 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Hewlett-Packard Development Company, LP
++  - Copyright 2010, 2011 Epimorphics Ltd.
++  - Copyright 2010, 2011 Talis Systems Ltd.
++These have been licensed to the Apache Software Foundation under a software grant.

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/resources/META-INF/services/org.apache.jena.system.JenaSubsystemLifecycle
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/resources/META-INF/services/org.apache.jena.system.JenaSubsystemLifecycle
index 0000000,0000000..21f02ea
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/resources/META-INF/services/org.apache.jena.system.JenaSubsystemLifecycle
@@@ -1,0 -1,0 +1,1 @@@
++org.seaborne.tdb2.sys.InitTDB2

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/resources/org/apache/jena/tdb/tdb-properties.xml
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/resources/org/apache/jena/tdb/tdb-properties.xml
index 0000000,0000000..67d8255
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/resources/org/apache/jena/tdb/tdb-properties.xml
@@@ -1,0 -1,0 +1,8 @@@
++<?xml version="1.0" encoding="UTF-8"?>
++<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
++<!-- Licensed under the terms of http://www.apache.org/licenses/LICENSE-2.0 -->
++<properties version="1.0">
++  <comment>TDB System Properties</comment>
++  <entry key="org.apache.jena.tdb.version">${project.version}</entry>
++  <entry key="org.apache.jena.tdb.build.datetime">${build.time.xsd}</entry>
++</properties>

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/ConfigTest.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/ConfigTest.java
index 0000000,0000000..fc161fd
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/ConfigTest.java
@@@ -1,0 -1,0 +1,95 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2;
++
++import org.apache.jena.atlas.lib.FileOps ;
++import org.seaborne.tdb2.sys.SystemTDB ;
++
++public class ConfigTest
++{
++    private static String testingDataRoot = "testing" ;
++    // Place under target
++    private static final String testingDir = "target/tdb-testing" ;
++    private static final String testingDirDB = "target/tdb-testing/DB" ;
++    static boolean nonDeleteableMMapFiles = SystemTDB.isWindows ;
++    
++    static boolean initialized = false ; 
++    
++    private static void init()
++    {
++        FileOps.ensureDir("target") ;
++        FileOps.ensureDir(testingDir) ;
++        FileOps.ensureDir(testingDirDB) ;
++        initialized = true ;
++    }
++    
++    private static int count = 0 ;
++
++    public static void setTestingDataRoot(String dir) { testingDataRoot = dir ; }
++    public static String getTestingDataRoot() { return testingDataRoot ; }
++    
++    /** return a directory */ 
++    public static final String getCleanDir() {
++        init() ;
++        String dir = nonDeleteableMMapFiles ? getTestingDirUnique() : getTestingDirDB() ;
++        FileOps.ensureDir(dir); 
++        FileOps.clearDirectory(dir) ;
++        return dir ;
++    }
++    /** Get a empty directory name that has not been used before in this JVM */
++    
++    private static final String getTestingDirUnique()
++    {
++        init() ;
++    	String dn = testingDir+"/D-"+(++count) ;
++    	FileOps.ensureDir(dn) ;
++    	FileOps.clearDirectory(dn) ;
++    	return dn ; 
++    }
++    
++    public static final String getTestingDir()
++    {
++        init() ;
++        return testingDir ;
++    }
++    
++    public static final void deleteTestingDir()
++    {
++        if ( ! FileOps.exists(testingDir) )
++            return ;
++        deleteTestingDirDB() ;
++        FileOps.clearDirectory(testingDir) ;
++        FileOps.deleteSilent(testingDir) ;
++    }
++
++    public static final String getTestingDirDB()
++    {
++        init() ;
++        FileOps.ensureDir(testingDirDB) ;
++        return testingDirDB ;
++    }
++    
++    public static final void deleteTestingDirDB()
++    {
++        if ( ! FileOps.exists(testingDirDB) )
++            return ;
++        FileOps.clearDirectory(testingDirDB) ;
++        FileOps.deleteSilent(testingDirDB) ;
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/TC_TDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/TC_TDB.java
index 0000000,0000000..8d1a695
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/TC_TDB.java
@@@ -1,0 -1,0 +1,80 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2;
++
++import org.apache.jena.sparql.engine.optimizer.reorder.ReorderLib ;
++import org.apache.jena.sparql.engine.optimizer.reorder.ReorderTransformation ;
++import org.apache.log4j.Level ;
++import org.apache.log4j.Logger ;
++import org.junit.AfterClass ;
++import org.junit.BeforeClass ;
++import org.junit.runner.RunWith ;
++import org.junit.runners.Suite ;
++import org.seaborne.dboe.base.block.FileMode ;
++import org.seaborne.tdb2.assembler.TS_TDBAssembler ;
++import org.seaborne.tdb2.graph.TS_Graph ;
++import org.seaborne.tdb2.lib.TS_LibTDB ;
++import org.seaborne.tdb2.setup.TS_TDBSetup ;
++import org.seaborne.tdb2.solver.TS_SolverTDB ;
++import org.seaborne.tdb2.store.TS_Store ;
++import org.seaborne.tdb2.store.nodetable.TS_NodeTable ;
++import org.seaborne.tdb2.store.tupletable.TS_TupleTable ;
++import org.seaborne.tdb2.sys.SystemTDB ;
++import org.seaborne.tdb2.sys.TS_Sys ;
++
++// Naming conventions.
++// TS_* - Test sets: collections of testing files (Often Test*)
++// TC_*  - Test collection: sets of TS's and TC's.
++
++@RunWith(Suite.class)
++@Suite.SuiteClasses( {
++    // TDB
++    TS_LibTDB.class
++    , TS_NodeTable.class
++    , TS_TupleTable.class
++    , TS_TDBSetup.class
++    , TS_Store.class
++    , TS_SolverTDB.class
++    , TS_Graph.class
++    , TS_Factory.class
++    , TS_TDBAssembler.class
++    , TS_Sys.class
++} )
++
++public class TC_TDB
++{
++    static {
++        if ( false )
++            SystemTDB.setFileMode(FileMode.direct) ;
++    }
++    static ReorderTransformation dftReorder = null ; 
++        
++    @BeforeClass static public void beforeClass()   
++    {
++        //org.apache.log4j.LogManager.resetConfiguration() ;
++        //org.apache.log4j.PropertyConfigurator.configure("log4j.properties") ;
++        Logger.getLogger("org.apache.jena.tdb.info").setLevel(Level.WARN) ;
++        //Logger.getLogger("org.apache.jena.tdb.exec").setLevel(Level.WARN) ;
++        dftReorder = SystemTDB.defaultReorderTransform ;
++        SystemTDB.defaultReorderTransform = ReorderLib.identity() ;
++    }
++    
++    @AfterClass static public void afterClass() {
++        SystemTDB.defaultReorderTransform = dftReorder ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/TS_Factory.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/TS_Factory.java
index 0000000,0000000..b6dc979
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/TS_Factory.java
@@@ -1,0 -1,0 +1,32 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2;
++
++
++import org.junit.runner.RunWith;
++import org.junit.runners.Suite;
++
++@RunWith(Suite.class)
++@Suite.SuiteClasses( {
++    TestDatabaseMgr.class
++    , TestTDBFactory.class
++
++})
++
++public class TS_Factory
++{}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseMgr.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseMgr.java
index 0000000,0000000..da75494
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseMgr.java
@@@ -1,0 -1,0 +1,89 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2;
++
++import static org.junit.Assert.*;
++import org.apache.jena.atlas.lib.FileOps ;
++import org.apache.jena.sparql.core.DatasetGraph ;
++import org.apache.jena.sparql.core.Quad ;
++import org.apache.jena.sparql.sse.SSE ;
++import org.junit.Test ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.dboe.jenax.Txn ;
++import org.seaborne.tdb2.sys.TDBInternal ;
++
++/** Test of DatabaseMgr - the DatasetGraph level API to TDB2 **/
++public class TestDatabaseMgr
++{
++//    String DIRx = ConfigTest.getCleanDir() ;
++//    Location DIR = Location.create(DIRx);
++    
++    static Quad quad1 = SSE.parseQuad("(_ _:a <p> 1)") ;
++    static Quad quad2 = SSE.parseQuad("(_ <s> <p> 1)") ;
++    
++    @Test
++    public void testDatabaseMgr1() {
++        TDBInternal.reset() ; 
++        DatasetGraph dg1 = DatabaseMgr.connectDatasetGraph(Location.mem("FOO")) ;
++        DatasetGraph dg2 = DatabaseMgr.connectDatasetGraph(Location.mem("FOO")) ;
++        Txn.executeWrite(dg1, ()->{
++            dg1.add(quad1) ;
++        }) ;
++        Txn.executeRead(dg2, ()->{
++            assertTrue(dg2.contains(quad1)) ;
++        }) ;
++    }
++    
++    @Test
++    public void testDatabaseMgr2() {
++        TDBInternal.reset() ;
++        // The unnamed location is unique each time.
++        DatasetGraph dg1 = DatabaseMgr.connectDatasetGraph(Location.mem()) ;
++        DatasetGraph dg2 = DatabaseMgr.connectDatasetGraph(Location.mem()) ;
++        Txn.executeWrite(dg1, ()->{
++            dg1.add(quad1) ;    
++        }) ;
++        Txn.executeRead(dg2, ()->{
++            assertFalse(dg2.contains(quad1)) ;
++        }) ;
++    }
++
++    @Test
++    public void testDatabaseMgrDisk() {
++        TDBInternal.reset() ;
++        // The named disk location
++        String DIRx = ConfigTest.getCleanDir() ;
++        Location LOC = Location.create(DIRx) ;
++        FileOps.clearDirectory(DIRx) ;
++        try {
++            DatasetGraph dg1 = DatabaseMgr.connectDatasetGraph(LOC) ;
++            DatasetGraph dg2 = DatabaseMgr.connectDatasetGraph(Location.create(LOC.getDirectoryPath())) ;
++            assertSame(dg1, dg2) ;
++            Txn.executeWrite(dg1, ()-> {
++                dg1.add(quad1) ;
++            }) ;
++            Txn.executeRead(dg2, ()-> {
++                assertTrue(dg2.contains(quad1)) ;
++            }) ;
++        }
++        finally {
++            FileOps.clearDirectory(DIRx) ;
++        }
++
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/TestTDBFactory.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/TestTDBFactory.java
index 0000000,0000000..752a95c
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/TestTDBFactory.java
@@@ -1,0 -1,0 +1,130 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2;
++
++import static org.junit.Assert.*;
++import org.apache.jena.atlas.lib.FileOps ;
++import org.apache.jena.datatypes.xsd.XSDDatatype ;
++import org.apache.jena.query.Dataset ;
++import org.apache.jena.rdf.model.Literal ;
++import org.apache.jena.rdf.model.Property ;
++import org.apache.jena.rdf.model.Resource ;
++import org.apache.jena.rdf.model.ResourceFactory ;
++import org.junit.Test ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.dboe.jenax.Txn ;
++import org.seaborne.tdb2.sys.TDBInternal ;
++
++/** Test of TDB2Factory - the Dataset level API to TDB2 **/
++public class TestTDBFactory
++{
++    static Resource s1 = ResourceFactory.createResource("http://example/s2");
++    static Resource s2 = ResourceFactory.createResource();
++    static Property p  =  ResourceFactory.createProperty("http://example/ns#", "p");
++    static Literal  o1 =  ResourceFactory.createPlainLiteral("object");
++    static Literal  o2 =  ResourceFactory.createTypedLiteral("1", XSDDatatype.XSDinteger);
++    
++    @Test
++    public void testTDBFactory1() {
++        TDBInternal.reset() ;
++        Dataset ds = TDB2Factory.connectDataset(Location.mem()) ;
++        test(ds);
++    }
++    
++    @Test
++    public void testTDBFactory2() {
++        TDBInternal.reset() ;
++        Dataset ds = TDB2Factory.connectDataset(Location.mem("MEMORY")) ;
++        test(ds);
++    }
++    
++    @Test
++    public void testTDBFactory3() {
++        TDBInternal.reset() ;
++        // Only do disk things for tests that need them (disk takes time!).
++        String DIRx = ConfigTest.getCleanDir() ;
++        Location DIR = Location.create(DIRx);
++        try { 
++            FileOps.clearDirectory(DIRx) ;
++            Dataset ds = TDB2Factory.connectDataset(DIR) ;
++            test(ds);
++        } finally { FileOps.clearDirectory(DIRx) ; }
++    }
++    
++    @Test
++    public void testTDBFactory2DS_1() {
++        TDBInternal.reset() ;
++        Dataset ds1 = TDB2Factory.connectDataset(Location.mem("FOO")) ;
++        Dataset ds2 = TDB2Factory.connectDataset(Location.mem("FOO")) ;
++        Txn.executeWrite(ds1, ()->{
++            ds1.getDefaultModel().add(s1, p, o1);
++        });
++        Txn.executeRead(ds2, ()->{
++            assertTrue(ds2.getDefaultModel().contains(s1, p, o1));
++        });
++    }
++    
++    @Test
++    public void testTDBFactory2DS_2() {
++        TDBInternal.reset() ;
++        // The unnamed location is unique each time.
++        Dataset ds1 = TDB2Factory.connectDataset(Location.mem()) ;
++        Dataset ds2 = TDB2Factory.connectDataset(Location.mem()) ;
++        Txn.executeWrite(ds1, ()->{
++            ds1.getDefaultModel().add(s1, p, o1);
++        });
++        Txn.executeRead(ds2, ()->{
++            assertFalse(ds2.getDefaultModel().contains(s1, p, o1));
++        });
++    }
++    
++    @Test
++    public void testTDBFactory2DS_3() {
++        TDBInternal.reset() ;
++        TDBInternal.reset() ;
++        String DIRx = ConfigTest.getCleanDir() ;
++        Location DIR = Location.create(DIRx);
++        try {
++            Dataset ds1 = TDB2Factory.connectDataset(DIR) ;
++            Dataset ds2 = TDB2Factory.connectDataset(DIR) ;
++            Txn.executeWrite(ds1, ()->{
++                ds1.getDefaultModel().add(s1, p, o1);
++            });
++            Txn.executeRead(ds2, ()->{
++                assertTrue(ds2.getDefaultModel().contains(s1, p, o1));
++            });
++        } finally { FileOps.clearDirectory(DIRx) ; }
++    }
++
++    private static void test(Dataset ds) {
++        Txn.executeWrite(ds, ()->{
++            ds.getDefaultModel().add(s1, p, o1);
++        });
++        Txn.executeRead(ds, ()->{
++            assertTrue(ds.getDefaultModel().contains(s1, p, o1));
++        });
++        Txn.executeWrite(ds, ()->{
++            ds.getDefaultModel().remove(s1, p, o1);
++            ds.getDefaultModel().add(s2, p, o2);
++        });
++        Txn.executeRead(ds, ()->{
++            assertFalse(ds.getDefaultModel().contains(s1, p, o1));
++            assertTrue(ds.getDefaultModel().contains(s2, p, o2));
++        });
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/assembler/TS_TDBAssembler.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/assembler/TS_TDBAssembler.java
index 0000000,0000000..d6f622a
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/assembler/TS_TDBAssembler.java
@@@ -1,0 -1,0 +1,32 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.assembler;
++
++import org.junit.runner.RunWith ;
++import org.junit.runners.Suite ;
++import org.seaborne.tdb2.junit.Base_TS ;
++
++@RunWith(Suite.class)
++@Suite.SuiteClasses( {
++     TestTDBAssembler.class
++})
++
++public class TS_TDBAssembler extends Base_TS
++{
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/assembler/TestTDBAssembler.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/assembler/TestTDBAssembler.java
index 0000000,0000000..f1e13e2
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/assembler/TestTDBAssembler.java
@@@ -1,0 -1,0 +1,142 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.assembler;
++
++import org.apache.jena.assembler.JA ;
++import org.apache.jena.assembler.exceptions.AssemblerException ;
++import static org.junit.Assert.*;
++import org.apache.jena.atlas.lib.FileOps ;
++import org.apache.jena.graph.Graph ;
++import org.apache.jena.query.Dataset ;
++import org.apache.jena.rdf.model.Model ;
++import org.apache.jena.rdf.model.Resource ;
++import org.apache.jena.sparql.core.assembler.AssemblerUtils ;
++import org.apache.jena.sparql.core.assembler.DatasetAssemblerVocab ;
++import org.junit.AfterClass ;
++import org.junit.Before ;
++import org.junit.BeforeClass ;
++import org.junit.Test ;
++import org.seaborne.tdb2.ConfigTest ;
++import org.seaborne.tdb2.store.DatasetGraphSwitchable ;
++import org.seaborne.tdb2.store.GraphViewSwitchable ;
++import org.seaborne.tdb2.sys.TDBInternal ;
++
++public class TestTDBAssembler
++{
++    // Can be slow - explicitly closes the dataset.
++    static String dirAssem      = null ;
++    static final String dirDB   = ConfigTest.getTestingDir()+"/DB" ;
++
++    @BeforeClass
++    static public void beforeClass() {
++        dirAssem = ConfigTest.getTestingDataRoot() + "/Assembler" ;
++        FileOps.ensureDir(dirDB) ;
++    }
++
++    @Before
++    public void before() {
++        TDBInternal.reset() ;
++        FileOps.clearDirectory(dirDB) ;
++    }
++
++    @AfterClass
++    static public void afterClass() {
++        TDBInternal.reset() ;
++        FileOps.clearDirectory(dirDB) ;
++    }
++
++    @Test
++    public void createDatasetDirect() {
++        createTest(dirAssem + "/tdb-dataset.ttl", VocabTDB2.tDatasetTDB) ;
++    }
++
++    @Test
++    public void createDatasetEmbed() {
++        createTest(dirAssem + "/tdb-dataset-embed.ttl", DatasetAssemblerVocab.tDataset) ;
++    }
++
++    private void createTest(String filename, Resource type) {
++        Object thing = AssemblerUtils.build(filename, type) ;
++        assertTrue(thing instanceof Dataset) ;
++        Dataset ds = (Dataset)thing ;
++        assertTrue(ds.asDatasetGraph() instanceof DatasetGraphSwitchable) ;
++        assertTrue(ds.supportsTransactions()) ;
++        ds.close() ;
++    }
++
++    @Test
++    public void createGraphDirect() {
++        testGraph(dirAssem + "/tdb-graph.ttl", false) ;
++    }
++
++    @Test
++    public void createGraphEmbed() {
++        String f = dirAssem + "/tdb-graph-embed.ttl" ;
++        Object thing = null ;
++        try {
++            thing = AssemblerUtils.build(f, JA.Model) ;
++        }
++        catch (AssemblerException e) {
++            e.getCause().printStackTrace(System.err) ;
++            throw e ;
++        }
++
++        assertTrue(thing instanceof Model) ;
++        Graph graph = ((Model)thing).getGraph() ;
++        assertTrue(graph instanceof GraphViewSwitchable) ;
++
++        DatasetGraphSwitchable ds = ((GraphViewSwitchable)graph).getDataset() ;
++        if ( ds != null )
++            ds.close() ;
++    }
++
++    @Test
++    public void createNamedGraph1() {
++        testGraph(dirAssem + "/tdb-named-graph-1.ttl", true) ;
++    }
++
++    @Test
++    public void createNamedGraph2() {
++        testGraph(dirAssem + "/tdb-named-graph-2.ttl", true) ;
++    }
++
++    @Test
++    public void createNamedGraphViaDataset() {
++        testGraph(dirAssem + "/tdb-graph-ref-dataset.ttl", false) ;
++    }
++
++    private static void testGraph(String assemblerFile, boolean named) {
++        Object thing = null ;
++        try {
++            thing = AssemblerUtils.build(assemblerFile, VocabTDB2.tGraphTDB) ;
++        }
++        catch (AssemblerException e) {
++            e.getCause().printStackTrace(System.err) ;
++            throw e ;
++        }
++
++        assertTrue(thing instanceof Model) ;
++        Graph graph = ((Model)thing).getGraph() ;
++
++        assertTrue(graph instanceof GraphViewSwitchable) ;
++
++        DatasetGraphSwitchable dsg = ((GraphViewSwitchable)graph).getDataset();
++        if ( dsg != null )
++            dsg.close() ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/graph/AbstractTestGraphsTDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/graph/AbstractTestGraphsTDB.java
index 0000000,0000000..762e8ee
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/graph/AbstractTestGraphsTDB.java
@@@ -1,0 -1,0 +1,50 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.graph;
++
++import org.apache.jena.sparql.engine.optimizer.reorder.ReorderLib ;
++import org.apache.jena.sparql.engine.optimizer.reorder.ReorderTransformation ;
++import org.apache.jena.sparql.graph.GraphsTests ;
++import org.junit.AfterClass ;
++import org.junit.BeforeClass ;
++import org.junit.Ignore ;
++import org.junit.Test ;
++import org.seaborne.tdb2.sys.SystemTDB ;
++
++public abstract class AbstractTestGraphsTDB extends GraphsTests
++{
++    private static ReorderTransformation reorder  ;
++    
++    @BeforeClass public static void setupClass()
++    {
++        reorder = SystemTDB.defaultReorderTransform ;
++        SystemTDB.defaultReorderTransform = ReorderLib.identity() ;
++    }
++    
++    @AfterClass public static void afterClass() {  SystemTDB.defaultReorderTransform = reorder ; }
++
++    // These don't pass ... not quite clear if the test is right.  Investigate.
++    
++    @Override
++    @Ignore @Test public void graph_count5() {} 
++    
++    @Override
++    @Ignore @Test public void graph_count6() {} 
++    
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/graph/TS_Graph.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/graph/TS_Graph.java
index 0000000,0000000..b6dbdea
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/graph/TS_Graph.java
@@@ -1,0 -1,0 +1,35 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.graph;
++
++import org.junit.runner.RunWith ;
++import org.junit.runners.Suite ;
++
++@RunWith(Suite.class)
++@Suite.SuiteClasses( {
++    TestPrefixMappingTDB.class
++    , TestDatasetGraphTDB.class
++    , TestGraphsTDB1.class
++    , TestGraphsTDB2.class
++    , TestDatasetGraphAccessorTDB.class
++    , TestGraphOverDatasetTDB.class
++})
++public class TS_Graph
++{
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/graph/TestDatasetGraphAccessorTDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/graph/TestDatasetGraphAccessorTDB.java
index 0000000,0000000..8184154
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/graph/TestDatasetGraphAccessorTDB.java
@@@ -1,0 -1,0 +1,47 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.graph;
++
++import org.apache.jena.query.DatasetAccessorFactory ;
++import org.apache.jena.query.ReadWrite ;
++import org.apache.jena.sparql.core.DatasetGraph ;
++import org.apache.jena.web.AbstractTestDatasetGraphAccessor ;
++import org.apache.jena.web.DatasetGraphAccessor ;
++import org.junit.After ;
++import org.junit.Before ;
++import org.seaborne.tdb2.junit.TL ;
++
++public class TestDatasetGraphAccessorTDB extends AbstractTestDatasetGraphAccessor
++{
++    DatasetGraph dsg = TL.createTestDatasetGraphMem() ;
++    @Before public void before() {
++        dsg.begin(ReadWrite.WRITE);
++    }
++    
++    @After public void after() {
++        dsg.abort();
++        dsg.end();
++        TL.expel(dsg);
++    }
++
++    @Override
++    protected DatasetGraphAccessor getDatasetUpdater()
++    {
++        return DatasetAccessorFactory.make(dsg) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/graph/TestDatasetGraphTDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/graph/TestDatasetGraphTDB.java
index 0000000,0000000..0cd2bca
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/graph/TestDatasetGraphTDB.java
@@@ -1,0 -1,0 +1,52 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.graph;
++
++import org.apache.jena.query.ReadWrite ;
++import org.apache.jena.sparql.core.AbstractDatasetGraphTests ;
++import org.apache.jena.sparql.core.DatasetGraph ;
++import org.junit.After ;
++import org.junit.Before ;
++import org.junit.Test ;
++import org.seaborne.tdb2.junit.TL ;
++
++// Quad tests
++public class TestDatasetGraphTDB extends AbstractDatasetGraphTests
++{
++    DatasetGraph dsg = TL.createTestDatasetGraphMem() ;
++    @Before public void before() {
++        dsg.begin(ReadWrite.WRITE);
++    }
++    
++    @After public void after() {
++        dsg.abort();
++        dsg.end();
++        TL.expel(dsg);
++    }
++    
++    @Override
++    protected DatasetGraph emptyDataset() {
++        return dsg ;
++    }
++
++    @Override
++    // Empty graph tests: N/A
++    @Test public void graph_01()
++    {}
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/graph/TestGraphOverDatasetTDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/graph/TestGraphOverDatasetTDB.java
index 0000000,0000000..de01a86
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/graph/TestGraphOverDatasetTDB.java
@@@ -1,0 -1,0 +1,63 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.graph;
++
++import org.apache.jena.graph.Graph ;
++import org.apache.jena.graph.Node ;
++import org.apache.jena.query.ReadWrite ;
++import org.apache.jena.sparql.core.AbstractTestGraphOverDataset ;
++import org.apache.jena.sparql.core.DatasetGraph ;
++import org.junit.After ;
++import org.seaborne.tdb2.junit.TL ;
++
++/** This is the view-graph test suite run over a TDB DatasetGraph to check compatibility */
++public class TestGraphOverDatasetTDB extends AbstractTestGraphOverDataset
++{
++    DatasetGraph dsg = null;
++    @After public void after2() {
++        if ( dsg == null )
++            return;
++        dsg.abort();
++        dsg.end();
++        TL.expel(dsg);
++    }
++    
++    @Override
++    protected DatasetGraph createBaseDSG() {
++        // Called in initialization.
++        if ( dsg == null ) {
++            dsg = TL.createTestDatasetGraphMem() ;
++            dsg.begin(ReadWrite.WRITE);
++        }
++        return dsg ;
++    }
++    
++    @Override
++    protected Graph makeNamedGraph(DatasetGraph dsg, Node gn)
++    {
++        return dsg.getGraph(gn) ;
++    }
++
++    @Override
++    protected Graph makeDefaultGraph(DatasetGraph dsg)
++    {
++        return  dsg.getDefaultGraph() ;
++    }
++
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/graph/TestGraphsTDB1.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/graph/TestGraphsTDB1.java
index 0000000,0000000..3f703e5
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/graph/TestGraphsTDB1.java
@@@ -1,0 -1,0 +1,42 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.graph;
++
++import org.apache.jena.query.Dataset ;
++import org.apache.jena.query.ReadWrite ;
++import org.junit.After ;
++import org.junit.Before ;
++import org.seaborne.tdb2.junit.TL ;
++
++public class TestGraphsTDB1 extends AbstractTestGraphsTDB
++{
++    Dataset ds = TL.createTestDatasetMem();
++    @Before public void before() {
++        ds.begin(ReadWrite.WRITE);
++    }
++    
++    @After public void after() {
++        ds.abort();
++        ds.end();
++        TL.expel(ds);
++    }
++    @Override
++    protected Dataset createDataset() {
++        return ds ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/graph/TestGraphsTDB2.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/graph/TestGraphsTDB2.java
index 0000000,0000000..ba665f8
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/graph/TestGraphsTDB2.java
@@@ -1,0 -1,0 +1,51 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.graph;
++
++import org.apache.jena.query.Dataset ;
++import org.apache.jena.query.ReadWrite ;
++import org.junit.After ;
++import org.junit.Before ;
++import org.seaborne.dboe.jenax.Txn ;
++import org.seaborne.tdb2.TDB2Factory ;
++
++public class TestGraphsTDB2 extends AbstractTestGraphsTDB
++{
++    // Transactional.
++    @Before
++    public void before() {
++        getDataset().begin(ReadWrite.READ) ;
++    }
++
++    @After
++    public void after() {
++        getDataset().end() ;
++    }
++
++    @Override
++    protected void fillDataset(Dataset dataset) {
++        Txn.executeWrite(dataset, ()->{
++            super.fillDataset(dataset) ;
++        });
++    }
++    
++    @Override
++    protected Dataset createDataset() {
++        return TDB2Factory.createDataset() ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/graph/TestPrefixMappingTDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/graph/TestPrefixMappingTDB.java
index 0000000,0000000..7deaea4
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/graph/TestPrefixMappingTDB.java
@@@ -1,0 -1,0 +1,106 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.graph;
++
++import org.apache.jena.atlas.lib.FileOps ;
++import org.apache.jena.shared.PrefixMapping ;
++import org.apache.jena.sparql.core.DatasetPrefixStorage ;
++import org.apache.jena.sparql.graph.AbstractTestPrefixMapping2 ;
++import org.junit.* ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.tdb2.ConfigTest ;
++import org.seaborne.tdb2.junit.BuildTestLib ;
++import org.seaborne.tdb2.sys.TDBInternal ;
++
++public class TestPrefixMappingTDB extends AbstractTestPrefixMapping2
++{
++    static DatasetPrefixStorage last = null ;
++    
++    @BeforeClass public static void beforeClass() {}
++    @AfterClass public static void afterClass()   { TDBInternal.reset() ; ConfigTest.deleteTestingDir() ; }
++
++    @Before public void before() { TDBInternal.reset() ; }
++    @After public  void after()  { }
++
++    
++    @Override
++    protected PrefixMapping create() {
++        last = createTestingMem() ;
++        return view() ;
++    }
++
++    static DatasetPrefixStorage createTestingMem() { 
++        return createTesting(Location.mem()) ;
++    }
++    
++    static DatasetPrefixStorage createTesting(Location location) {
++        return BuildTestLib.makePrefixes(location) ;
++    }
++
++    @Override
++    protected PrefixMapping view() {
++        return last.getPrefixMapping() ; 
++    }
++
++    @Test public void multiple1() {
++        DatasetPrefixStorage prefixes = createTestingMem() ;
++        PrefixMapping pmap1 = prefixes.getPrefixMapping() ;
++        PrefixMapping pmap2 = prefixes.getPrefixMapping("http://graph/") ;
++        pmap1.setNsPrefix("x", "http://foo/") ;
++        assertNull(pmap2.getNsPrefixURI("x")) ;
++        assertNotNull(pmap1.getNsPrefixURI("x")) ;
++    }
++    
++    @Test public void multiple2() {
++        DatasetPrefixStorage prefixes = createTestingMem() ;
++        PrefixMapping pmap1 = prefixes.getPrefixMapping("http://graph/") ;  // Same
++        PrefixMapping pmap2 = prefixes.getPrefixMapping("http://graph/") ;
++        pmap1.setNsPrefix("x", "http://foo/") ;
++        assertNotNull(pmap2.getNsPrefixURI("x")) ;
++        assertNotNull(pmap1.getNsPrefixURI("x")) ;
++    }
++    
++    // Persistent.
++    @Test
++    public void persistent1() {
++        String dir = ConfigTest.getTestingDir() ;
++        FileOps.clearDirectory(dir) ;
++
++        DatasetPrefixStorage prefixes = createTesting(Location.create(dir)) ;
++        PrefixMapping pmap1 = prefixes.getPrefixMapping() ;
++
++        String x = pmap1.getNsPrefixURI("x") ;
++        assertNull(x) ;
++        prefixes.close() ;
++    }
++    
++    // Persistent.
++    @Test
++    public void persistent2() {
++        String dir = ConfigTest.getTestingDir() ;
++        FileOps.clearDirectory(dir) ;
++
++        DatasetPrefixStorage prefixes = createTesting(Location.create(dir)) ;
++        PrefixMapping pmap1 = prefixes.getPrefixMapping() ;
++
++        pmap1.setNsPrefix("x", "http://foo/") ;
++        assertEquals("http://foo/", pmap1.getNsPrefixURI("x")) ;
++        prefixes.close() ;
++    }
++    
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/junit/AbstractTestGraphTDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/junit/AbstractTestGraphTDB.java
index 0000000,0000000..bac78a5
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/junit/AbstractTestGraphTDB.java
@@@ -1,0 -1,0 +1,33 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.junit;
++
++import org.apache.jena.graph.test.AbstractTestGraph ;
++
++public abstract class AbstractTestGraphTDB extends AbstractTestGraph
++{
++    public AbstractTestGraphTDB(String name)
++    {
++        super(name) ;
++    }
++    
++    @Override public void testIsomorphismFile() {}
++    
++    // Meaning less - graphs (and hence models) are not indpenedent of their datasets. 
++    @Override public void testCloseSetsIsClosed() {}
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/junit/Base_TS.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/junit/Base_TS.java
index 0000000,0000000..ce2eda1
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/junit/Base_TS.java
@@@ -1,0 -1,0 +1,51 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.junit;
++
++import org.apache.jena.sparql.engine.optimizer.reorder.ReorderLib ;
++import org.apache.jena.sparql.engine.optimizer.reorder.ReorderTransformation ;
++import org.apache.log4j.Level ;
++import org.apache.log4j.Logger ;
++import org.junit.AfterClass ;
++import org.junit.BeforeClass ;
++import org.seaborne.tdb2.sys.SystemTDB ;
++
++public class Base_TS
++{
++    static Level level = null ;
++    static ReorderTransformation rt = null ;
++    
++    @BeforeClass static public void beforeClass()   
++    {
++        rt = SystemTDB.defaultReorderTransform ;
++        level = Logger.getLogger("org.apache.jena.tdb.info").getLevel() ;
++        Logger.getLogger("org.apache.jena.tdb.info").setLevel(Level.FATAL) ;
++        Logger.getLogger("org.seaborne.tdb2.info").setLevel(Level.FATAL) ;
++        SystemTDB.defaultReorderTransform = ReorderLib.identity() ;
++        rt = SystemTDB.defaultReorderTransform ;
++    }
++    
++    @AfterClass static public void afterClass()
++    {
++        if ( level != null ) {
++            Logger.getLogger("org.apache.jena.tdb.info").setLevel(level) ;
++            Logger.getLogger("org.seaborne.tdb2.info").setLevel(level) ;
++        }
++        SystemTDB.defaultReorderTransform = rt ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/junit/BuildTestLib.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/junit/BuildTestLib.java
index 0000000,0000000..171232e
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/junit/BuildTestLib.java
@@@ -1,0 -1,0 +1,81 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.junit;
++
++import org.apache.jena.query.ReadWrite ;
++import org.apache.jena.sparql.core.DatasetPrefixStorage ;
++import org.seaborne.dboe.base.file.* ;
++import org.seaborne.dboe.base.record.RecordFactory ;
++import org.seaborne.dboe.index.Index ;
++import org.seaborne.dboe.index.IndexParams ;
++import org.seaborne.dboe.index.RangeIndex ;
++import org.seaborne.dboe.trans.bplustree.BPlusTree ;
++import org.seaborne.dboe.trans.bplustree.BPlusTreeFactory ;
++import org.seaborne.tdb2.setup.StoreParams ;
++import org.seaborne.tdb2.setup.TDBBuilder ;
++import org.seaborne.tdb2.store.DatasetGraphTDB ;
++import org.seaborne.tdb2.store.nodetable.NodeTable ;
++import org.seaborne.tdb2.store.nodetable.NodeTableCache ;
++import org.seaborne.tdb2.store.nodetable.NodeTableInline ;
++import org.seaborne.tdb2.store.nodetable.NodeTableTRDF ;
++import org.seaborne.tdb2.sys.SystemTDB ;
++
++/** Build things for non-transactional tests.
++ * Sometimes, create a daatset and find the relevant part. 
++ */
++public class BuildTestLib {
++
++    public static RangeIndex buildRangeIndex(FileSet mem, RecordFactory factory, IndexParams indexParams) {
++        BPlusTree bpt = BPlusTreeFactory.makeMem(5, factory.keyLength(), factory.valueLength()) ;
++        bpt.nonTransactional() ;
++        return bpt ; 
++    }
++
++    public static NodeTable makeNodeTable(Location location, String basename, StoreParams params) {
++        NodeTable nt = makeNodeTableBase(location, basename, params) ;
++        nt = NodeTableCache.create(nt, params) ;
++        nt = NodeTableInline.create(nt) ;
++        return nt ;
++    }
++    
++    public static NodeTable makeNodeTableBase(Location location, String basename, StoreParams params) {
++        RecordFactory recordFactory = new RecordFactory(SystemTDB.LenNodeHash, SystemTDB.SizeOfNodeId) ;
++        FileSet fs = new FileSet(location, basename) ;
++                
++        Index index = buildRangeIndex(fs, recordFactory, params) ;
++        BinaryDataFile bdf = createBinaryDataFile(location, basename+"-data") ;
++        NodeTable nt = new NodeTableTRDF(index, bdf) ;
++        return nt ;
++    }
++
++    public static DatasetPrefixStorage makePrefixes(Location location) {
++        DatasetGraphTDB ds = (DatasetGraphTDB)TDBBuilder.build(location) ;
++        ds.begin(ReadWrite.WRITE);
++        return ds.getPrefixes() ; 
++    }
++    
++    /** Create a non-thread-safe BinaryDataFile*/ 
++    public static BinaryDataFile createBinaryDataFile(Location loc, String name) {
++        if ( loc.isMem() )
++            return new BinaryDataFileMem() ;
++        String filename = loc.getPath(name) ;
++        return new BinaryDataFileRandomAccess(filename) ;
++    }
++
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/junit/QueryTestTDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/junit/QueryTestTDB.java
index 0000000,0000000..ef16ed9
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/junit/QueryTestTDB.java
@@@ -1,0 -1,0 +1,192 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.junit;
++
++import java.util.List ;
++
++import org.apache.jena.query.* ;
++import org.apache.jena.rdf.model.Model ;
++import org.apache.jena.sparql.SystemARQ ;
++import org.apache.jena.sparql.engine.QueryEngineFactory ;
++import org.apache.jena.sparql.engine.QueryExecutionBase ;
++import org.apache.jena.sparql.engine.ref.QueryEngineRef ;
++import org.apache.jena.sparql.junit.EarlReport ;
++import org.apache.jena.sparql.junit.EarlTestCase ;
++import org.apache.jena.sparql.junit.TestItem ;
++import org.apache.jena.sparql.resultset.ResultSetCompare ;
++import org.apache.jena.sparql.resultset.SPARQLResult ;
++import org.apache.jena.util.FileManager ;
++import org.seaborne.dboe.jenax.Txn ;
++import org.seaborne.tdb2.TDB2Factory ;
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++public class QueryTestTDB extends EarlTestCase
++{
++    // Changed to using in-memory graphs/datasets because this is testing the query
++    // processing.  Physical graph/datsets is in package "store". 
++    
++    private static Logger log = LoggerFactory.getLogger(QueryTestTDB.class) ;
++    private Dataset dataset = null ;
++
++    boolean skipThisTest = false ;
++
++    final List<String> defaultGraphURIs ;
++    final List<String> namedGraphURIs ;
++    final String queryFile ; 
++    final SPARQLResult results ;
++    
++    // Track what's currently loaded in the GraphLocation
++    private static List<String> currentDefaultGraphs = null ;
++    private static List<String> currentNamedGraphs = null ;
++
++    // Old style (Junit3)
++    public QueryTestTDB(String testName, EarlReport report, TestItem item)
++    {
++        this(testName, report, item.getURI(), 
++             item.getDefaultGraphURIs(), item.getNamedGraphURIs(), 
++             item.getResults(), item.getQueryFile()
++             ) ;
++    }
++    
++    public QueryTestTDB(String testName, EarlReport report, 
++                        String uri,
++                        List<String> dftGraphs,
++                        List<String> namedGraphs,
++                        SPARQLResult rs,
++                        String queryFile
++                        )
++    {
++        super(testName, uri, report) ;
++        this.defaultGraphURIs = dftGraphs ;
++        this.namedGraphURIs = namedGraphs ;
++        this.queryFile = queryFile ;
++        this.results = rs ;
++    }
++    
++    boolean oldValueUsePlainGraph = SystemARQ.UsePlainGraph ;
++    
++    @Override public void setUpTest() {
++        dataset = TDB2Factory.createDataset() ;
++        Txn.executeWrite(dataset, ()->{
++            setupData() ;
++        }) ;
++        // Make sure a plain, no sameValueAs graph is used.
++        oldValueUsePlainGraph = SystemARQ.UsePlainGraph ;
++        SystemARQ.UsePlainGraph = true ;
++    }
++    
++    @Override public void tearDownTest()
++    { 
++        if ( dataset != null )
++        {
++            dataset.close() ;
++            dataset = null ;
++        }
++        SystemARQ.UsePlainGraph = oldValueUsePlainGraph ;
++    }
++    
++    public void setupData()
++    {
++        if ( compareLists(defaultGraphURIs, currentDefaultGraphs) &&
++             compareLists(namedGraphURIs, currentNamedGraphs) )
++            return ;
++        
++        if ( defaultGraphURIs == null )
++            throw new TDBTestException("No default graphs given") ;
++
++        //graphLocation.clear() ;
++        
++        for ( String fn : defaultGraphURIs )
++            load(dataset.getDefaultModel(), fn) ;
++        
++        for ( String fn : namedGraphURIs )
++            load(dataset.getNamedModel(fn), fn) ;
++    }
++    
++    
++    @Override
++    protected void runTestForReal() throws Throwable
++    {
++        if ( skipThisTest )
++        {
++            log.info(this.getName()+" : Skipped") ;
++            return ;
++        }
++        
++        Query query = QueryFactory.read(queryFile) ;
++        Dataset ds = DatasetFactory.create(defaultGraphURIs, namedGraphURIs) ;
++        
++        // ---- First, get the expected results by executing in-memory or from a results file.
++        
++        ResultSetRewindable rs1$ = null ;
++        String expectedLabel$ = "" ;
++        if ( results != null )
++        {
++            rs1$ = ResultSetFactory.makeRewindable(results.getResultSet()) ;
++            expectedLabel$ = "Results file" ;
++        }
++        else
++        {
++            QueryEngineFactory f = QueryEngineRef.getFactory() ;
++            try(QueryExecution qExec1 = new QueryExecutionBase(query, ds, null, f)) {
++                rs1$ = ResultSetFactory.makeRewindable(qExec1.execSelect()) ;
++            }
++            expectedLabel$ = "Standard engine" ;
++        }
++        // Effectively final.
++        ResultSetRewindable rs1 = rs1$ ;
++        String expectedLabel = expectedLabel$ ;
++        // ---- Second, execute in persistent graph
++
++        Dataset ds2 = dataset ; //DatasetFactory.create(model) ;
++        Txn.executeRead(ds2, ()->{
++            try(QueryExecution qExec2 = QueryExecutionFactory.create(query, ds2)) {
++                ResultSet rs = qExec2.execSelect() ;
++                ResultSetRewindable rs2 = ResultSetFactory.makeRewindable(rs) ;
++
++                // See if the same.
++                boolean b = ResultSetCompare.equalsByValue(rs1, rs2) ;
++                if ( !b )
++                {
++                    rs1.reset() ;
++                    rs2.reset() ;
++                    System.out.println("------------------- "+this.getName());
++                    System.out.printf("**** Expected (%s)", expectedLabel) ;
++                    ResultSetFormatter.out(System.out, rs1) ; 
++                    System.out.println("**** Got (TDB)") ;
++                    ResultSetFormatter.out(System.out, rs2) ;
++                }
++
++                assertTrue("Results sets not the same", b) ;
++            }
++        }) ;
++    }
++
++    private static void load(Model model, String fn)
++    {
++        FileManager.get().readModel(model, fn) ;
++    }
++    
++    private static boolean compareLists(List<String> list1, List<String> list2)
++    {
++        if ( list1 == null )
++            return ( list2 == null ) ;
++        return list1.equals(list2) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/junit/TDBTestException.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/junit/TDBTestException.java
index 0000000,0000000..e2e2bcb
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/junit/TDBTestException.java
@@@ -1,0 -1,0 +1,28 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.junit;
++
++import org.seaborne.tdb2.TDBException ;
++
++public class TDBTestException extends TDBException
++{
++    public TDBTestException() {}
++    public TDBTestException(String msg) { super(msg) ; }
++    public TDBTestException(Throwable th) { super(th) ;}
++    public TDBTestException(String msg, Throwable th) { super(msg, th) ; }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/junit/TL.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/junit/TL.java
index 0000000,0000000..4ff4ae6
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/junit/TL.java
@@@ -1,0 -1,0 +1,89 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.junit;
++
++import java.util.function.Consumer ;
++
++import org.apache.jena.query.Dataset ;
++import org.apache.jena.sparql.core.DatasetGraph ;
++import org.apache.jena.system.Txn ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.tdb2.ConfigTest ;
++import org.seaborne.tdb2.DatabaseMgr ;
++import org.seaborne.tdb2.TDB2Factory ;
++import org.seaborne.tdb2.sys.TDBInternal ;
++
++/** Support for tests to be less "transaction-y" */
++public class TL {
++    
++    public static void exec(Consumer<Dataset> action) {
++        Dataset dataset = createTestDataset();
++        try { 
++            Txn.executeWrite(dataset, ()->action.accept(dataset));
++            //TDBInternal.reset();
++            
++        } finally { expel(dataset); } 
++    }
++    
++    public static void execMem(Consumer<Dataset> action) {
++        Dataset dataset = createTestDatasetMem() ;
++        Txn.executeWrite(dataset, ()->action.accept(dataset));
++        expel(dataset);
++    }
++    
++    public static void expel(Dataset dataset) {
++        expel(dataset.asDatasetGraph());
++    }
++
++    public static void expel(DatasetGraph dataset) {
++        TDBInternal.expel(dataset);
++    }
++
++    // Or use these for @Before, @After style.
++    
++    public static Location cleanLocation() {
++        // To avoid the problems on MS Windows where memory mapped files
++        // can't be deleted from a running JVM, we use a different, cleaned 
++        // directory each time.
++        String dirname = ConfigTest.getCleanDir() ;
++        Location location = Location.create(dirname) ;
++        return location ;
++    }
++    
++    private static void releaseDataset(Dataset dataset) {
++        dataset.abort() ;
++        expel(dataset);
++    }
++
++    private static Dataset createTestDataset() {
++        Location location = cleanLocation() ;
++        Dataset dataset = TDB2Factory.connectDataset(location) ;
++        return dataset ;
++    }
++    
++    public static Dataset createTestDatasetMem() {
++        Dataset dataset = TDB2Factory.createDataset() ;
++        return dataset ;
++    }
++
++    public static DatasetGraph createTestDatasetGraphMem() {
++        DatasetGraph dataset = DatabaseMgr.createDatasetGraph() ;
++        return dataset ;
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/junit/TestFactoryTDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/junit/TestFactoryTDB.java
index 0000000,0000000..516de02
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/junit/TestFactoryTDB.java
@@@ -1,0 -1,0 +1,87 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.junit;
++
++import junit.framework.Test ;
++import junit.framework.TestCase ;
++import junit.framework.TestSuite ;
++import org.apache.jena.rdf.model.Resource ;
++import org.apache.jena.sparql.junit.EarlReport ;
++import org.apache.jena.sparql.junit.SurpressedTest ;
++import org.apache.jena.sparql.junit.TestItem ;
++import org.apache.jena.sparql.vocabulary.TestManifestX ;
++import org.apache.jena.util.junit.TestFactoryManifest ;
++
++public class TestFactoryTDB extends TestFactoryManifest
++{
++    public static EarlReport report = null ;
++    
++    public static void make(TestSuite ts, String manifestFile, String testRootName)
++    {
++        // for each graph type do
++        TestSuite ts2 = makeSuite(manifestFile, testRootName) ;
++        ts.addTest(ts2) ;
++    }
++    
++    public static TestSuite makeSuite(String manifestFile, String testRootName)
++    {
++        TestFactoryTDB f = new TestFactoryTDB(testRootName) ;
++        TestSuite ts = f.process(manifestFile) ;
++        if ( testRootName != null )
++            ts.setName(testRootName+ts.getName()) ;
++        return ts ;
++    }
++    
++    // Factory
++    
++    public String testRootName ;
++
++    public TestFactoryTDB(String testRootName)
++    {
++        this.testRootName = testRootName ;
++    }
++    
++    @Override
++    protected Test makeTest(Resource manifest, Resource entry, String testName, Resource action, Resource result)
++    {
++        if ( testRootName != null )
++            testName = testRootName+testName ;
++        
++        TestItem testItem = TestItem.create(entry, null) ;
++        
++        TestCase test = null ;
++        
++        if ( testItem.getTestType() != null )
++        {
++            if ( testItem.getTestType().equals(TestManifestX.TestQuery) )
++                test = new QueryTestTDB(testName, report, testItem) ;
++            
++            if ( testItem.getTestType().equals(TestManifestX.TestSurpressed) )
++                test = new SurpressedTest(testName, report, testItem) ;
++            
++            if ( test == null )
++                System.err.println("Unrecognized test type: "+testItem.getTestType()) ;
++        }
++        // Default 
++        if ( test == null )
++            test = new QueryTestTDB(testName, report, testItem) ;
++
++        return test ;
++    }
++
++}


[35/50] [abbrv] jena git commit: JENA-1396: Merge //github/afs/mantis as subdirectory jena-db/

Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Basic/manifest.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Basic/manifest.ttl
index 0000000,0000000..aacbb56
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Basic/manifest.ttl
@@@ -1,0 -1,0 +1,101 @@@
++#  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.
++
++@prefix rdf:    <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
++@prefix rdfs:	<http://www.w3.org/2000/01/rdf-schema#> .
++@prefix mf:     <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
++@prefix qt:     <http://www.w3.org/2001/sw/DataAccess/tests/test-query#> .
++
++<>  rdf:type mf:Manifest ;
++    rdfs:label "Basic test cases" ;
++    mf:entries
++    ( 
++      [  mf:name    "Basic 00" ;
++         mf:action
++            [ qt:query  <basic-00.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Basic S" ;
++         mf:action
++            [ qt:query  <basic-1-S.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Basic P" ;
++         mf:action
++            [ qt:query  <basic-1-P.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Basic O" ;
++         mf:action
++            [ qt:query  <basic-1-O.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Basic 1 SP" ;
++         mf:action
++            [ qt:query  <basic-1-SP.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Basic 1 SO" ;
++         mf:action
++            [ qt:query  <basic-1-SO.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Basic 1 PO" ;
++         mf:action
++            [ qt:query  <basic-1-PO.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Basic 1 SPO" ;
++         mf:action
++            [ qt:query  <basic-1-SPO.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Basic 2 SO" ;
++         mf:action
++            [ qt:query  <basic-2-SO.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++
++      [  mf:name    "Basic 3 S" ;
++         mf:action
++            [ qt:query  <basic-3-S.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Basic 3 P" ;
++         mf:action
++            [ qt:query  <basic-3-P.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Basic 3 O" ;
++         mf:action
++            [ qt:query  <basic-3-O.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Basic 3 SP" ;
++         mf:action
++            [ qt:query  <basic-3-SP.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Basic 3 SO" ;
++         mf:action
++            [ qt:query  <basic-3-SO.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Basic 3 PO" ;
++         mf:action
++            [ qt:query  <basic-3-SO.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++    ) .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Data/solver-data.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Data/solver-data.ttl
index 0000000,0000000..cdbfb19
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Data/solver-data.ttl
@@@ -1,0 -1,0 +1,7 @@@
++@prefix : <http://example/> .
++
++:s :p :o .
++:s :p 10 .
++:s :p :x .
++:x :q :y .
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Loader/data-1.nq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Loader/data-1.nq
index 0000000,0000000..e9812a6
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Loader/data-1.nq
@@@ -1,0 -1,0 +1,1 @@@
++<s> <p> <o> <g> .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Loader/data-2.nt
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Loader/data-2.nt
index 0000000,0000000..81fc25f
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Loader/data-2.nt
@@@ -1,0 -1,0 +1,1 @@@
++<s> <p> <o> .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Loader/data-3.trig
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Loader/data-3.trig
index 0000000,0000000..8000cc1
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Loader/data-3.trig
@@@ -1,0 -1,0 +1,3 @@@
++prefix : <http://example/> 
++
++:g { :s :p :o }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Loader/data-4.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Loader/data-4.ttl
index 0000000,0000000..49678f1
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Loader/data-4.ttl
@@@ -1,0 -1,0 +1,4 @@@
++prefix : <http://example/> 
++
++:s :p :o .
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Pattern/data-1.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Pattern/data-1.ttl
index 0000000,0000000..d3a3d80
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Pattern/data-1.ttl
@@@ -1,0 -1,0 +1,17 @@@
++@prefix : <http://example/> .
++
++:x1 :p1 :z1 .
++:x1 :p1 :z2 .
++
++:x1 :p2 :z1 .
++:x1 :p2 :z2 .
++
++:x2 :p1 :z1 .
++:x2 :p1 :z2 .
++
++:x2 :p2 :z1 .
++:x2 :p2 :z2 .
++
++:x :p :x .
++:x :x :x .
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Pattern/manifest.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Pattern/manifest.ttl
index 0000000,0000000..094244b
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Pattern/manifest.ttl
@@@ -1,0 -1,0 +1,35 @@@
++#  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.
++
++@prefix rdf:    <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
++@prefix rdfs:	<http://www.w3.org/2000/01/rdf-schema#> .
++@prefix mf:     <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
++@prefix qt:     <http://www.w3.org/2001/sw/DataAccess/tests/test-query#> .
++
++<>  rdf:type mf:Manifest ;
++    rdfs:label "Patterns" ;
++    mf:entries
++    ( 
++      [  mf:name    "Pattern 1" ;
++         mf:action
++            [ qt:query  <pattern-1.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Pattern 2" ;
++         mf:action
++            [ qt:query  <pattern-2.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++    ) .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Pattern/pattern-1.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Pattern/pattern-1.rq
index 0000000,0000000..ca7c8fc
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Pattern/pattern-1.rq
@@@ -1,0 -1,0 +1,7 @@@
++PREFIX :  <http://example/OTHER>
++
++SELECT * 
++{ 
++    :NoSuchNode ?p ?z .
++    ?x ?p ?z
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Pattern/pattern-2.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Pattern/pattern-2.rq
index 0000000,0000000..9b90038
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Pattern/pattern-2.rq
@@@ -1,0 -1,0 +1,8 @@@
++PREFIX :  <http://example/OTHER>
++
++SELECT * 
++{ 
++    # Test shared variables
++    ?x1 :p1 ?z .
++    ?x2 :p1 ?z .
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Quads/data-1.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Quads/data-1.ttl
index 0000000,0000000..adc4239
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Quads/data-1.ttl
@@@ -1,0 -1,0 +1,5 @@@
++@prefix : <http://example/> .
++
++:x :p 1 .
++
++:x1 :p :x .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Quads/data-2.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Quads/data-2.ttl
index 0000000,0000000..100a7c7
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Quads/data-2.ttl
@@@ -1,0 -1,0 +1,5 @@@
++@prefix : <http://example/> .
++
++:x :p 1 .
++
++:x2 :p :x .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Quads/data-dft.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Quads/data-dft.ttl
index 0000000,0000000..a2ef718
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Quads/data-dft.ttl
@@@ -1,0 -1,0 +1,10 @@@
++@prefix : <http://example/> .
++
++:x :p 1 .
++:x :p 2 .
++
++:y :p 1 .
++:y :p :x .
++:y :p :z .
++
++:z :q "abc" .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Quads/manifest.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Quads/manifest.ttl
index 0000000,0000000..15a46a7
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Quads/manifest.ttl
@@@ -1,0 -1,0 +1,51 @@@
++#  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.
++
++@prefix rdf:    <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
++@prefix rdfs:	<http://www.w3.org/2000/01/rdf-schema#> .
++@prefix mf:     <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
++@prefix qt:     <http://www.w3.org/2001/sw/DataAccess/tests/test-query#> .
++
++<>  rdf:type mf:Manifest ;
++    rdfs:label "Quads and quad patterns" ;
++    mf:entries
++    ( 
++       [  mf:name    "Quad-1" ;
++          mf:action
++            [ qt:query  <quad-01.rq> ;
++              qt:data       <data-dft.ttl> ;
++              qt:graphData  <data-1.ttl> ;
++              qt:graphData  <data-2.ttl> ;
++             ]
++      ]
++       [  mf:name    "Quad-2" ;
++          mf:action
++            [ qt:query  <quad-02.rq> ;
++              qt:data       <data-dft.ttl> ;
++              qt:graphData  <data-1.ttl> ;
++              qt:graphData  <data-2.ttl> ;
++             ]
++      ]
++
++        ## Union of named graphs, explicitly named default graph
++        ## Complex patterns.
++
++##       [  mf:name    "Basic 00" ;
++##          mf:action
++##             [ qt:query  <basic-00.rq> ;
++##               qt:data   <data-1.ttl> ;
++##               qt:graphData ; ]
++##       ]
++    ) .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Quads/quad-01.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Quads/quad-01.rq
index 0000000,0000000..b705fc8
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Quads/quad-01.rq
@@@ -1,0 -1,0 +1,9 @@@
++PREFIX : <http://example/>
++PREFIX sys: <urn:x-arq:>
++
++SELECT *
++{
++   {?s ?p ?o } UNION { GRAPH ?g {?s ?p ?o}}
++}
++
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Quads/quad-02.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Quads/quad-02.rq
index 0000000,0000000..ef3bf2f
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Quads/quad-02.rq
@@@ -1,0 -1,0 +1,8 @@@
++PREFIX : <http://example/>
++PREFIX sys: <urn:x-arq:>
++
++SELECT *
++{
++  ?s ?p ?x
++   GRAPH ?g {?s1 ?p1 ?x }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/UnionGraph/data-1.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/UnionGraph/data-1.ttl
index 0000000,0000000..77be09c
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/UnionGraph/data-1.ttl
@@@ -1,0 -1,0 +1,7 @@@
++@prefix : <http://example/> .
++
++:x :p 1 .
++:x :p 2 .
++
++
++:y :q :x .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/UnionGraph/data-2.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/UnionGraph/data-2.ttl
index 0000000,0000000..59ac719
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/UnionGraph/data-2.ttl
@@@ -1,0 -1,0 +1,5 @@@
++@prefix : <http://example/> .
++
++:x :p 1 .
++:x :p 99 .
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/UnionGraph/data-dft.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/UnionGraph/data-dft.ttl
index 0000000,0000000..d64ddd9
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/UnionGraph/data-dft.ttl
@@@ -1,0 -1,0 +1,5 @@@
++@prefix : <http://example/> .
++
++:x :p 11 .
++:x :p 999 .
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/UnionGraph/manifest.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/UnionGraph/manifest.ttl
index 0000000,0000000..217d229
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/UnionGraph/manifest.ttl
@@@ -1,0 -1,0 +1,107 @@@
++#  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.
++
++@prefix rdf:    <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
++@prefix rdfs:	<http://www.w3.org/2000/01/rdf-schema#> .
++@prefix mf:     <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
++@prefix mfx:    <http://jena.hpl.hp.com/2005/05/test-manifest-extra#> .
++@prefix qt:     <http://www.w3.org/2001/sw/DataAccess/tests/test-query#> .
++
++<>  rdf:type mf:Manifest ;
++    rdfs:label "Union of named graphs" ;
++    mf:entries
++    ( 
++      [  mf:name    "Merge-1" ;
++         rdf:type   mfx:TestQuery ; 
++         mf:action
++            [ qt:query  <merge-1.rq> ;
++              qt:data <data-dft.ttl> ;
++              qt:graphData <data-1.ttl> ;
++              qt:graphData <data-2.ttl> ;
++            ] ;
++         mf:result <merge-1-results.srx>
++      ]
++      [  mf:name    "Merge-2" ;
++         rdf:type   mfx:TestQuery ; 
++         mf:action
++            [ qt:query  <merge-2.rq> ;
++              qt:data <data-dft.ttl> ;
++              qt:graphData <data-1.ttl> ;
++              qt:graphData <data-2.ttl> ;
++            ] ;
++         mf:result <merge-2-results.srx>
++      ]
++      [  mf:name    "Merge-3" ;
++         rdf:type   mfx:TestQuery ; 
++         mf:action
++            [ qt:query  <merge-3.rq> ;
++              qt:data <data-dft.ttl> ;
++              qt:graphData <data-1.ttl> ;
++              qt:graphData <data-2.ttl> ;
++            ] ;
++         mf:result <merge-3-results.srx>
++      ]
++      [  mf:name    "Merge-4" ;
++         rdf:type   mfx:TestQuery ; 
++         mf:action
++            [ qt:query  <merge-4.rq> ;
++              qt:data <data-dft.ttl> ;
++              qt:graphData <data-1.ttl> ;
++              qt:graphData <data-2.ttl> ;
++            ] ;
++         mf:result <merge-4-results.srx>
++      ]
++      [  mf:name    "Merge-5" ;
++         rdf:type   mfx:TestQuery ; 
++         mf:action
++            [ qt:query  <merge-5.rq> ;
++              qt:data <data-dft.ttl> ;
++              qt:graphData <data-1.ttl> ;
++              qt:graphData <data-2.ttl> ;
++            ] ;
++         mf:result <merge-5-results.srx>
++      ]
++      [  mf:name    "Merge-6" ;
++         rdf:type   mfx:TestQuery ; 
++         mf:action
++            [ qt:query  <merge-6.rq> ;
++              qt:data <data-dft.ttl> ;
++              qt:graphData <data-1.ttl> ;
++              qt:graphData <data-2.ttl> ;
++            ] ;
++         mf:result <merge-6-results.srx>
++      ]
++      # Queries that need more than one SQL query (currently)
++      [  mf:name    "Merge-A" ;
++         rdf:type   mfx:TestQuery ; 
++         mf:action
++            [ qt:query  <merge-A.rq> ;
++              qt:data <data-dft.ttl> ;
++              qt:graphData <data-1.ttl> ;
++              qt:graphData <data-2.ttl> ;
++            ] ;
++         mf:result <merge-A-results.srx>
++      ]
++      [  mf:name    "Merge-B" ;
++         rdf:type   mfx:TestQuery ; 
++         mf:action
++            [ qt:query  <merge-B.rq> ;
++              qt:data <data-dft.ttl> ;
++              qt:graphData <data-1.ttl> ;
++              qt:graphData <data-2.ttl> ;
++            ] ;
++         mf:result <merge-B-results.srx>
++      ]
++    ).

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/UnionGraph/merge-1-results.srx
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/UnionGraph/merge-1-results.srx
index 0000000,0000000..f850ec3
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/UnionGraph/merge-1-results.srx
@@@ -1,0 -1,0 +1,57 @@@
++<?xml version="1.0"?>
++<sparql
++    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
++    xmlns:xs="http://www.w3.org/2001/XMLSchema#"
++    xmlns="http://www.w3.org/2005/sparql-results#" >
++  <head>
++    <variable name="s"/>
++    <variable name="p"/>
++    <variable name="o"/>
++  </head>
++  <results>
++    <result>
++      <binding name="s">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="o">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">1</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="s">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="o">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">99</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="s">
++        <uri>http://example/y</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/q</uri>
++      </binding>
++      <binding name="o">
++        <uri>http://example/x</uri>
++      </binding>
++    </result>
++    <result>
++      <binding name="s">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="o">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">2</literal>
++      </binding>
++    </result>
++  </results>
++</sparql>

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/UnionGraph/merge-1.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/UnionGraph/merge-1.rq
index 0000000,0000000..c403064
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/UnionGraph/merge-1.rq
@@@ -1,0 -1,0 +1,8 @@@
++PREFIX :    <http://example/>
++PREFIX arq: <urn:x-arq:>
++
++SELECT *
++{
++    GRAPH arq:UnionGraph { ?s ?p ?o }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/UnionGraph/merge-2-results.srx
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/UnionGraph/merge-2-results.srx
index 0000000,0000000..18f9252
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/UnionGraph/merge-2-results.srx
@@@ -1,0 -1,0 +1,66 @@@
++<?xml version="1.0"?>
++<sparql
++    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
++    xmlns:xs="http://www.w3.org/2001/XMLSchema#"
++    xmlns="http://www.w3.org/2005/sparql-results#" >
++  <head>
++    <variable name="s"/>
++    <variable name="p"/>
++    <variable name="o"/>
++    <variable name="q"/>
++    <variable name="z"/>
++  </head>
++  <results>
++    <result>
++      <binding name="s">
++        <uri>http://example/y</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/q</uri>
++      </binding>
++      <binding name="o">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="q">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="z">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">99</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="s">
++        <uri>http://example/y</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/q</uri>
++      </binding>
++      <binding name="o">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="q">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="z">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">1</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="s">
++        <uri>http://example/y</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/q</uri>
++      </binding>
++      <binding name="o">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="q">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="z">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">2</literal>
++      </binding>
++    </result>
++  </results>
++</sparql>

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/UnionGraph/merge-2.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/UnionGraph/merge-2.rq
index 0000000,0000000..27953a6
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/UnionGraph/merge-2.rq
@@@ -1,0 -1,0 +1,8 @@@
++PREFIX :    <http://example/>
++PREFIX arq: <urn:x-arq:>
++
++SELECT *
++{
++    GRAPH arq:UnionGraph { ?s ?p ?o . ?o ?q ?z }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/UnionGraph/merge-3-results.srx
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/UnionGraph/merge-3-results.srx
index 0000000,0000000..4a65413
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/UnionGraph/merge-3-results.srx
@@@ -1,0 -1,0 +1,31 @@@
++<?xml version="1.0"?>
++<sparql
++    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
++    xmlns:xs="http://www.w3.org/2001/XMLSchema#"
++    xmlns="http://www.w3.org/2005/sparql-results#" >
++  <head>
++    <variable name="p"/>
++  </head>
++  <results>
++    <result>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++    </result>
++    <result>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++    </result>
++    <result>
++      <binding name="p">
++        <uri>http://example/q</uri>
++      </binding>
++    </result>
++    <result>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++    </result>
++  </results>
++</sparql>

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/UnionGraph/merge-3.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/UnionGraph/merge-3.rq
index 0000000,0000000..1212926
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/UnionGraph/merge-3.rq
@@@ -1,0 -1,0 +1,8 @@@
++PREFIX :    <http://example/>
++PREFIX arq: <urn:x-arq:>
++
++SELECT *
++{
++    GRAPH arq:UnionGraph { [] ?p [] }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/UnionGraph/merge-4-results.srx
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/UnionGraph/merge-4-results.srx
index 0000000,0000000..0b44cb2
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/UnionGraph/merge-4-results.srx
@@@ -1,0 -1,0 +1,99 @@@
++<?xml version="1.0"?>
++<sparql
++    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
++    xmlns:xs="http://www.w3.org/2001/XMLSchema#"
++    xmlns="http://www.w3.org/2005/sparql-results#" >
++  <head>
++    <variable name="s"/>
++    <variable name="p"/>
++    <variable name="o"/>
++    <variable name="q"/>
++    <variable name="z"/>
++  </head>
++  <results>
++    <result>
++      <binding name="s">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="o">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">1</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="s">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="o">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">99</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="s">
++        <uri>http://example/y</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/q</uri>
++      </binding>
++      <binding name="o">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="q">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="z">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">1</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="s">
++        <uri>http://example/y</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/q</uri>
++      </binding>
++      <binding name="o">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="q">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="z">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">99</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="s">
++        <uri>http://example/y</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/q</uri>
++      </binding>
++      <binding name="o">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="q">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="z">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">2</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="s">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="o">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">2</literal>
++      </binding>
++    </result>
++  </results>
++</sparql>

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/UnionGraph/merge-4.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/UnionGraph/merge-4.rq
index 0000000,0000000..004f4bc
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/UnionGraph/merge-4.rq
@@@ -1,0 -1,0 +1,8 @@@
++PREFIX :    <http://example/>
++PREFIX arq: <urn:x-arq:>
++
++SELECT *
++{
++    GRAPH arq:UnionGraph 
++    { ?s ?p ?o . OPTIONAL { ?o ?q ?z } }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/UnionGraph/merge-5-results.srx
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/UnionGraph/merge-5-results.srx
index 0000000,0000000..c7d777f
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/UnionGraph/merge-5-results.srx
@@@ -1,0 -1,0 +1,33 @@@
++<?xml version="1.0"?>
++<sparql xmlns="http://www.w3.org/2005/sparql-results#">
++  <head>
++    <variable name="p"/>
++    <variable name="o"/>
++  </head>
++  <results>
++    <result>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="o">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">99</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="o">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">2</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="o">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">1</literal>
++      </binding>
++    </result>
++  </results>
++</sparql>

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/UnionGraph/merge-5.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/UnionGraph/merge-5.rq
index 0000000,0000000..fa9bfe3
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/UnionGraph/merge-5.rq
@@@ -1,0 -1,0 +1,8 @@@
++PREFIX :    <http://example/>
++PREFIX arq: <urn:x-arq:>
++
++SELECT *
++{
++    GRAPH arq:UnionGraph { :x ?p  ?o }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/UnionGraph/merge-6-results.srx
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/UnionGraph/merge-6-results.srx
index 0000000,0000000..b4d31dc
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/UnionGraph/merge-6-results.srx
@@@ -1,0 -1,0 +1,43 @@@
++<?xml version="1.0"?>
++<sparql xmlns="http://www.w3.org/2005/sparql-results#">
++  <head>
++    <variable name="x"/>
++    <variable name="p"/>
++    <variable name="v"/>
++  </head>
++  <results>
++    <result>
++      <binding name="x">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="v">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">99</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="x">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="v">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">2</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="x">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="v">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">1</literal>
++      </binding>
++    </result>
++  </results>
++</sparql>

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/UnionGraph/merge-6.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/UnionGraph/merge-6.rq
index 0000000,0000000..7819cbb
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/UnionGraph/merge-6.rq
@@@ -1,0 -1,0 +1,7 @@@
++PREFIX :    <http://example/>
++PREFIX arq: <urn:x-arq:>
++
++SELECT *
++{
++    GRAPH arq:UnionGraph { ?x ?p  99 . ?x :p ?v }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/UnionGraph/merge-A-results.srx
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/UnionGraph/merge-A-results.srx
index 0000000,0000000..1438c25
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/UnionGraph/merge-A-results.srx
@@@ -1,0 -1,0 +1,99 @@@
++<?xml version="1.0"?>
++<sparql
++    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
++    xmlns:xs="http://www.w3.org/2001/XMLSchema#"
++    xmlns="http://www.w3.org/2005/sparql-results#" >
++  <head>
++    <variable name="s"/>
++    <variable name="p"/>
++    <variable name="o"/>
++    <variable name="q"/>
++    <variable name="z"/>
++  </head>
++  <results>
++    <result>
++      <binding name="s">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="o">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">1</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="s">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="o">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">99</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="s">
++        <uri>http://example/y</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/q</uri>
++      </binding>
++      <binding name="o">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="q">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="z">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">99</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="s">
++        <uri>http://example/y</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/q</uri>
++      </binding>
++      <binding name="o">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="q">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="z">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">1</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="s">
++        <uri>http://example/y</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/q</uri>
++      </binding>
++      <binding name="o">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="q">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="z">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">2</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="s">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="o">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">2</literal>
++      </binding>
++    </result>
++  </results>
++</sparql>

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/UnionGraph/merge-A.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/UnionGraph/merge-A.rq
index 0000000,0000000..7bab242
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/UnionGraph/merge-A.rq
@@@ -1,0 -1,0 +1,8 @@@
++PREFIX :    <http://example>
++PREFIX arq: <urn:x-arq:>
++
++SELECT *
++{
++    GRAPH arq:UnionGraph 
++    { ?s ?p ?o . OPTIONAL { ?o ?q ?z FILTER(?o != 123) } }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/UnionGraph/merge-B-results.srx
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/UnionGraph/merge-B-results.srx
index 0000000,0000000..60d7766
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/UnionGraph/merge-B-results.srx
@@@ -1,0 -1,0 +1,49 @@@
++<?xml version="1.0"?>
++<sparql
++    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
++    xmlns:xs="http://www.w3.org/2001/XMLSchema#"
++    xmlns="http://www.w3.org/2005/sparql-results#" >
++  <head>
++    <variable name="s"/>
++    <variable name="p"/>
++    <variable name="o"/>
++    <variable name="q"/>
++    <variable name="v"/>
++  </head>
++  <results>
++    <result>
++      <binding name="s">
++        <uri>http://example/y</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/q</uri>
++      </binding>
++      <binding name="o">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="q">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="v">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">11</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="s">
++        <uri>http://example/y</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/q</uri>
++      </binding>
++      <binding name="o">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="q">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="v">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">999</literal>
++      </binding>
++    </result>
++  </results>
++</sparql>

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/UnionGraph/merge-B.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/UnionGraph/merge-B.rq
index 0000000,0000000..d4aceda
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/UnionGraph/merge-B.rq
@@@ -1,0 -1,0 +1,8 @@@
++PREFIX :    <http://example>
++PREFIX arq: <urn:x-arq:>
++
++SELECT *
++{
++    GRAPH arq:UnionGraph { ?s ?p ?o . }
++    { ?o ?q ?v }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Update/create-1.ru
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Update/create-1.ru
index 0000000,0000000..3276ec1
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Update/create-1.ru
@@@ -1,0 -1,0 +1,1 @@@
++CREATE GRAPH <http://example/graph>

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Update/drop-1.ru
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Update/drop-1.ru
index 0000000,0000000..2512eb3
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Update/drop-1.ru
@@@ -1,0 -1,0 +1,1 @@@
++DROP GRAPH <http://example/graph>

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Update/update-1.ru
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Update/update-1.ru
index 0000000,0000000..7a6b737
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Update/update-1.ru
@@@ -1,0 -1,0 +1,3 @@@
++PREFIX : <http://example/> 
++
++INSERT { :r :p 123 }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Update/update-2.ru
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Update/update-2.ru
index 0000000,0000000..41e4ec0
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Update/update-2.ru
@@@ -1,0 -1,0 +1,5 @@@
++PREFIX : <http://example/> 
++
++CREATE GRAPH <http://example/g1>
++INSERT INTO GRAPH <http://example/g1>
++{ :r :p 123 }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Update/update-3.ru
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Update/update-3.ru
index 0000000,0000000..17f8410
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Update/update-3.ru
@@@ -1,0 -1,0 +1,7 @@@
++PREFIX : <http://example/> 
++
++CREATE GRAPH <http://example/g1>
++INSERT INTO GRAPH <http://example/g1>
++{ :r :p 123 }
++DELETE FROM GRAPH <http://example/g1>
++{ :r :p 123 }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Values/data-1.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Values/data-1.ttl
index 0000000,0000000..27c37dd
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Values/data-1.ttl
@@@ -1,0 -1,0 +1,23 @@@
++@prefix : <http://example/> .
++@prefix  xsd:    <http://www.w3.org/2001/XMLSchema#> .
++
++:x1 :p 1 .
++:x2 :p "1" .
++:x3 :p "1"@en .
++
++:x4 :p 1.5 .
++
++:x5 :p "2008-04-29T09:13:15+01:00"^^xsd:dateTime .
++
++:x6 :p "0008-04-29T09:13:15-01:00"^^xsd:dateTime .
++
++:x7 :p "-3000-04-29T09:13:15Z"^^xsd:dateTime .
++
++# Not encoded inline
++:x8 :p "-3000-04-29T09:13:15Z"^^xsd:dateTime .
++
++:x9 :p "2008-04-29"^^xsd:date .
++
++:x10 :p "2008-04-29Z"^^xsd:date .
++
++:x11 :p "2008-04-29+01:00"^^xsd:date .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Values/manifest.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Values/manifest.ttl
index 0000000,0000000..3396af5
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Values/manifest.ttl
@@@ -1,0 -1,0 +1,76 @@@
++#  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.
++
++@prefix rdf:    <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
++@prefix rdfs:	<http://www.w3.org/2000/01/rdf-schema#> .
++@prefix mf:     <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
++@prefix qt:     <http://www.w3.org/2001/sw/DataAccess/tests/test-query#> .
++
++<>  rdf:type mf:Manifest ;
++    rdfs:label "Value tests" ;
++    mf:entries
++    ( 
++      [  mf:name    "Value-00" ;
++         mf:action
++            [ qt:query  <value-00.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Value-01" ;
++         mf:action
++            [ qt:query  <value-01.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Value-02" ;
++         mf:action
++            [ qt:query  <value-02.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Value-03" ;
++         mf:action
++            [ qt:query  <value-03.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++
++      [  mf:name    "Value-04" ;
++         mf:action
++            [ qt:query  <value-04.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Value-05" ;
++         mf:action
++            [ qt:query  <value-05.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Value-06" ;
++         mf:action
++            [ qt:query  <value-06.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Value-07" ;
++         mf:action
++            [ qt:query  <value-07.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Value-08" ;
++         mf:action
++            [ qt:query  <value-08.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Value-09" ;
++         mf:action
++            [ qt:query  <value-09.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++    ) .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Values/value-00.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Values/value-00.rq
index 0000000,0000000..d1fe80b
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Values/value-00.rq
@@@ -1,0 -1,0 +1,1 @@@
++SELECT * { ?s ?p ?o }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Values/value-01.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Values/value-01.rq
index 0000000,0000000..664f67a
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Values/value-01.rq
@@@ -1,0 -1,0 +1,1 @@@
++SELECT * { ?s ?p 1 }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Values/value-02.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Values/value-02.rq
index 0000000,0000000..5dfe5ec
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Values/value-02.rq
@@@ -1,0 -1,0 +1,1 @@@
++SELECT * { ?s ?p 1.5 }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Values/value-03.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Values/value-03.rq
index 0000000,0000000..7134be8
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Values/value-03.rq
@@@ -1,0 -1,0 +1,1 @@@
++SELECT * { ?s ?p "1" }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Values/value-04.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Values/value-04.rq
index 0000000,0000000..030116d
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Values/value-04.rq
@@@ -1,0 -1,0 +1,1 @@@
++SELECT * { ?s ?p "1"@en }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Values/value-05.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Values/value-05.rq
index 0000000,0000000..fbd8d9b
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Values/value-05.rq
@@@ -1,0 -1,0 +1,5 @@@
++SELECT *
++{ 
++    ?s ?p ?o . 
++    FILTER(?o = 1)
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Values/value-06.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Values/value-06.rq
index 0000000,0000000..8f34124
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Values/value-06.rq
@@@ -1,0 -1,0 +1,6 @@@
++PREFIX  xsd:    <http://www.w3.org/2001/XMLSchema#>
++
++SELECT *
++{ 
++    ?s ?p "2008-04-29+01:00"^^xsd:date .
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Values/value-07.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Values/value-07.rq
index 0000000,0000000..cafd777
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Values/value-07.rq
@@@ -1,0 -1,0 +1,6 @@@
++PREFIX  xsd:    <http://www.w3.org/2001/XMLSchema#>
++
++SELECT *
++{ 
++    ?s ?p ?x . FILTER (?x = "2008-04-29+01:00"^^xsd:date) .
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Values/value-08.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Values/value-08.rq
index 0000000,0000000..e6fc284
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Values/value-08.rq
@@@ -1,0 -1,0 +1,6 @@@
++PREFIX  xsd:    <http://www.w3.org/2001/XMLSchema#>
++
++SELECT *
++{ 
++    ?s ?p "2008-04-29T09:13:15+01:00"^^xsd:dateTime .
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/Values/value-09.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/Values/value-09.rq
index 0000000,0000000..02dcc20
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/Values/value-09.rq
@@@ -1,0 -1,0 +1,7 @@@
++PREFIX  xsd:    <http://www.w3.org/2001/XMLSchema#>
++
++SELECT *
++{ 
++    # Out of range - not an immediate literal
++    ?s ?p "-3000-04-29T09:13:15Z"^^xsd:dateTime
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/testing/manifest.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/testing/manifest.ttl
index 0000000,0000000..aeaf80e
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/testing/manifest.ttl
@@@ -1,0 -1,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.
++
++@prefix rdf:    <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
++@prefix rdfs:	<http://www.w3.org/2000/01/rdf-schema#> .
++@prefix mf:     <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
++@prefix qt:     <http://www.w3.org/2001/sw/DataAccess/tests/test-query#> .
++
++<>  rdf:type mf:Manifest ;
++    rdfs:label "TDB - Scripts" ;
++    mf:include (
++        <Basic/manifest.ttl>
++        <Pattern/manifest.ttl>
++        <Quads/manifest.ttl>
++        <Values/manifest.ttl>
++        <UnionGraph/manifest.ttl>
++    ) .
++
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/.gitignore
----------------------------------------------------------------------
diff --cc jena-db/tdb2/.gitignore
index 0000000,0000000..731afb6
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/.gitignore
@@@ -1,0 -1,0 +1,2 @@@
++DB*
++D.*

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/pom.xml
----------------------------------------------------------------------
diff --cc jena-db/tdb2/pom.xml
index 0000000,0000000..80cfc5a
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/pom.xml
@@@ -1,0 -1,0 +1,108 @@@
++<?xml version="1.0" encoding="utf-8"?>
++<!--
++   Licensed 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.
++ 
++   See the NOTICE file distributed with this work for additional
++   information regarding copyright ownership.
++-->
++
++<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
++
++  <modelVersion>4.0.0</modelVersion>
++
++  <artifactId>tdb2</artifactId>
++  <packaging>jar</packaging>
++  <name>Mantis (${project.artifactId})</name>
++
++  <parent>
++    <groupId>org.seaborne.mantis</groupId>
++    <artifactId>mantis</artifactId>
++    <version>0.3.1-SNAPSHOT</version>
++  </parent> 
++
++  <dependencies>
++
++    <!--
++        <dependency>
++        <groupId>org.seaborne.mantis</groupId>
++        <artifactId>dboe-quack</artifactId>
++        <version>0.2.0-SNAPSHOT</version>
++        </dependency>
++    -->
++    
++    <dependency>
++      <groupId>org.seaborne.mantis</groupId>
++      <artifactId>dboe-trans-data</artifactId>
++      <version>0.3.1-SNAPSHOT</version>
++    </dependency>
++
++    <dependency>
++      <groupId>org.apache.jena</groupId>
++      <artifactId>jena-core</artifactId>
++      <classifier>tests</classifier>
++      <scope>test</scope>
++    </dependency>
++
++    <dependency>
++      <groupId>org.apache.jena</groupId>
++      <artifactId>jena-arq</artifactId>
++      <classifier>tests</classifier>
++      <scope>test</scope>
++    </dependency>
++
++    <dependency>
++      <groupId>org.apache.jena</groupId>
++      <artifactId>jena-base</artifactId>
++      <classifier>tests</classifier>
++      <scope>test</scope>
++    </dependency>
++
++  </dependencies>
++
++  <build>
++    <plugins>
++      <plugin>
++        <groupId>org.apache.maven.plugins</groupId>
++        <artifactId>maven-jar-plugin</artifactId>
++        <executions>
++          <execution>
++            <goals>
++              <goal>test-jar</goal>
++            </goals>
++          </execution>
++        </executions>
++      </plugin>
++      <plugin>
++        <groupId>org.apache.maven.plugins</groupId>
++        <artifactId>maven-source-plugin</artifactId>
++        <executions>
++          <execution>
++            <id>attach-sources</id>
++            <!-- <phase>package</phase> package is the default -->
++            <goals>
++              <goal>jar-no-fork</goal>
++            </goals>
++          </execution>
++          <execution>
++            <id>attach-sources-test</id>
++            <goals>
++              <goal>test-jar-no-fork</goal>
++            </goals>
++          </execution>
++        </executions>
++      </plugin>
++
++    </plugins>
++  </build>
++  
++</project>

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/DatabaseMgr.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/DatabaseMgr.java
index 0000000,0000000..098a646
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/DatabaseMgr.java
@@@ -1,0 -1,0 +1,114 @@@
++/*
++ * 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.seaborne.tdb2;
++
++import org.apache.jena.query.Dataset ;
++import org.apache.jena.sparql.core.DatasetGraph ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.tdb2.store.DatasetGraphSwitchable ;
++import org.seaborne.tdb2.store.DatasetGraphTDB ;
++import org.seaborne.tdb2.sys.DatabaseConnection ;
++import org.seaborne.tdb2.sys.DatabaseOps ;
++import org.seaborne.tdb2.sys.TDBInternal ;
++
++/** Operations for TDBS DatasetGraph, including admin operations 
++ * See {@link TDB2Factory} for creating API-level {@link Dataset Datasets}.
++ * 
++ * @see TDB2Factory
++ */
++public class DatabaseMgr {
++
++    // All creation of DatasetGraph for TDB2 goes through this method.
++    private static DatasetGraph DB_ConnectCreate(Location location) {
++        return DatabaseConnection.connectCreate(location).getDatasetGraph();
++//        // One level.
++//        return StoreConnection.connectCreate(location).getDatasetGraph() ; 
++    }
++
++    /** Create or connect to a TDB2-backed dataset */
++    public static DatasetGraph connectDatasetGraph(Location location) {
++        return DB_ConnectCreate(location); 
++    }
++
++    /** Create or connect to a TDB2-backed dataset */
++    public static DatasetGraph connectDatasetGraph(String location) {
++        return connectDatasetGraph(Location.create(location)) ;
++    }
++    
++    /**
++     * Compact a datasets which must be a switchable TDB database.
++     * This is the normal dataset type for on-disk TDB2 databases.
++     *  
++     * @param container
++     */
++    public static void compact(DatasetGraph container) {
++        DatasetGraphSwitchable dsg = requireSwitchable(container);
++        DatabaseOps.compact(dsg);
++    }
++
++    /**
++     * Create a backup for a switchable TDB database. This is the normal dataset type for
++     * on-disk TDB2 databases.
++     * <p>
++     * The backup is created in the databases folder, under "Backups".
++     * <p>
++     * Backup creates a consistent copy og the database. It is performed as a read-transaction
++     * and does not lock out other use of the dataset.
++     * 
++     * @param container
++     * @return File name of the backup.
++     */
++    public static String backup(DatasetGraph container) {
++        DatasetGraphSwitchable dsg = requireSwitchable(container);
++        return DatabaseOps.backup(dsg);
++    }
++
++
++    /** Create an in-memory TDB2-backed dataset (for testing) */
++    public static DatasetGraph createDatasetGraph() {
++        return connectDatasetGraph(Location.mem()) ;
++    }
++
++    /** Return the location of a dataset if it is backed by TDB, else null */ 
++    public static boolean isBackedByTDB(DatasetGraph datasetGraph) {
++        return TDBInternal.isBackedByTDB(datasetGraph);
++    }
++
++    /** Return the location of a DatasetGraph if it is backed by TDB, else null. */
++    public static Location location(DatasetGraph datasetGraph) {
++        DatasetGraphSwitchable dsg = requireSwitchable(datasetGraph);
++        if ( dsg == null )
++            return null ;
++        return dsg.getLocation();
++    }
++
++    private static DatasetGraphSwitchable requireSwitchable(DatasetGraph datasetGraph) {
++        if ( datasetGraph instanceof DatasetGraphSwitchable )
++            return (DatasetGraphSwitchable)datasetGraph;
++        throw new TDBException("Not a switchable TDB database");
++    }
++
++    static DatasetGraphTDB requireDirect(DatasetGraph datasetGraph) {
++        DatasetGraphTDB dsg = TDBInternal.getDatasetGraphTDB(datasetGraph);
++        if ( dsg == null )
++            throw new TDBException("Not a TDB database (argument is neither a switchable nor direct TDB DatasetGraph)");
++        return dsg;
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/TDB2.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/TDB2.java
index 0000000,0000000..6a18faa
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/TDB2.java
@@@ -1,0 -1,0 +1,268 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2 ;
++
++import org.apache.jena.atlas.lib.Sync ;
++import org.apache.jena.graph.Graph ;
++import org.apache.jena.ontology.OntModel ;
++import org.apache.jena.ontology.impl.OntModelImpl ;
++import org.apache.jena.query.ARQ ;
++import org.apache.jena.rdf.model.Model ;
++import org.apache.jena.reasoner.InfGraph ;
++import org.apache.jena.riot.lang.ReaderRIOTRDFXML;
++import org.apache.jena.sparql.SystemARQ ;
++import org.apache.jena.sparql.core.assembler.AssemblerUtils ;
++import org.apache.jena.sparql.engine.main.StageBuilder ;
++import org.apache.jena.sparql.engine.main.StageGenerator ;
++import org.apache.jena.sparql.lib.Metadata ;
++import org.apache.jena.sparql.mgt.SystemInfo ;
++import org.apache.jena.sparql.util.Context ;
++import org.apache.jena.sparql.util.MappingRegistry ;
++import org.apache.jena.sparql.util.Symbol ;
++import org.apache.jena.system.JenaSystem ;
++import org.seaborne.tdb2.assembler.VocabTDB2 ;
++import org.seaborne.tdb2.modify.UpdateEngineTDB ;
++import org.seaborne.tdb2.solver.QueryEngineTDB ;
++import org.seaborne.tdb2.solver.StageGeneratorDirectTDB ;
++import org.seaborne.tdb2.sys.EnvTDB ;
++import org.seaborne.tdb2.sys.SystemTDB ;
++import org.seaborne.tdb2.sys.TDBInternal ;
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++public class TDB2 {
++    /** IRI for TDB */
++    public static final String  tdbIRI                           = "http://seaborne.org/#tdb" ;
++
++    /** Root of TDB-defined parameter names */
++    public static final String  tdbParamNS                       = "http://seaborne.org/TDB#" ;
++
++    /** Prefix for TDB-defined parameter names */
++    public static final String  tdbSymbolPrefix                  = "tdb" ;
++
++    // Internal logging
++    private static final Logger log                              = LoggerFactory.getLogger(TDB2.class) ;
++
++    /** Logger for loading information */
++    public static final String  logLoaderName                    = "org.apache.jena.tdb.loader" ;
++    /** Logger for loading information */
++    public static final Logger  logLoader                        = LoggerFactory.getLogger(logLoaderName) ;
++
++    /** Logger for general information */
++    public static final String  logInfoName                      = "org.apache.jena.info" ;
++    /** Logger for general information */
++    public static final Logger  logInfo                          = LoggerFactory.getLogger(logInfoName) ;
++
++    // /** Logger for execution information */
++    // public static final String logExecName = "org.apache.jena.tdb.exec" ;
++    // /** Logger for execution information */
++    // public static final Logger logExec = LoggerFactory.getLogger(logExecName)
++    // ;
++
++    public final static String  namespace                        = "http://jena.apache.org/2016/tdb#" ;
++
++    /** Symbol to use the union of named graphs as the default graph of a query */
++    public static final Symbol  symUnionDefaultGraph             = SystemTDB.allocSymbol("unionDefaultGraph") ;
++
++    /**
++     * A String enum Symbol that specifies the type of temporary storage for
++     * transaction journal write blocks.
++     * <p/>
++     * "mem" = Java heap memory (default) <br>
++     * "direct" = Process heap memory <br>
++     * "mapped" = Memory mapped temporary file <br>
++     */
++    public static final Symbol  transactionJournalWriteBlockMode = SystemTDB.allocSymbol("transactionJournalWriteBlockMode") ;
++
++    public static Context getContext() {
++        return ARQ.getContext() ;
++    }
++
++    /**
++     * Release any and all system resources held by TDB.
++     * All release datasets or graphs held by client code are no longer valid. 
++     */
++    public static void closedown() {
++        TDBInternal.reset() ;
++    }
++
++    /** Sync a TDB-backed Model. Do nothing if not TDB-backed. */
++    public static void sync(Model model) {
++        if ( model instanceof OntModelImpl ) {
++            OntModelImpl ontModel = (OntModelImpl)model ;
++            sync(ontModel.getBaseGraph()) ;
++            return ;
++        }
++        // This never happens (there is only one OntModel implementation)
++        if ( model instanceof OntModel ) {
++            OntModel ontModel = (OntModel)model ;
++            sync(ontModel.getBaseModel()) ;
++            return ;
++        }
++
++        sync(model.getGraph()) ;
++    }
++
++    /** Sync a TDB-backed Graph. Do nothing if not TDB-backed. */
++    public static void sync(Graph graph) {
++        if ( graph == null )
++            return ;
++
++        if ( graph instanceof InfGraph ) {
++            InfGraph infGraph = (InfGraph)graph ;
++            sync(infGraph.getRawGraph()) ;
++            return ;
++        }
++        syncObject(graph) ;
++    }
++
++//    /** Sync a TDB-backed Dataset. Do nothing if not TDB-backed. */
++//    public static void sync(Dataset dataset) {
++//        if ( dataset == null )
++//            return ;
++//        DatasetGraph ds = dataset.asDatasetGraph() ;
++//        sync(ds) ;
++//    }
++//
++//    /** Sync a TDB-backed DatasetGraph. Do nothing if not TDB-backed. */
++//    public static void sync(DatasetGraph dataset) {
++//        if ( dataset == null )
++//            return ;
++//        
++//        // Should be: SystemARQ.sync(dataset) ;
++//        if ( dataset instanceof DatasetGraphTDB ) {
++//            syncObject(dataset) ;
++//            return ;
++//        }
++//
++//        if ( dataset instanceof DatasetGraphTransaction ) {
++//            DatasetGraphTransaction dsgt = (DatasetGraphTransaction)dataset ;
++//            // This only sync if the dataset has not been used transactionally.
++//            // Can't sync transactional datasets (it's meaningless)
++//            dsgt.syncIfNotTransactional() ;
++//            return ;
++//        }
++//
++//        // May be a general purpose dataset with TDB objects in it.
++//        sync(dataset.getDefaultGraph()) ;
++//        Iterator<Node> iter = dataset.listGraphNodes() ;
++//        iter = Iter.toList(iter).iterator() ; // Avoid iterator concurrency.
++//        for (; iter.hasNext();) {
++//            Node n = iter.next() ;
++//            Graph g = dataset.getGraph(n) ;
++//            sync(g) ;
++//        }
++//    }
++
++    /**
++     * Sync a TDB synchronizable object (model, graph, dataset). If force is
++     * true, synchronize as much as possible (e.g. file metadata) else make a
++     * reasonable attenpt at synchronization but does not gauarantee disk state.
++     * Do nothing otherwise
++     */
++    private static void syncObject(Object object) {
++        if ( object == null )
++            return ;
++        if ( object instanceof Sync )
++            ((Sync)object).sync() ;
++    }
++
++    private static final Object initLock = new Object() ;
++    private static volatile boolean initialized = false ;
++    static { JenaSystem.init(); }
++    
++    /**
++     * TDB System initialization - normally, this is not explicitly called
++     * because all routes to use TDB will cause initialization to occur.
++     * However, calling it repeatedly is safe and low cost.
++     */
++    public static void init() {
++        if ( initialized )
++            return ;
++        synchronized(initLock) {
++            if ( initialized ) {
++                if ( JenaSystem.DEBUG_INIT )
++                    System.err.println("TDB2.init - return") ;
++                return ;
++            }
++            initialized = true ;
++            if ( JenaSystem.DEBUG_INIT )
++                System.err.println("TDB2.init - start") ;
++
++            SystemTDB.init() ;
++            ARQ.init() ;
++            ReaderRIOTRDFXML.RiotUniformCompatibility = true ;
++            EnvTDB.processGlobalSystemProperties() ;
++
++            MappingRegistry.addPrefixMapping(SystemTDB.tdbSymbolPrefix, SystemTDB.symbolNamespace) ;
++            AssemblerUtils.init() ;
++            VocabTDB2.init() ;
++            QueryEngineTDB.register() ;
++            UpdateEngineTDB.register() ;
++            MappingRegistry.addPrefixMapping(TDB2.tdbSymbolPrefix, TDB2.tdbParamNS) ;
++
++            wireIntoExecution() ;
++            if ( JenaSystem.DEBUG_INIT )
++                System.err.println("TDB.init - finish") ;
++        }
++    }
++
++    private static void wireIntoExecution() {
++        // Globally change the stage generator to intercept BGP on TDB
++        // Globally change the stage generator to intercept BGP on TDB
++        Context cxt = ARQ.getContext() ;
++        StageGenerator orig = StageBuilder.chooseStageGenerator(cxt) ; 
++
++        // Wire in the TDB stage generator which will make TDB work whether
++        // or not the TDB executor is used. This means that datasets of mixed
++        // graph types inside a general purpose dataset work.
++        StageGenerator stageGenerator = new StageGeneratorDirectTDB(orig) ;
++        StageBuilder.setGenerator(ARQ.getContext(), stageGenerator) ;
++    }
++
++    // ---- Static constants read by modVersion
++    // ---- Must be after initialization.
++
++    static private String      metadataLocation = "org/apache/jena/tdb/tdb-properties.xml" ;
++    static private Metadata    metadata         = new Metadata(metadataLocation) ;
++
++    /** The root package name for TDB */
++    public static final String PATH             = "org.apache.jena.tdb" ;
++
++    // The names known to ModVersion : "NAME", "VERSION", "BUILD_DATE"
++
++    public static final String NAME             = "TDB" ;
++
++    /** The full name of the current TDB version */
++    public static final String VERSION          = metadata.get(PATH + ".version", "DEV") ;
++
++    /** The date and time at which this release was built */
++    public static final String BUILD_DATE       = metadata.get(PATH + ".build.datetime", "unset") ;
++
++    // Final initialization (in case any statics in this file are important).
++    static {
++        initialization2() ;
++    }
++
++    private static void initialization2() {
++        // Set management information.
++        SystemInfo systemInfo = new SystemInfo(TDB2.tdbIRI, TDB2.PATH, TDB2.VERSION, TDB2.BUILD_DATE) ;
++        SystemARQ.registerSubSystem(systemInfo) ;
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/TDB2Factory.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/TDB2Factory.java
index 0000000,0000000..93cc3d2
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/TDB2Factory.java
@@@ -1,0 -1,0 +1,124 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2;
++
++import org.apache.jena.query.Dataset ;
++import org.apache.jena.query.DatasetFactory ;
++import org.apache.jena.sparql.core.DatasetGraph ;
++import org.seaborne.dboe.base.file.Location ;
++
++/**
++ *  Public factory for connecting to and creating datasets backed by TDB2 storage.
++ */
++public class TDB2Factory
++{
++    private TDB2Factory() {} 
++
++    /** @deprecated Use {@link DatabaseMgr#connectDatasetGraph(Location)} */
++    @Deprecated
++    public static DatasetGraph createDatasetGraph(Location location) {
++        return DatabaseMgr.connectDatasetGraph(location);
++    }
++    
++    /** @deprecated Use {@link #connectDataset(Location)} */
++    @Deprecated
++    public static Dataset createDataset(Location location) {
++        return connectDataset(location);
++    }
++    
++    /** Create or connect to a TDB2-backed dataset */
++    public static Dataset connectDataset(Location location) {
++        DatasetGraph dsg = DatabaseMgr.connectDatasetGraph(location) ;
++        return DatasetFactory.wrap(dsg) ;
++    }
++
++    /** @deprecated Use {@link DatabaseMgr#connectDatasetGraph(String)} */
++    @Deprecated
++    public static DatasetGraph createDatasetGraph(String location) {
++        return DatabaseMgr.connectDatasetGraph(location);
++    }
++    
++    /** @deprecated Use {@link #connectDataset(String)} */
++    @Deprecated
++    public static Dataset createDataset(String location) {
++        return connectDataset(location);
++    }
++    
++    /** Create or connect to a TDB2-backed dataset */
++    public static Dataset connectDataset(String location) {
++        return connectDataset(Location.create(location)) ;    }
++
++    /**
++     * Create an in-memory TDB2-backed dataset (for testing). In-memory TDB2 datasets are use
++     * a simple simulation of disk I/O to give exact semantics, which is useful to create
++     * tests that run fast where setup and teardown of datasets can be the major cost.
++     * <p> 
++     * In-memory TDB2 datasets are not designed to scale, nor provide efficient execution for
++     * applications for long-term use. 
++     */ 
++    public static Dataset createDataset() { return connectDataset(Location.mem()) ; }
++
++//    /**
++//     *  Read the file and assembler a dataset
++//     */
++//    public static Dataset assembleDataset(String assemblerFile) {
++//        return (Dataset)AssemblerUtils.build(assemblerFile, VocabTDB.tDatasetTDB) ;
++//    }
++//    
++//    /** Release from the JVM. All caching is lost. */
++//    public static void release(Dataset dataset) {
++//        _release(location(dataset)) ;
++//    }
++//    
++//    /** Release from the JVM.  All caching is lost. */
++//    public static void release(DatasetGraph dataset) {
++//        _release(location(dataset)) ;
++//    }
++
++    /** Tes whether a dataset is backed by TDB or not. */ 
++    public static boolean isBackedByTDB(Dataset dataset) {
++        DatasetGraph dsg = dataset.asDatasetGraph() ;
++        return DatabaseMgr.isBackedByTDB(dsg) ;
++    }
++    
++    /** Return the location of a dataset if it is backed by TDB, else null */
++    public static Location location(Dataset dataset) {
++        DatasetGraph dsg = dataset.asDatasetGraph() ;
++        return DatabaseMgr.location(dsg) ;
++    }
++
++//    /** Set the {@link StoreParams} for specific Location.
++//     *  This call must only be called before a dataset from Location
++//     *  is created. This operation should be used with care; bad choices
++//     *  of {@link StoreParams} can reduce performance.
++//     *  
++//     *  <a href="http://jena.apache.org/documentation/tdb/store-paramters.html"
++//     *  >See documentation</a>.
++//     *  
++//     *  @param location  The persistent storage location
++//     *  @param params  StoreParams to use
++//     *  @throws IllegalStateException If the dataset has already been setup.
++//     */
++//    public static void setup(Location location, StoreParams params) {
++//        StoreConnection sConn = StoreConnection.getExisting(location) ;
++//        if ( sConn != null )
++//            throw new IllegalStateException("Location is already active") ;
++//        StoreConnection.make(location, params) ;
++//    }
++    
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/TDBException.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/TDBException.java
index 0000000,0000000..2e0ea26
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/TDBException.java
@@@ -1,0 -1,0 +1,28 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2;
++
++import org.apache.jena.shared.JenaException ;
++
++public class TDBException extends JenaException
++{
++    public TDBException()                          { super() ; }
++    public TDBException(String msg)                { super(msg) ; }
++    public TDBException(Throwable th)              { super(th) ; }
++    public TDBException(String msg, Throwable th)  { super(msg, th) ; }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/assembler/DatasetAssemblerTDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/assembler/DatasetAssemblerTDB.java
index 0000000,0000000..dc2d67a
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/assembler/DatasetAssemblerTDB.java
@@@ -1,0 -1,0 +1,79 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.assembler;
++
++import static org.apache.jena.sparql.util.graph.GraphUtils.exactlyOneProperty ;
++import static org.apache.jena.sparql.util.graph.GraphUtils.getStringValue ;
++import static org.seaborne.tdb2.assembler.VocabTDB2.pLocation ;
++import static org.seaborne.tdb2.assembler.VocabTDB2.pUnionDefaultGraph ;
++
++import org.apache.jena.assembler.Assembler ;
++import org.apache.jena.assembler.Mode ;
++import org.apache.jena.assembler.exceptions.AssemblerException ;
++import org.apache.jena.atlas.logging.Log ;
++import org.apache.jena.graph.Node ;
++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.assembler.AssemblerUtils ;
++import org.apache.jena.sparql.core.assembler.DatasetAssembler ;
++import org.apache.jena.sparql.expr.NodeValue ;
++import org.apache.jena.system.JenaSystem ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.tdb2.DatabaseMgr ;
++import org.seaborne.tdb2.TDB2 ;
++
++public class DatasetAssemblerTDB extends DatasetAssembler
++{
++    static { JenaSystem.init(); }
++    
++    @Override
++    public Dataset createDataset(Assembler a, Resource root, Mode mode) {
++        TDB2.init() ;
++        return make(root) ;
++    }
++
++    static Dataset make(Resource root) {
++        if ( !exactlyOneProperty(root, pLocation) )
++            throw new AssemblerException(root, "No location given") ;
++
++        String dir = getStringValue(root, pLocation) ;
++        Location loc = Location.create(dir) ;
++        DatasetGraph dsg = DatabaseMgr.connectDatasetGraph(loc) ;
++
++        if ( root.hasProperty(pUnionDefaultGraph) ) {
++            Node b = root.getProperty(pUnionDefaultGraph).getObject().asNode() ;
++            NodeValue nv = NodeValue.makeNode(b) ;
++            if ( nv.isBoolean() )
++                dsg.getContext().set(TDB2.symUnionDefaultGraph, nv.getBoolean()) ;
++            else
++                Log.warn(DatasetAssemblerTDB.class, "Failed to recognize value for union graph setting (ignored): " + b) ;
++        }
++
++        /*
++        <r> rdf:type tdb:DatasetTDB2 ;
++            tdb:location "dir" ;
++            //ja:context [ ja:cxtName "arq:queryTimeout" ;  ja:cxtValue "10000" ] ;
++            tdb:unionGraph true ; # or "true"
++        */
++        AssemblerUtils.setContext(root, dsg.getContext());
++        return DatasetFactory.wrap(dsg) ; 
++    }
++    
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/assembler/TDBGraphAssembler.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/assembler/TDBGraphAssembler.java
index 0000000,0000000..eebe51b
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/assembler/TDBGraphAssembler.java
@@@ -1,0 -1,0 +1,124 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.assembler;
++
++import static org.apache.jena.sparql.util.graph.GraphUtils.getAsStringValue ;
++import static org.apache.jena.sparql.util.graph.GraphUtils.getResourceValue ;
++import static org.apache.jena.sparql.util.graph.GraphUtils.getStringValue ;
++import static org.seaborne.tdb2.assembler.VocabTDB2.* ;
++
++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.atlas.logging.Log ;
++import org.apache.jena.query.Dataset ;
++import org.apache.jena.rdf.model.* ;
++import org.apache.jena.riot.out.NodeFmtLib ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.tdb2.TDB2 ;
++import org.seaborne.tdb2.TDBException ;
++import org.seaborne.tdb2.TDB2Factory ;
++
++public class TDBGraphAssembler extends AssemblerBase implements Assembler
++{
++    @Override
++    public Model open(Assembler a, Resource root, Mode mode)
++    {
++        // In case we go via explicit index construction,
++        // although given we got here, the assembler is wired in
++        // and that probably means TDB.init
++        TDB2.init() ;
++        
++        // Make a model - the default model of the TDB dataset
++        // [] rdf:type tdb:GraphTDB ;
++        //    tdb:location "dir" ;
++        
++        // Make a named model.
++        // [] rdf:type tdb:GraphTDB ;
++        //    tdb:location "dir" ;
++        //    tdb:graphName <http://example/name> ;
++
++        // Location or dataset reference.
++        String locationDir = getStringValue(root, pLocation) ;
++        Resource dataset = getResourceValue(root, pDataset) ;
++        
++        if ( locationDir != null && dataset != null )
++            throw new AssemblerException(root, "Both location and dataset given: exactly one required") ; 
++        
++        if ( locationDir == null && dataset == null )
++            throw new AssemblerException(root, "Must give location or refer to a dataset description") ;
++        
++        String graphName = null ;
++        if ( root.hasProperty(pGraphName1) )
++            graphName = getAsStringValue(root, pGraphName1) ;
++        if ( root.hasProperty(pGraphName2) )
++            graphName = getAsStringValue(root, pGraphName2) ;
++
++        if ( root.hasProperty(pIndex) )
++            Log.warn(this, "Custom indexes not implemented yet - ignored") ;
++
++        final Dataset ds ;
++        
++        if ( locationDir != null )
++        {
++            Location location = Location.create(locationDir) ;
++            ds = TDB2Factory.connectDataset(location) ;
++        }
++        else
++            ds = DatasetAssemblerTDB.make(dataset) ;
++
++        try {
++            if ( graphName != null )
++                return ds.getNamedModel(graphName) ;
++            else
++                return ds.getDefaultModel() ;
++        } catch (RuntimeException ex)
++        {
++            ex.printStackTrace(System.err) ;
++            throw ex ;
++        }
++    }
++    
++    //@Unused
++    private void indexes(Resource root)
++    {
++        // ---- API ways
++
++        StmtIterator sIter = root.listProperties(pIndex) ;
++        while(sIter.hasNext())
++        {
++            RDFNode obj = sIter.nextStatement().getObject() ;
++            if ( obj.isLiteral() )
++            {
++                String desc = ((Literal)obj).getString() ;
++                System.out.printf("Index: %s\n", desc) ; System.out.flush();
++                continue ;
++            }
++            throw new TDBException("Wrong format for tdb:index: should be a string: found: "+NodeFmtLib.displayStr(obj)) ; 
++            //          Resource x = (Resource)obj ;
++            //          String desc = x.getProperty(pDescription).getString() ;
++            //          String file = x.getProperty(pFile).getString() ;
++            //          System.out.printf("Index: %s in file %s\n", desc, file) ; System.out.flush();
++        }
++
++        System.out.flush();
++        throw new TDBException("Custom indexes turned off") ; 
++    }
++
++}


[25/50] [abbrv] jena git commit: JENA-1396: Merge //github/afs/mantis as subdirectory jena-db/

Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/value/TestNodeIdInline.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/value/TestNodeIdInline.java
index 0000000,0000000..5aeafe5
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/value/TestNodeIdInline.java
@@@ -1,0 -1,0 +1,285 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.value;
++
++import static org.junit.Assert.*;
++import org.apache.jena.datatypes.xsd.XSDDatatype;
++import org.apache.jena.graph.Node;
++import org.apache.jena.graph.NodeFactory;
++import org.apache.jena.sparql.expr.NodeValue;
++import org.apache.jena.sparql.util.NodeFactoryExtra;
++import org.junit.Test;
++import org.seaborne.tdb2.store.NodeId;
++
++public class TestNodeIdInline
++{
++    @Test public void nodeId_int_01()
++    { test("'1'^^xsd:integer") ; }
++
++    @Test public void nodeId_int_02()
++    { test("2") ; }
++
++    @Test public void nodeId_int_03()
++    { test("'3'^^xsd:int") ; }
++
++    @Test public void nodeId_int_04()
++    { testNoInline("'3'") ; }
++
++    @Test public void nodeId_int_05()
++    { test("-1") ; }
++    
++    @Test public void nodeId_int_06()
++    { test("-180") ; }
++
++    @Test public void nodeId_int_07()
++    { test("01",  "1") ; }
++    
++    @Test public void nodeId_int_08()
++    { test("+01",  "1") ; }
++    
++    @Test public void nodeId_int_09()
++    // More than Long.MAX_VALUE
++    { testNoInline("92233720368547758070") ; }
++
++    // On the edge.
++    
++    static final long X = 1L<<55 ;        // Just too large 
++    static final long Y = -((1L<<55) +1) ;   // Just too small 
++    
++    @Test public void nodeId_int_10()
++    { testNoInline("\""+Long.toString(X)+"\"^^xsd:integer") ; }
++
++    @Test public void nodeId_int_11()
++    { 
++        Node n = NodeValue.makeInteger(X-1).asNode() ;
++        test("\""+Long.toString(X-1)+"\"^^xsd:integer",  n) ; 
++    }
++
++    @Test public void nodeId_int_12()
++    { testNoInline("\""+Long.toString(Y)+"\"^^xsd:integer") ; }
++
++    @Test public void nodeId_int_13()
++    { 
++        Node n = NodeValue.makeInteger(Y+1).asNode() ;
++        test("\""+Long.toString(Y+1)+"\"^^xsd:integer",  n) ; 
++    }
++
++    @Test public void nodeId_int_20()
++    { test("'30'^^xsd:positiveInteger") ; }
++    
++    @Test public void nodeId_int_21()
++    { testNoInline("'300'^^xsd:byte") ; }
++
++    @Test public void nodeId_decimal_1()
++    { test("3.14", "3.14") ; }
++
++    @Test public void nodeId_decimal_2()
++    { testNoInline("123456789.123456789") ; }
++    
++    // Just this once, directly create the Node.
++    @Test public void nodeId_decimal_3()
++    { test("12.89", NodeFactory.createLiteral("12.89", XSDDatatype.XSDdecimal)) ; }
++
++    @Test public void nodeId_decimal_4()
++    { test("-1.0",  "-1.0") ; }
++    
++    // This number has > 47 bits of value : 2412.80478192688
++    @Test public void nodeId_decimal_5()
++    { testNoInline("2412.80478192688") ; }
++    
++    // This number has > 47 bits of value : -2412.80478192688
++    @Test public void nodeId_decimal_6()
++    { testNoInline("-2412.80478192688") ; }
++
++    @Test public void nodeId_decimal_7()
++    { test("'0.00000001'^^xsd:decimal",  
++           NodeFactory.createLiteral("0.00000001", XSDDatatype.XSDdecimal)) ; 
++    }
++
++    @Test public void nodeId_decimal_8()
++    { test("0.00000001", NodeFactory.createLiteral("0.00000001", XSDDatatype.XSDdecimal)) ; }
++
++    @Test public void nodeId_dateTime_01()
++    { test("'2008-04-28T15:36:15+01:00'^^xsd:dateTime") ; }
++
++    @Test public void nodeId_dateTime_02()
++    { test("'2008-04-28T15:36:15Z'^^xsd:dateTime") ; }
++
++    @Test public void nodeId_dateTime_03()
++    { test("'2008-04-28T15:36:15+00:00'^^xsd:dateTime") ; }
++
++    @Test public void nodeId_dateTime_04()
++    { test("'2008-04-28T15:36:15-05:00'^^xsd:dateTime") ; }
++
++    // No timezone.
++    @Test public void nodeId_dateTime_05()
++    { test("'2008-04-28T15:36:15'^^xsd:dateTime") ; }
++
++    // Note the trailing zero - system does not preserve perfect lexical forms. 
++    @Test public void nodeId_dateTime_06()
++    { test("'2008-04-28T15:36:05.450'^^xsd:dateTime", "'2008-04-28T15:36:05.45'^^xsd:dateTime") ; }
++
++    // Old Java bug, now fixed: T24:00:00 not accepted by DatatypeFactory.newXMLGregorianCalendar(lex)
++    // Note the Jena value is an XSDDateTime so it retains the "24"/"00" next day distinction. 
++    @Test public void nodeId_dateTime_07()
++    { test("'2008-04-28T24:00:00'^^xsd:dateTime") ; }
++    
++    // Out of range.
++    @Test public void nodeId_dateTime_08()
++    { testNoInline("'8008-04-28T15:36:05.45'^^xsd:dateTime") ; }
++
++    @Test public void nodeId_dateTime_09()
++    { test("'2008-04-28T15:36:05.001'^^xsd:dateTime") ; }
++    
++    @Test public void nodeId_dateTime_10()
++    { test("'2008-04-28T15:36:05.01'^^xsd:dateTime") ; }
++
++    @Test public void nodeId_dateTime_11()
++    { test("'2008-04-28T15:36:05.1'^^xsd:dateTime") ; }
++
++    // Canonicalization test - fractional seconds.
++    @Test public void nodeId_dateTime_12()
++    { test("'2008-04-28T15:36:05.010'^^xsd:dateTime", "'2008-04-28T15:36:05.01'^^xsd:dateTime") ; }
++
++    @Test public void nodeId_dateTime_13()
++    { test("'2008-04-28T15:36:05.100'^^xsd:dateTime", "'2008-04-28T15:36:05.1'^^xsd:dateTime") ; }
++
++    @Test public void nodeId_dateTime_14()
++    { test("'2012-07-29T20:39:11.100+01:15'^^xsd:dateTime", "'2012-07-29T20:39:11.1+01:15'^^xsd:dateTime") ; }
++
++    @Test public void nodeId_dateTime_15()
++    { test("'2012-07-29T20:39:11.100-01:15'^^xsd:dateTime", "'2012-07-29T20:39:11.1-01:15'^^xsd:dateTime") ; }
++
++    @Test public void nodeId_dateTime_16()
++    { test("'2012-07-29T20:39:11.100+01:30'^^xsd:dateTime", "'2012-07-29T20:39:11.1+01:30'^^xsd:dateTime") ; }
++
++    @Test public void nodeId_dateTime_17()
++    { test("'2012-07-29T20:39:11.100-01:45'^^xsd:dateTime", "'2012-07-29T20:39:11.1-01:45'^^xsd:dateTime") ; }
++
++    @Test public void nodeId_dateTime_18()
++    { test("'2012-07-29T20:39:11.100Z'^^xsd:dateTime", "'2012-07-29T20:39:11.1Z'^^xsd:dateTime") ; }
++
++    @Test public void nodeId_dateTimeStamp_01()
++    { test("'2012-07-29T20:39:11.100Z'^^xsd:dateTimeStamp", "'2012-07-29T20:39:11.1Z'^^xsd:dateTimeStamp") ; }
++
++    
++    @Test public void nodeId_date_1()
++    { test("'2008-04-28Z'^^xsd:date", "'2008-04-28Z'^^xsd:date") ; }
++
++    @Test public void nodeId_date_2()
++    { test("'2008-04-28+00:00'^^xsd:date", "'2008-04-28+00:00'^^xsd:date") ; }
++
++    @Test public void nodeId_date_3()
++    { test("'2008-04-28-05:00'^^xsd:date", "'2008-04-28-05:00'^^xsd:date") ; }
++
++    @Test public void nodeId_date_4()
++    { test("'2008-04-28+02:00'^^xsd:date", "'2008-04-28+02:00'^^xsd:date") ; }
++
++    @Test public void nodeId_date_5()
++    { testNoInline("'8008-04-28'^^xsd:date") ; }
++
++    @Test public void nodeId_date_6()
++    { test("'2012-07-29+06:15'^^xsd:date", "'2012-07-29+06:15'^^xsd:date") ; }
++
++    @Test public void nodeId_date_7()
++    { test("'2012-07-29-06:30'^^xsd:date", "'2012-07-29-06:30'^^xsd:date") ; }
++
++    @Test public void nodeId_boolean_1()
++    { test("'true'^^xsd:boolean", "'true'^^xsd:boolean") ; }
++
++    @Test public void nodeId_boolean_2()
++    { test("'false'^^xsd:boolean", "'false'^^xsd:boolean") ; }
++
++    @Test public void nodeId_boolean_3()
++    { test("'1'^^xsd:boolean", "'true'^^xsd:boolean") ; }
++
++    @Test public void nodeId_boolean_4()
++    { test("'0'^^xsd:boolean", "'false'^^xsd:boolean") ; }
++
++    // Many tests in TestDoubleNode62
++    
++    @Test public void nodeId_double_1()
++    { test("1.0e0", NodeFactory.createLiteral("1.0e0", XSDDatatype.XSDdouble)) ; }
++
++    // Becomes the RDF/SPARQL preferred form - with expooent - for pretty printing.
++    @Test public void nodeId_double_2()
++    { test("'-0001.0'^^xsd:double", NodeFactory.createLiteral("-1.0e0", XSDDatatype.XSDdouble)) ; }
++
++    @Test public void nodeId_double_3()
++    { test("'NaN'^^xsd:double", NodeFactory.createLiteral("NaN", XSDDatatype.XSDdouble)) ; }
++
++    @Test public void nodeId_double_4()
++    { test("'INF'^^xsd:double", NodeFactory.createLiteral("INF", XSDDatatype.XSDdouble)) ; }
++
++    @Test public void nodeId_double_5()
++    { test("'-INF'^^xsd:double", NodeFactory.createLiteral("-INF", XSDDatatype.XSDdouble)) ; }
++
++    @Test public void nodeId_double_6()
++    { test("'1.1E9'^^xsd:double") ; }
++  
++    @Test public void nodeId_double_7()
++    { test("1.1E9") ; }
++    
++    @Test public void nodeId_double_8()
++    { testNoInline("1.1E80") ; }
++    
++    @Test public void nodeId_double_9()
++    { testNoInline("1.1E-80") ; }
++
++    @Test public void nodeId_float_1()
++    { test("'0.0'^^xsd:float") ; }
++
++    @Test public void nodeId_float_2()
++    { test("'0'^^xsd:float", "'0.0'^^xsd:float") ; }
++    
++    @Test public void nodeId_float_3()
++    { testNoInline("'x'^^xsd:float") ; }
++    
++    @Test public void nodeId_float_4()
++    { test("'1.1e1'^^xsd:float", "'11.0'^^xsd:float") ; }
++
++    @Test public void nodeId_float_5()
++    { test("'1.1E9'^^xsd:float") ; }
++
++    private void test(String x) { test(x, x) ; }
++    
++    private void test(String x, String expected) {
++        test(x, NodeFactoryExtra.parseNode(expected)) ;
++    }
++
++    private void testNoInline(String x) {
++        Node n = NodeFactoryExtra.parseNode(x) ;
++        NodeId nodeId = NodeId.inline(n) ;
++        assertNull("Converted NodeId but expected no inline form: "+x, nodeId) ;
++    }
++    
++    private void test(String x, Node correct) {
++        Node n = NodeFactoryExtra.parseNode(x) ;
++        NodeId nodeId = NodeId.inline(n) ;
++        assertNotNull("Expected inlining: "+x, nodeId);
++        
++        boolean b = NodeId.hasInlineDatatype(n) ;
++        assertTrue("Converted NodeId but datatype test was false", b) ;
++        Node n2 = NodeId.extract(nodeId) ;
++        assertNotNull("Expected recovery", n2) ;
++        String s = "("+correct.getLiteralLexicalForm()+","+n2.getLiteralLexicalForm()+")" ;
++        assertTrue("Not same value: "+s, correct.sameValueAs(n2)) ;
++        // Term equality.
++        assertEquals("Not same term", correct, n2) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/sys/TS_Sys.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/sys/TS_Sys.java
index 0000000,0000000..7a37acc
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/sys/TS_Sys.java
@@@ -1,0 -1,0 +1,33 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.sys;
++
++
++import org.junit.runner.RunWith;
++import org.junit.runners.Suite;
++
++@RunWith(Suite.class)
++@Suite.SuiteClasses( {
++    TestDatabaseOps.class
++    , TestDatabaseConnection.class
++    , TestSys.class
++    //, TestOps.class
++})
++
++public class TS_Sys
++{}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/sys/TestDatabaseConnection.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/sys/TestDatabaseConnection.java
index 0000000,0000000..8e6bdcc
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/sys/TestDatabaseConnection.java
@@@ -1,0 -1,0 +1,69 @@@
++/*
++ * 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.seaborne.tdb2.sys;
++
++import static org.junit.Assert.assertNotSame ;
++import static org.junit.Assert.assertSame ;
++
++import org.apache.jena.atlas.lib.FileOps ;
++import org.apache.jena.sparql.core.DatasetGraph ;
++import org.junit.Test ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.tdb2.ConfigTest ;
++import org.seaborne.tdb2.sys.DatabaseConnection ;
++
++public class TestDatabaseConnection {
++    
++    @Test
++    public void testStoreConnectionTxn1() {
++        TDBInternal.reset() ;
++        // Only do disk things for test that need them (disk takes time!).
++        String DIRx = ConfigTest.getCleanDir() ;
++        Location DIR = Location.create(DIRx);
++        FileOps.clearDirectory(DIRx) ;
++        try {
++            DatasetGraph dg1 = DatabaseConnection.connectCreate(DIR).getDatasetGraph() ;
++            DatasetGraph dg2 = DatabaseConnection.connectCreate(DIR).getDatasetGraph() ;
++            assertSame(dg1, dg2) ;
++        }
++        finally {
++            FileOps.clearDirectory(DIRx) ;
++        }
++    }
++    
++    @Test
++    public void testStoreConnectionTxn2() {
++        // Named memory locations
++        TDBInternal.reset() ;
++        DatasetGraph dg1 = DatabaseConnection.connectCreate(Location.mem("FOO")).getDatasetGraph() ;
++        DatasetGraph dg2 = DatabaseConnection.connectCreate(Location.mem("FOO")).getDatasetGraph() ;
++        assertSame(dg1, dg2) ;
++    }
++    
++    @Test
++    public void testStoreConnectionTxn3() {
++        // Un-named memory locations
++        TDBInternal.reset() ;
++        DatasetGraph dg1 = DatabaseConnection.connectCreate(Location.mem()).getDatasetGraph() ;
++        DatasetGraph dg2 = DatabaseConnection.connectCreate(Location.mem()).getDatasetGraph() ;
++        assertNotSame(dg1, dg2) ;
++    }
++
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/sys/TestDatabaseOps.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/sys/TestDatabaseOps.java
index 0000000,0000000..fa90400
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/sys/TestDatabaseOps.java
@@@ -1,0 -1,0 +1,172 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.sys;
++
++import static org.junit.Assert.* ;
++import static org.junit.Assert.assertFalse ;
++import static org.junit.Assert.assertNotEquals ;
++import static org.junit.Assert.assertTrue ;
++
++import org.apache.commons.io.FileUtils ;
++import org.apache.jena.atlas.lib.FileOps ;
++import org.apache.jena.graph.Graph ;
++import org.apache.jena.graph.Triple ;
++import org.apache.jena.riot.RDFDataMgr ;
++import org.apache.jena.sparql.core.DatasetGraph ;
++import org.apache.jena.sparql.core.Quad ;
++import org.apache.jena.sparql.sse.SSE ;
++import org.junit.After ;
++import org.junit.Before ;
++import org.junit.Test ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.dboe.jenax.Txn ;
++import org.seaborne.tdb2.ConfigTest ;
++import org.seaborne.tdb2.DatabaseMgr ;
++import org.seaborne.tdb2.store.DatasetGraphSwitchable ;
++import org.seaborne.tdb2.store.DatasetGraphTDB ;
++
++public class TestDatabaseOps
++{
++    static String DIRx = ConfigTest.getCleanDir() ;
++    static Location DIR = Location.create(DIRx);
++    
++    static Quad quad1 = SSE.parseQuad("(_ <s> <p> 1)") ;
++    static Quad quad2 = SSE.parseQuad("(_ _:a <p> 2)") ;
++    static Triple triple1 = quad1.asTriple();
++    static Triple triple2 = quad2.asTriple();
++    static Triple triple3 = SSE.parseTriple("(<s> <q> 3)") ;
++    
++    @Before
++    public void before() {
++        FileUtils.deleteQuietly(IOX.asFile(DIR));
++        FileOps.ensureDir(DIR.getDirectoryPath());
++    }
++
++    @After  
++    public void after() {
++        TDBInternal.reset();
++        FileUtils.deleteQuietly(IOX.asFile(DIR));
++    }
++
++    @Test public void compact_dsg_1() {
++        DatasetGraph dsg = DatabaseMgr.connectDatasetGraph(DIR);
++        DatasetGraphSwitchable dsgs = (DatasetGraphSwitchable)dsg;
++        DatasetGraph dsg1 = dsgs.get();
++        Location loc1 = ((DatasetGraphTDB)dsg1).getLocation();
++        
++        Txn.executeWrite(dsg, ()-> {
++            dsg.add(quad2) ;
++            dsg.add(quad1) ;
++        }) ;
++        DatabaseMgr.compact(dsg);
++        
++        assertFalse(StoreConnection.isSetup(loc1));
++
++        DatasetGraph dsg2 = dsgs.get();
++        Location loc2 = ((DatasetGraphTDB)dsg2).getLocation();
++
++        assertNotEquals(dsg1, dsg2);
++        assertNotEquals(loc1, loc2);
++
++        Txn.executeRead(dsg, ()-> {
++            assertTrue(dsg.contains(quad2)) ;
++            assertTrue(dsg.contains(quad1)) ;
++        }) ;
++        
++        // dsg1 was closed and expelled. We must carefully reopen its storage only.
++        DatasetGraph dsgOld = StoreConnection.connectCreate(loc1).getDatasetGraph();
++        
++        Txn.executeWrite(dsgOld, ()->dsgOld.delete(quad2));
++        Txn.executeRead(dsg,     ()->assertTrue(dsg.contains(quad2)) );
++        Txn.executeRead(dsg2,    ()->assertTrue(dsg2.contains(quad2)) ) ;
++    }
++
++    @Test public void compact_graph_2() {
++        // graphs across compaction.
++        DatasetGraph dsg = DatabaseMgr.connectDatasetGraph(DIR);
++        Graph g = dsg.getDefaultGraph();
++        
++        DatasetGraphSwitchable dsgs = (DatasetGraphSwitchable)dsg;
++        DatasetGraph dsg1 = dsgs.get();
++        Location loc1 = ((DatasetGraphTDB)dsg1).getLocation();
++        
++        Txn.executeWrite(dsg, ()-> {
++            dsg.add(quad2) ;
++            dsg.add(quad1) ;
++        }) ;
++        DatabaseMgr.compact(dsg);
++        Txn.executeRead(dsg, ()-> {
++            assertEquals(2, g.size());
++            assertTrue(g.contains(triple2));
++        }) ;
++        
++        // Check is not attached to the old graph.
++        DatasetGraph dsgOld = StoreConnection.connectCreate(loc1).getDatasetGraph();
++
++        Txn.executeWrite(dsgOld, ()->dsgOld.getDefaultGraph().delete(triple2));
++        Txn.executeRead(dsg,     ()->assertTrue(g.contains(triple2)) );
++        
++        Txn.executeWrite(dsg,    ()->g.add(triple3));
++        Txn.executeRead(dsgOld,  ()->assertFalse(dsgOld.getDefaultGraph().contains(triple3)) );
++    }
++    
++    @Test public void compact_prefixes_3() {
++        // prefixes axcross compaction.
++        DatasetGraph dsg = DatabaseMgr.connectDatasetGraph(DIR);
++        Graph g = dsg.getDefaultGraph();
++        Txn.executeWrite(dsg, ()-> g.getPrefixMapping().setNsPrefix("ex", "http://example/") );
++        
++        DatasetGraphSwitchable dsgs = (DatasetGraphSwitchable)dsg;
++        DatasetGraph dsg1 = dsgs.get();
++        Location loc1 = ((DatasetGraphTDB)dsg1).getLocation();
++        
++        DatabaseMgr.compact(dsg);
++
++        Txn.executeRead(dsg, ()-> {
++            assertEquals("ex", g.getPrefixMapping().getNsURIPrefix("http://example/"));
++            assertEquals("http://example/", g.getPrefixMapping().getNsPrefixURI("ex"));
++        }) ;
++        
++        // Check is not attached to the old graph.
++        DatasetGraph dsgOld = StoreConnection.connectCreate(loc1).getDatasetGraph();
++
++        Txn.executeWrite(dsgOld, ()->dsgOld.getDefaultGraph().getPrefixMapping().removeNsPrefix("ex"));
++        Txn.executeRead(dsg,     ()->assertEquals("http://example/", g.getPrefixMapping().getNsPrefixURI("ex")));
++        
++        Txn.executeWrite(dsg,    ()->g.getPrefixMapping().setNsPrefix("ex2", "http://exampl2/") );
++        Txn.executeRead(dsgOld,  ()->assertNull(dsgOld.getDefaultGraph().getPrefixMapping().getNsPrefixURI("ex")));
++    }
++
++    @Test public void backup_1() {
++        DatasetGraph dsg = DatabaseMgr.connectDatasetGraph(DIR);
++        Txn.executeWrite(dsg, ()-> {
++            dsg.add(quad2) ;
++            dsg.add(quad1) ;
++        }) ;
++        String file1 = DatabaseMgr.backup(dsg);
++        DatasetGraph dsg2 = RDFDataMgr.loadDatasetGraph(file1);
++        Txn.executeRead(dsg, ()-> {
++            assertTrue(dsg.contains(quad1)) ;
++            assertEquals(2, dsg.getDefaultGraph().size());
++            assertTrue(dsg2.getDefaultGraph().isIsomorphicWith(dsg.getDefaultGraph()));
++        }) ;
++        String file2 = DatabaseMgr.backup(dsg);
++        assertNotEquals(file1, file2);
++    }
++    
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/sys/TestOps.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/sys/TestOps.java
index 0000000,0000000..0c997fe
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/sys/TestOps.java
@@@ -1,0 -1,0 +1,31 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.sys;
++
++import org.seaborne.dboe.base.block.FileMode ;
++import org.seaborne.tdb2.sys.SystemTDB ;
++
++public class TestOps
++{
++    public static void setFileMode(FileMode mode)
++    {
++        SystemTDB.internalSetFileMode(mode) ;
++    }
++    
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/sys/TestSys.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/sys/TestSys.java
index 0000000,0000000..b537655
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/sys/TestSys.java
@@@ -1,0 -1,0 +1,32 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.sys;
++
++
++import static org.junit.Assert.*;
++import org.junit.Test ;
++import org.seaborne.tdb2.TDB2 ;
++
++public class TestSys
++{
++    @Test public void sys1()
++    {
++        assertNotNull(TDB2.VERSION) ;
++        assertNotNull(TDB2.BUILD_DATE) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Assembler/tdb-dataset-embed.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Assembler/tdb-dataset-embed.ttl
index 0000000,0000000..9edd5a3
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Assembler/tdb-dataset-embed.ttl
@@@ -1,0 -1,0 +1,23 @@@
++#  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.
++
++@prefix tdb2:    <http://jena.apache.org/2016/tdb#> .
++@prefix rdfs:	 <http://www.w3.org/2000/01/rdf-schema#> .
++@prefix rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
++@prefix ja:      <http://jena.hpl.hp.com/2005/11/Assembler#> .
++
++<#dataset> rdf:type      tdb2:DatasetTDB2 ;
++    tdb2:location "--mem--" ;
++    .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Assembler/tdb-dataset.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Assembler/tdb-dataset.ttl
index 0000000,0000000..c9d8d7b
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Assembler/tdb-dataset.ttl
@@@ -1,0 -1,0 +1,23 @@@
++#  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.
++
++@prefix tdb2:    <http://jena.apache.org/2016/tdb#> .
++@prefix rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
++@prefix ja:      <http://jena.hpl.hp.com/2005/11/Assembler#> .
++
++<#dataset> rdf:type      tdb2:DatasetTDB2 ;
++    # Do at least one the long way.
++    tdb2:location "target/tdb-testing/DB" ;
++    .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Assembler/tdb-graph-embed.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Assembler/tdb-graph-embed.ttl
index 0000000,0000000..af3daab
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Assembler/tdb-graph-embed.ttl
@@@ -1,0 -1,0 +1,27 @@@
++#  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.
++
++@prefix tdb2:    <http://jena.apache.org/2016/tdb#> .
++@prefix rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
++@prefix rdfs:	 <http://www.w3.org/2000/01/rdf-schema#> .
++@prefix ja:      <http://jena.hpl.hp.com/2005/11/Assembler#> .
++
++<#dataset> rdf:type      ja:RDFDataset ;
++    ja:defaultGraph <#graph> ;
++    . 
++
++<#graph> rdf:type tdb2:GraphTDB2 ;
++    tdb2:location "--mem--" ;
++    .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Assembler/tdb-graph-ref-dataset.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Assembler/tdb-graph-ref-dataset.ttl
index 0000000,0000000..fe256bd
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Assembler/tdb-graph-ref-dataset.ttl
@@@ -1,0 -1,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.
++
++@prefix tdb2:    <http://jena.apache.org/2016/tdb#> .
++@prefix rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
++@prefix rdfs:	 <http://www.w3.org/2000/01/rdf-schema#> .
++@prefix ja:      <http://jena.hpl.hp.com/2005/11/Assembler#> .
++
++<#dataset> rdf:type      ja:RDFDataset ;
++    ja:defaultGraph <#graph> ;
++    . 
++
++<#graph> rdf:type tdb2:GraphTDB2 ;
++    tdb2:dataset <#dataset> ;
++    .
++
++<#dataset> rdf:type tdb2:DatasetTDB2 ;
++    tdb2:location "--mem--" ;
++    .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Assembler/tdb-graph.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Assembler/tdb-graph.ttl
index 0000000,0000000..2ae8396
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Assembler/tdb-graph.ttl
@@@ -1,0 -1,0 +1,27 @@@
++#  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.
++
++@prefix tdb2:    <http://jena.apache.org/2016/tdb#> .
++@prefix rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
++@prefix ja:      <http://jena.hpl.hp.com/2005/11/Assembler#> .
++
++<#dataset> rdf:type      ja:RDFDataset ;
++    ja:defaultGraph <#graph> ;
++    . 
++
++<#graph> rdf:type tdb2:GraphTDB2 ;
++    tdb2:location "target/tdb-testing/DB" ;
++    .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Assembler/tdb-named-graph-1.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Assembler/tdb-named-graph-1.ttl
index 0000000,0000000..78e40cf
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Assembler/tdb-named-graph-1.ttl
@@@ -1,0 -1,0 +1,29 @@@
++
++#  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.
++
++@prefix tdb2:    <http://jena.apache.org/2016/tdb#> .
++@prefix rdfs:	 <http://www.w3.org/2000/01/rdf-schema#> .
++@prefix rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
++@prefix ja:      <http://jena.hpl.hp.com/2005/11/Assembler#> .
++
++<#dataset> rdf:type      ja:RDFDataset ;
++    ja:defaultGraph <#graph> ;
++    . 
++
++<#graph> rdf:type tdb2:GraphTDB2 ;
++    tdb2:location "--mem--" ;
++    tdb2:graphName "http://example.com/graph" ;
++    .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Assembler/tdb-named-graph-2.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Assembler/tdb-named-graph-2.ttl
index 0000000,0000000..ee2feab
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Assembler/tdb-named-graph-2.ttl
@@@ -1,0 -1,0 +1,28 @@@
++#  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.
++
++@prefix tdb2:    <http://jena.apache.org/2016/tdb#> .
++@prefix rdfs:	 <http://www.w3.org/2000/01/rdf-schema#> .
++@prefix rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
++@prefix ja:      <http://jena.hpl.hp.com/2005/11/Assembler#> .
++
++<#dataset> rdf:type      ja:RDFDataset ;
++    ja:defaultGraph <#graph> ;
++    . 
++
++<#graph> rdf:type tdb2:GraphTDB2 ;
++    tdb2:location "--mem--" ;
++    tdb2:graphName "http://example.com/graph" ;
++    .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Basic/basic-00.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Basic/basic-00.rq
index 0000000,0000000..f2341a0
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Basic/basic-00.rq
@@@ -1,0 -1,0 +1,4 @@@
++PREFIX :  <http://example>
++
++SELECT * 
++{ ?x ?p ?z }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Basic/basic-1-O.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Basic/basic-1-O.rq
index 0000000,0000000..e1d1da7
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Basic/basic-1-O.rq
@@@ -1,0 -1,0 +1,4 @@@
++PREFIX :  <http://example/>
++
++SELECT * 
++{ ?x ?p :z1 }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Basic/basic-1-P.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Basic/basic-1-P.rq
index 0000000,0000000..d64b9f9
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Basic/basic-1-P.rq
@@@ -1,0 -1,0 +1,4 @@@
++PREFIX :  <http://example/>
++
++SELECT * 
++{ ?x :p1 ?z }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Basic/basic-1-PO.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Basic/basic-1-PO.rq
index 0000000,0000000..01fa14a
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Basic/basic-1-PO.rq
@@@ -1,0 -1,0 +1,4 @@@
++PREFIX :  <http://example/>
++
++SELECT * 
++{ ?x :p1 :z1 }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Basic/basic-1-S.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Basic/basic-1-S.rq
index 0000000,0000000..180188e
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Basic/basic-1-S.rq
@@@ -1,0 -1,0 +1,4 @@@
++PREFIX :  <http://example/>
++
++SELECT * 
++{ :x1 ?p ?z }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Basic/basic-1-SO.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Basic/basic-1-SO.rq
index 0000000,0000000..a4242fb
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Basic/basic-1-SO.rq
@@@ -1,0 -1,0 +1,4 @@@
++PREFIX :  <http://example/>
++
++SELECT * 
++{ :x1 ?p :z1 }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Basic/basic-1-SP.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Basic/basic-1-SP.rq
index 0000000,0000000..37ddfe6
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Basic/basic-1-SP.rq
@@@ -1,0 -1,0 +1,4 @@@
++PREFIX :  <http://example/>
++
++SELECT * 
++{ :x1 :p1 ?z }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Basic/basic-1-SPO.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Basic/basic-1-SPO.rq
index 0000000,0000000..41aa20f
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Basic/basic-1-SPO.rq
@@@ -1,0 -1,0 +1,4 @@@
++PREFIX :  <http://example/>
++
++SELECT ?v
++{ :x1 :p1 :z1 }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Basic/basic-2-SO.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Basic/basic-2-SO.rq
index 0000000,0000000..49c5711
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Basic/basic-2-SO.rq
@@@ -1,0 -1,0 +1,4 @@@
++PREFIX :  <http://example/>
++
++SELECT *
++{ ?x ?p ?x }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Basic/basic-3-O.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Basic/basic-3-O.rq
index 0000000,0000000..f7f3f6b
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Basic/basic-3-O.rq
@@@ -1,0 -1,0 +1,4 @@@
++PREFIX :  <http://example/>
++
++SELECT * 
++{ ?x ?p :z1a }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Basic/basic-3-P.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Basic/basic-3-P.rq
index 0000000,0000000..8c79fa5
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Basic/basic-3-P.rq
@@@ -1,0 -1,0 +1,4 @@@
++PREFIX :  <http://example/>
++
++SELECT * 
++{ ?x :p1a ?z }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Basic/basic-3-PO.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Basic/basic-3-PO.rq
index 0000000,0000000..a011a1c
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Basic/basic-3-PO.rq
@@@ -1,0 -1,0 +1,4 @@@
++PREFIX :  <http://example/>
++
++SELECT * 
++{ ?x :p1 :z1a }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Basic/basic-3-S.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Basic/basic-3-S.rq
index 0000000,0000000..8b62930
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Basic/basic-3-S.rq
@@@ -1,0 -1,0 +1,4 @@@
++PREFIX :  <http://example/>
++
++SELECT * 
++{ :x1a ?p ?z }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Basic/basic-3-SO.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Basic/basic-3-SO.rq
index 0000000,0000000..e71f0b3
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Basic/basic-3-SO.rq
@@@ -1,0 -1,0 +1,4 @@@
++PREFIX :  <http://example/>
++
++SELECT * 
++{ :x1 ?p :z1a }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Basic/basic-3-SP.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Basic/basic-3-SP.rq
index 0000000,0000000..3b7ff81
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Basic/basic-3-SP.rq
@@@ -1,0 -1,0 +1,4 @@@
++PREFIX :  <http://example/>
++
++SELECT * 
++{ :x1a :p1 ?z }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Basic/data-1.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Basic/data-1.ttl
index 0000000,0000000..d3a3d80
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Basic/data-1.ttl
@@@ -1,0 -1,0 +1,17 @@@
++@prefix : <http://example/> .
++
++:x1 :p1 :z1 .
++:x1 :p1 :z2 .
++
++:x1 :p2 :z1 .
++:x1 :p2 :z2 .
++
++:x2 :p1 :z1 .
++:x2 :p1 :z2 .
++
++:x2 :p2 :z1 .
++:x2 :p2 :z2 .
++
++:x :p :x .
++:x :x :x .
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Basic/manifest.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Basic/manifest.ttl
index 0000000,0000000..aacbb56
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Basic/manifest.ttl
@@@ -1,0 -1,0 +1,101 @@@
++#  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.
++
++@prefix rdf:    <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
++@prefix rdfs:	<http://www.w3.org/2000/01/rdf-schema#> .
++@prefix mf:     <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
++@prefix qt:     <http://www.w3.org/2001/sw/DataAccess/tests/test-query#> .
++
++<>  rdf:type mf:Manifest ;
++    rdfs:label "Basic test cases" ;
++    mf:entries
++    ( 
++      [  mf:name    "Basic 00" ;
++         mf:action
++            [ qt:query  <basic-00.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Basic S" ;
++         mf:action
++            [ qt:query  <basic-1-S.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Basic P" ;
++         mf:action
++            [ qt:query  <basic-1-P.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Basic O" ;
++         mf:action
++            [ qt:query  <basic-1-O.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Basic 1 SP" ;
++         mf:action
++            [ qt:query  <basic-1-SP.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Basic 1 SO" ;
++         mf:action
++            [ qt:query  <basic-1-SO.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Basic 1 PO" ;
++         mf:action
++            [ qt:query  <basic-1-PO.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Basic 1 SPO" ;
++         mf:action
++            [ qt:query  <basic-1-SPO.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Basic 2 SO" ;
++         mf:action
++            [ qt:query  <basic-2-SO.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++
++      [  mf:name    "Basic 3 S" ;
++         mf:action
++            [ qt:query  <basic-3-S.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Basic 3 P" ;
++         mf:action
++            [ qt:query  <basic-3-P.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Basic 3 O" ;
++         mf:action
++            [ qt:query  <basic-3-O.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Basic 3 SP" ;
++         mf:action
++            [ qt:query  <basic-3-SP.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Basic 3 SO" ;
++         mf:action
++            [ qt:query  <basic-3-SO.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Basic 3 PO" ;
++         mf:action
++            [ qt:query  <basic-3-SO.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++    ) .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Data/solver-data.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Data/solver-data.ttl
index 0000000,0000000..cdbfb19
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Data/solver-data.ttl
@@@ -1,0 -1,0 +1,7 @@@
++@prefix : <http://example/> .
++
++:s :p :o .
++:s :p 10 .
++:s :p :x .
++:x :q :y .
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Loader/data-1.nq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Loader/data-1.nq
index 0000000,0000000..e9812a6
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Loader/data-1.nq
@@@ -1,0 -1,0 +1,1 @@@
++<s> <p> <o> <g> .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Loader/data-2.nt
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Loader/data-2.nt
index 0000000,0000000..81fc25f
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Loader/data-2.nt
@@@ -1,0 -1,0 +1,1 @@@
++<s> <p> <o> .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Loader/data-3.trig
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Loader/data-3.trig
index 0000000,0000000..8000cc1
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Loader/data-3.trig
@@@ -1,0 -1,0 +1,3 @@@
++prefix : <http://example/> 
++
++:g { :s :p :o }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Loader/data-4.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Loader/data-4.ttl
index 0000000,0000000..49678f1
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Loader/data-4.ttl
@@@ -1,0 -1,0 +1,4 @@@
++prefix : <http://example/> 
++
++:s :p :o .
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Pattern/data-1.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Pattern/data-1.ttl
index 0000000,0000000..d3a3d80
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Pattern/data-1.ttl
@@@ -1,0 -1,0 +1,17 @@@
++@prefix : <http://example/> .
++
++:x1 :p1 :z1 .
++:x1 :p1 :z2 .
++
++:x1 :p2 :z1 .
++:x1 :p2 :z2 .
++
++:x2 :p1 :z1 .
++:x2 :p1 :z2 .
++
++:x2 :p2 :z1 .
++:x2 :p2 :z2 .
++
++:x :p :x .
++:x :x :x .
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Pattern/manifest.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Pattern/manifest.ttl
index 0000000,0000000..094244b
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Pattern/manifest.ttl
@@@ -1,0 -1,0 +1,35 @@@
++#  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.
++
++@prefix rdf:    <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
++@prefix rdfs:	<http://www.w3.org/2000/01/rdf-schema#> .
++@prefix mf:     <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
++@prefix qt:     <http://www.w3.org/2001/sw/DataAccess/tests/test-query#> .
++
++<>  rdf:type mf:Manifest ;
++    rdfs:label "Patterns" ;
++    mf:entries
++    ( 
++      [  mf:name    "Pattern 1" ;
++         mf:action
++            [ qt:query  <pattern-1.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Pattern 2" ;
++         mf:action
++            [ qt:query  <pattern-2.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++    ) .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Pattern/pattern-1.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Pattern/pattern-1.rq
index 0000000,0000000..ca7c8fc
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Pattern/pattern-1.rq
@@@ -1,0 -1,0 +1,7 @@@
++PREFIX :  <http://example/OTHER>
++
++SELECT * 
++{ 
++    :NoSuchNode ?p ?z .
++    ?x ?p ?z
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Pattern/pattern-2.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Pattern/pattern-2.rq
index 0000000,0000000..9b90038
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Pattern/pattern-2.rq
@@@ -1,0 -1,0 +1,8 @@@
++PREFIX :  <http://example/OTHER>
++
++SELECT * 
++{ 
++    # Test shared variables
++    ?x1 :p1 ?z .
++    ?x2 :p1 ?z .
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Quads/data-1.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Quads/data-1.ttl
index 0000000,0000000..adc4239
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Quads/data-1.ttl
@@@ -1,0 -1,0 +1,5 @@@
++@prefix : <http://example/> .
++
++:x :p 1 .
++
++:x1 :p :x .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Quads/data-2.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Quads/data-2.ttl
index 0000000,0000000..100a7c7
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Quads/data-2.ttl
@@@ -1,0 -1,0 +1,5 @@@
++@prefix : <http://example/> .
++
++:x :p 1 .
++
++:x2 :p :x .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Quads/data-dft.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Quads/data-dft.ttl
index 0000000,0000000..a2ef718
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Quads/data-dft.ttl
@@@ -1,0 -1,0 +1,10 @@@
++@prefix : <http://example/> .
++
++:x :p 1 .
++:x :p 2 .
++
++:y :p 1 .
++:y :p :x .
++:y :p :z .
++
++:z :q "abc" .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Quads/manifest.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Quads/manifest.ttl
index 0000000,0000000..15a46a7
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Quads/manifest.ttl
@@@ -1,0 -1,0 +1,51 @@@
++#  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.
++
++@prefix rdf:    <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
++@prefix rdfs:	<http://www.w3.org/2000/01/rdf-schema#> .
++@prefix mf:     <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
++@prefix qt:     <http://www.w3.org/2001/sw/DataAccess/tests/test-query#> .
++
++<>  rdf:type mf:Manifest ;
++    rdfs:label "Quads and quad patterns" ;
++    mf:entries
++    ( 
++       [  mf:name    "Quad-1" ;
++          mf:action
++            [ qt:query  <quad-01.rq> ;
++              qt:data       <data-dft.ttl> ;
++              qt:graphData  <data-1.ttl> ;
++              qt:graphData  <data-2.ttl> ;
++             ]
++      ]
++       [  mf:name    "Quad-2" ;
++          mf:action
++            [ qt:query  <quad-02.rq> ;
++              qt:data       <data-dft.ttl> ;
++              qt:graphData  <data-1.ttl> ;
++              qt:graphData  <data-2.ttl> ;
++             ]
++      ]
++
++        ## Union of named graphs, explicitly named default graph
++        ## Complex patterns.
++
++##       [  mf:name    "Basic 00" ;
++##          mf:action
++##             [ qt:query  <basic-00.rq> ;
++##               qt:data   <data-1.ttl> ;
++##               qt:graphData ; ]
++##       ]
++    ) .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Quads/quad-01.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Quads/quad-01.rq
index 0000000,0000000..b705fc8
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Quads/quad-01.rq
@@@ -1,0 -1,0 +1,9 @@@
++PREFIX : <http://example/>
++PREFIX sys: <urn:x-arq:>
++
++SELECT *
++{
++   {?s ?p ?o } UNION { GRAPH ?g {?s ?p ?o}}
++}
++
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Quads/quad-02.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Quads/quad-02.rq
index 0000000,0000000..ef3bf2f
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Quads/quad-02.rq
@@@ -1,0 -1,0 +1,8 @@@
++PREFIX : <http://example/>
++PREFIX sys: <urn:x-arq:>
++
++SELECT *
++{
++  ?s ?p ?x
++   GRAPH ?g {?s1 ?p1 ?x }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/UnionGraph/data-1.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/UnionGraph/data-1.ttl
index 0000000,0000000..77be09c
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/UnionGraph/data-1.ttl
@@@ -1,0 -1,0 +1,7 @@@
++@prefix : <http://example/> .
++
++:x :p 1 .
++:x :p 2 .
++
++
++:y :q :x .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/UnionGraph/data-2.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/UnionGraph/data-2.ttl
index 0000000,0000000..59ac719
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/UnionGraph/data-2.ttl
@@@ -1,0 -1,0 +1,5 @@@
++@prefix : <http://example/> .
++
++:x :p 1 .
++:x :p 99 .
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/UnionGraph/data-dft.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/UnionGraph/data-dft.ttl
index 0000000,0000000..d64ddd9
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/UnionGraph/data-dft.ttl
@@@ -1,0 -1,0 +1,5 @@@
++@prefix : <http://example/> .
++
++:x :p 11 .
++:x :p 999 .
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/UnionGraph/manifest.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/UnionGraph/manifest.ttl
index 0000000,0000000..217d229
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/UnionGraph/manifest.ttl
@@@ -1,0 -1,0 +1,107 @@@
++#  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.
++
++@prefix rdf:    <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
++@prefix rdfs:	<http://www.w3.org/2000/01/rdf-schema#> .
++@prefix mf:     <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
++@prefix mfx:    <http://jena.hpl.hp.com/2005/05/test-manifest-extra#> .
++@prefix qt:     <http://www.w3.org/2001/sw/DataAccess/tests/test-query#> .
++
++<>  rdf:type mf:Manifest ;
++    rdfs:label "Union of named graphs" ;
++    mf:entries
++    ( 
++      [  mf:name    "Merge-1" ;
++         rdf:type   mfx:TestQuery ; 
++         mf:action
++            [ qt:query  <merge-1.rq> ;
++              qt:data <data-dft.ttl> ;
++              qt:graphData <data-1.ttl> ;
++              qt:graphData <data-2.ttl> ;
++            ] ;
++         mf:result <merge-1-results.srx>
++      ]
++      [  mf:name    "Merge-2" ;
++         rdf:type   mfx:TestQuery ; 
++         mf:action
++            [ qt:query  <merge-2.rq> ;
++              qt:data <data-dft.ttl> ;
++              qt:graphData <data-1.ttl> ;
++              qt:graphData <data-2.ttl> ;
++            ] ;
++         mf:result <merge-2-results.srx>
++      ]
++      [  mf:name    "Merge-3" ;
++         rdf:type   mfx:TestQuery ; 
++         mf:action
++            [ qt:query  <merge-3.rq> ;
++              qt:data <data-dft.ttl> ;
++              qt:graphData <data-1.ttl> ;
++              qt:graphData <data-2.ttl> ;
++            ] ;
++         mf:result <merge-3-results.srx>
++      ]
++      [  mf:name    "Merge-4" ;
++         rdf:type   mfx:TestQuery ; 
++         mf:action
++            [ qt:query  <merge-4.rq> ;
++              qt:data <data-dft.ttl> ;
++              qt:graphData <data-1.ttl> ;
++              qt:graphData <data-2.ttl> ;
++            ] ;
++         mf:result <merge-4-results.srx>
++      ]
++      [  mf:name    "Merge-5" ;
++         rdf:type   mfx:TestQuery ; 
++         mf:action
++            [ qt:query  <merge-5.rq> ;
++              qt:data <data-dft.ttl> ;
++              qt:graphData <data-1.ttl> ;
++              qt:graphData <data-2.ttl> ;
++            ] ;
++         mf:result <merge-5-results.srx>
++      ]
++      [  mf:name    "Merge-6" ;
++         rdf:type   mfx:TestQuery ; 
++         mf:action
++            [ qt:query  <merge-6.rq> ;
++              qt:data <data-dft.ttl> ;
++              qt:graphData <data-1.ttl> ;
++              qt:graphData <data-2.ttl> ;
++            ] ;
++         mf:result <merge-6-results.srx>
++      ]
++      # Queries that need more than one SQL query (currently)
++      [  mf:name    "Merge-A" ;
++         rdf:type   mfx:TestQuery ; 
++         mf:action
++            [ qt:query  <merge-A.rq> ;
++              qt:data <data-dft.ttl> ;
++              qt:graphData <data-1.ttl> ;
++              qt:graphData <data-2.ttl> ;
++            ] ;
++         mf:result <merge-A-results.srx>
++      ]
++      [  mf:name    "Merge-B" ;
++         rdf:type   mfx:TestQuery ; 
++         mf:action
++            [ qt:query  <merge-B.rq> ;
++              qt:data <data-dft.ttl> ;
++              qt:graphData <data-1.ttl> ;
++              qt:graphData <data-2.ttl> ;
++            ] ;
++         mf:result <merge-B-results.srx>
++      ]
++    ).

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/UnionGraph/merge-1-results.srx
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/UnionGraph/merge-1-results.srx
index 0000000,0000000..f850ec3
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/UnionGraph/merge-1-results.srx
@@@ -1,0 -1,0 +1,57 @@@
++<?xml version="1.0"?>
++<sparql
++    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
++    xmlns:xs="http://www.w3.org/2001/XMLSchema#"
++    xmlns="http://www.w3.org/2005/sparql-results#" >
++  <head>
++    <variable name="s"/>
++    <variable name="p"/>
++    <variable name="o"/>
++  </head>
++  <results>
++    <result>
++      <binding name="s">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="o">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">1</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="s">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="o">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">99</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="s">
++        <uri>http://example/y</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/q</uri>
++      </binding>
++      <binding name="o">
++        <uri>http://example/x</uri>
++      </binding>
++    </result>
++    <result>
++      <binding name="s">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="o">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">2</literal>
++      </binding>
++    </result>
++  </results>
++</sparql>

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/UnionGraph/merge-1.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/UnionGraph/merge-1.rq
index 0000000,0000000..c403064
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/UnionGraph/merge-1.rq
@@@ -1,0 -1,0 +1,8 @@@
++PREFIX :    <http://example/>
++PREFIX arq: <urn:x-arq:>
++
++SELECT *
++{
++    GRAPH arq:UnionGraph { ?s ?p ?o }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/UnionGraph/merge-2-results.srx
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/UnionGraph/merge-2-results.srx
index 0000000,0000000..18f9252
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/UnionGraph/merge-2-results.srx
@@@ -1,0 -1,0 +1,66 @@@
++<?xml version="1.0"?>
++<sparql
++    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
++    xmlns:xs="http://www.w3.org/2001/XMLSchema#"
++    xmlns="http://www.w3.org/2005/sparql-results#" >
++  <head>
++    <variable name="s"/>
++    <variable name="p"/>
++    <variable name="o"/>
++    <variable name="q"/>
++    <variable name="z"/>
++  </head>
++  <results>
++    <result>
++      <binding name="s">
++        <uri>http://example/y</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/q</uri>
++      </binding>
++      <binding name="o">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="q">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="z">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">99</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="s">
++        <uri>http://example/y</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/q</uri>
++      </binding>
++      <binding name="o">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="q">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="z">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">1</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="s">
++        <uri>http://example/y</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/q</uri>
++      </binding>
++      <binding name="o">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="q">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="z">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">2</literal>
++      </binding>
++    </result>
++  </results>
++</sparql>

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/UnionGraph/merge-2.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/UnionGraph/merge-2.rq
index 0000000,0000000..27953a6
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/UnionGraph/merge-2.rq
@@@ -1,0 -1,0 +1,8 @@@
++PREFIX :    <http://example/>
++PREFIX arq: <urn:x-arq:>
++
++SELECT *
++{
++    GRAPH arq:UnionGraph { ?s ?p ?o . ?o ?q ?z }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/UnionGraph/merge-3-results.srx
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/UnionGraph/merge-3-results.srx
index 0000000,0000000..4a65413
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/UnionGraph/merge-3-results.srx
@@@ -1,0 -1,0 +1,31 @@@
++<?xml version="1.0"?>
++<sparql
++    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
++    xmlns:xs="http://www.w3.org/2001/XMLSchema#"
++    xmlns="http://www.w3.org/2005/sparql-results#" >
++  <head>
++    <variable name="p"/>
++  </head>
++  <results>
++    <result>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++    </result>
++    <result>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++    </result>
++    <result>
++      <binding name="p">
++        <uri>http://example/q</uri>
++      </binding>
++    </result>
++    <result>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++    </result>
++  </results>
++</sparql>

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/UnionGraph/merge-3.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/UnionGraph/merge-3.rq
index 0000000,0000000..1212926
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/UnionGraph/merge-3.rq
@@@ -1,0 -1,0 +1,8 @@@
++PREFIX :    <http://example/>
++PREFIX arq: <urn:x-arq:>
++
++SELECT *
++{
++    GRAPH arq:UnionGraph { [] ?p [] }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/UnionGraph/merge-4-results.srx
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/UnionGraph/merge-4-results.srx
index 0000000,0000000..0b44cb2
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/UnionGraph/merge-4-results.srx
@@@ -1,0 -1,0 +1,99 @@@
++<?xml version="1.0"?>
++<sparql
++    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
++    xmlns:xs="http://www.w3.org/2001/XMLSchema#"
++    xmlns="http://www.w3.org/2005/sparql-results#" >
++  <head>
++    <variable name="s"/>
++    <variable name="p"/>
++    <variable name="o"/>
++    <variable name="q"/>
++    <variable name="z"/>
++  </head>
++  <results>
++    <result>
++      <binding name="s">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="o">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">1</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="s">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="o">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">99</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="s">
++        <uri>http://example/y</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/q</uri>
++      </binding>
++      <binding name="o">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="q">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="z">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">1</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="s">
++        <uri>http://example/y</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/q</uri>
++      </binding>
++      <binding name="o">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="q">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="z">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">99</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="s">
++        <uri>http://example/y</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/q</uri>
++      </binding>
++      <binding name="o">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="q">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="z">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">2</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="s">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="o">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">2</literal>
++      </binding>
++    </result>
++  </results>
++</sparql>

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/UnionGraph/merge-4.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/UnionGraph/merge-4.rq
index 0000000,0000000..004f4bc
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/UnionGraph/merge-4.rq
@@@ -1,0 -1,0 +1,8 @@@
++PREFIX :    <http://example/>
++PREFIX arq: <urn:x-arq:>
++
++SELECT *
++{
++    GRAPH arq:UnionGraph 
++    { ?s ?p ?o . OPTIONAL { ?o ?q ?z } }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/UnionGraph/merge-5-results.srx
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/UnionGraph/merge-5-results.srx
index 0000000,0000000..c7d777f
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/UnionGraph/merge-5-results.srx
@@@ -1,0 -1,0 +1,33 @@@
++<?xml version="1.0"?>
++<sparql xmlns="http://www.w3.org/2005/sparql-results#">
++  <head>
++    <variable name="p"/>
++    <variable name="o"/>
++  </head>
++  <results>
++    <result>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="o">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">99</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="o">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">2</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="o">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">1</literal>
++      </binding>
++    </result>
++  </results>
++</sparql>

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/UnionGraph/merge-5.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/UnionGraph/merge-5.rq
index 0000000,0000000..fa9bfe3
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/UnionGraph/merge-5.rq
@@@ -1,0 -1,0 +1,8 @@@
++PREFIX :    <http://example/>
++PREFIX arq: <urn:x-arq:>
++
++SELECT *
++{
++    GRAPH arq:UnionGraph { :x ?p  ?o }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/UnionGraph/merge-6-results.srx
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/UnionGraph/merge-6-results.srx
index 0000000,0000000..b4d31dc
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/UnionGraph/merge-6-results.srx
@@@ -1,0 -1,0 +1,43 @@@
++<?xml version="1.0"?>
++<sparql xmlns="http://www.w3.org/2005/sparql-results#">
++  <head>
++    <variable name="x"/>
++    <variable name="p"/>
++    <variable name="v"/>
++  </head>
++  <results>
++    <result>
++      <binding name="x">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="v">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">99</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="x">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="v">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">2</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="x">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="v">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">1</literal>
++      </binding>
++    </result>
++  </results>
++</sparql>

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/UnionGraph/merge-6.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/UnionGraph/merge-6.rq
index 0000000,0000000..7819cbb
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/UnionGraph/merge-6.rq
@@@ -1,0 -1,0 +1,7 @@@
++PREFIX :    <http://example/>
++PREFIX arq: <urn:x-arq:>
++
++SELECT *
++{
++    GRAPH arq:UnionGraph { ?x ?p  99 . ?x :p ?v }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/UnionGraph/merge-A-results.srx
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/UnionGraph/merge-A-results.srx
index 0000000,0000000..1438c25
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/UnionGraph/merge-A-results.srx
@@@ -1,0 -1,0 +1,99 @@@
++<?xml version="1.0"?>
++<sparql
++    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
++    xmlns:xs="http://www.w3.org/2001/XMLSchema#"
++    xmlns="http://www.w3.org/2005/sparql-results#" >
++  <head>
++    <variable name="s"/>
++    <variable name="p"/>
++    <variable name="o"/>
++    <variable name="q"/>
++    <variable name="z"/>
++  </head>
++  <results>
++    <result>
++      <binding name="s">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="o">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">1</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="s">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="o">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">99</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="s">
++        <uri>http://example/y</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/q</uri>
++      </binding>
++      <binding name="o">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="q">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="z">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">99</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="s">
++        <uri>http://example/y</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/q</uri>
++      </binding>
++      <binding name="o">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="q">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="z">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">1</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="s">
++        <uri>http://example/y</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/q</uri>
++      </binding>
++      <binding name="o">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="q">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="z">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">2</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="s">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="o">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">2</literal>
++      </binding>
++    </result>
++  </results>
++</sparql>

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/UnionGraph/merge-A.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/UnionGraph/merge-A.rq
index 0000000,0000000..7bab242
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/UnionGraph/merge-A.rq
@@@ -1,0 -1,0 +1,8 @@@
++PREFIX :    <http://example>
++PREFIX arq: <urn:x-arq:>
++
++SELECT *
++{
++    GRAPH arq:UnionGraph 
++    { ?s ?p ?o . OPTIONAL { ?o ?q ?z FILTER(?o != 123) } }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/UnionGraph/merge-B-results.srx
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/UnionGraph/merge-B-results.srx
index 0000000,0000000..60d7766
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/UnionGraph/merge-B-results.srx
@@@ -1,0 -1,0 +1,49 @@@
++<?xml version="1.0"?>
++<sparql
++    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
++    xmlns:xs="http://www.w3.org/2001/XMLSchema#"
++    xmlns="http://www.w3.org/2005/sparql-results#" >
++  <head>
++    <variable name="s"/>
++    <variable name="p"/>
++    <variable name="o"/>
++    <variable name="q"/>
++    <variable name="v"/>
++  </head>
++  <results>
++    <result>
++      <binding name="s">
++        <uri>http://example/y</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/q</uri>
++      </binding>
++      <binding name="o">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="q">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="v">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">11</literal>
++      </binding>
++    </result>
++    <result>
++      <binding name="s">
++        <uri>http://example/y</uri>
++      </binding>
++      <binding name="p">
++        <uri>http://example/q</uri>
++      </binding>
++      <binding name="o">
++        <uri>http://example/x</uri>
++      </binding>
++      <binding name="q">
++        <uri>http://example/p</uri>
++      </binding>
++      <binding name="v">
++        <literal datatype="http://www.w3.org/2001/XMLSchema#integer">999</literal>
++      </binding>
++    </result>
++  </results>
++</sparql>

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/UnionGraph/merge-B.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/UnionGraph/merge-B.rq
index 0000000,0000000..d4aceda
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/UnionGraph/merge-B.rq
@@@ -1,0 -1,0 +1,8 @@@
++PREFIX :    <http://example>
++PREFIX arq: <urn:x-arq:>
++
++SELECT *
++{
++    GRAPH arq:UnionGraph { ?s ?p ?o . }
++    { ?o ?q ?v }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Update/create-1.ru
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Update/create-1.ru
index 0000000,0000000..3276ec1
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Update/create-1.ru
@@@ -1,0 -1,0 +1,1 @@@
++CREATE GRAPH <http://example/graph>

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Update/drop-1.ru
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Update/drop-1.ru
index 0000000,0000000..2512eb3
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Update/drop-1.ru
@@@ -1,0 -1,0 +1,1 @@@
++DROP GRAPH <http://example/graph>

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Update/update-1.ru
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Update/update-1.ru
index 0000000,0000000..7a6b737
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Update/update-1.ru
@@@ -1,0 -1,0 +1,3 @@@
++PREFIX : <http://example/> 
++
++INSERT { :r :p 123 }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Update/update-2.ru
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Update/update-2.ru
index 0000000,0000000..41e4ec0
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Update/update-2.ru
@@@ -1,0 -1,0 +1,5 @@@
++PREFIX : <http://example/> 
++
++CREATE GRAPH <http://example/g1>
++INSERT INTO GRAPH <http://example/g1>
++{ :r :p 123 }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Update/update-3.ru
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Update/update-3.ru
index 0000000,0000000..17f8410
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Update/update-3.ru
@@@ -1,0 -1,0 +1,7 @@@
++PREFIX : <http://example/> 
++
++CREATE GRAPH <http://example/g1>
++INSERT INTO GRAPH <http://example/g1>
++{ :r :p 123 }
++DELETE FROM GRAPH <http://example/g1>
++{ :r :p 123 }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Values/data-1.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Values/data-1.ttl
index 0000000,0000000..27c37dd
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Values/data-1.ttl
@@@ -1,0 -1,0 +1,23 @@@
++@prefix : <http://example/> .
++@prefix  xsd:    <http://www.w3.org/2001/XMLSchema#> .
++
++:x1 :p 1 .
++:x2 :p "1" .
++:x3 :p "1"@en .
++
++:x4 :p 1.5 .
++
++:x5 :p "2008-04-29T09:13:15+01:00"^^xsd:dateTime .
++
++:x6 :p "0008-04-29T09:13:15-01:00"^^xsd:dateTime .
++
++:x7 :p "-3000-04-29T09:13:15Z"^^xsd:dateTime .
++
++# Not encoded inline
++:x8 :p "-3000-04-29T09:13:15Z"^^xsd:dateTime .
++
++:x9 :p "2008-04-29"^^xsd:date .
++
++:x10 :p "2008-04-29Z"^^xsd:date .
++
++:x11 :p "2008-04-29+01:00"^^xsd:date .


[48/50] [abbrv] jena git commit: JENA-1396: Merge //github/afs/mantis as subdirectory jena-db/

Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BinaryDataFileRandomAccess.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BinaryDataFileRandomAccess.java
index 0000000,0000000..c3738ad
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BinaryDataFileRandomAccess.java
@@@ -1,0 -1,0 +1,148 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++import java.io.IOException ;
++import java.io.RandomAccessFile ;
++
++import org.apache.jena.atlas.RuntimeIOException ;
++import org.apache.jena.atlas.io.IO ;
++
++/** Implementation of {@link BinaryDataFile} using {@link RandomAccessFile}.
++ *  
++ * <li>No buffering of reads or writes provided.
++ * <li>Not thread-safe.
++ *  
++ *  @see BinaryDataFileWriteBuffered
++ */
++public class BinaryDataFileRandomAccess implements BinaryDataFile {
++    // On OpenJDK, RandomAccessFile and FileChannelImpl both
++    // dive into native code.
++    protected RandomAccessFile file ;
++    protected boolean readMode ;
++    protected long readPosition ;
++    protected long writePosition ;
++    private final String filename ;
++    
++    public BinaryDataFileRandomAccess(String filename) {
++       this.filename = filename ;
++    }
++    
++    @Override
++    public void open() {
++        if ( file != null )
++            throw new RuntimeIOException("Already open") ;
++        try { 
++            file = new RandomAccessFile(filename, "rw") ; 
++            writePosition = file.length() ;
++            readPosition = 0 ;
++            readMode = true ;
++        }
++        catch (IOException e) { IO.exception(e); }
++    }
++
++    @Override
++    public boolean isOpen() {
++        return file != null ;
++    }
++    
++    @Override
++    public int read(long posn, byte[] b, int start, int length) {
++        checkOpen() ;
++        switchToReadMode() ;
++        seek(posn) ;
++        try {
++            int x = file.read(b, start, length) ;
++            readPosition += x ;
++            return x ;
++        }
++        catch (IOException ex) { IO.exception(ex); return -1 ; }
++    }
++
++    @Override
++    public long write(byte[] b, int start, int length) {
++        checkOpen() ;
++        switchToWriteMode() ;
++        long x = writePosition ;
++        try { 
++            file.write(b, start, length) ; 
++            writePosition += length ;
++        }
++        catch (IOException ex) { IO.exception(ex) ; }
++        return x ;
++    }
++
++    
++    // Move the RandomAccess file pointer.
++    private void seek(long posn) {
++        try { file.seek(posn) ; }
++        catch (IOException ex) { IO.exception(ex) ; }
++    }
++
++    @Override
++    public void truncate(long length) {
++        checkOpen() ;
++        switchToWriteMode() ; 
++        try { file.setLength(length); }
++        catch (IOException ex) { IO.exception(ex) ; }
++    }
++
++    @Override
++    public void sync() {
++        checkOpen() ;
++        flush$() ;
++    }
++
++    protected void flush$() {
++        try { file.getFD().sync() ; }
++        catch (IOException ex) { IO.exception(ex) ; }
++    }
++    
++    @Override
++    public void close() {
++        if ( ! isOpen() )
++            return ;
++        try { file.close(); }
++        catch (IOException ex) { IO.exception(ex) ; }
++        file = null ;
++    }
++
++    @Override
++    public long length() {
++        try { return file.length() ;}
++        catch (IOException ex) { IO.exception(ex) ; return -1 ;}
++    }
++    
++    protected void switchToReadMode() {
++        if ( ! readMode )
++            readMode = true ;
++    }
++
++    protected void switchToWriteMode() {
++        if ( readMode ) {
++            readMode = false ;
++            seek(writePosition);
++        }
++    }
++
++    protected void checkOpen() {
++        if ( ! isOpen() ) 
++            throw new RuntimeIOException("Not open") ;
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BinaryDataFileSync.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BinaryDataFileSync.java
index 0000000,0000000..bb5c1b1
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BinaryDataFileSync.java
@@@ -1,0 -1,0 +1,82 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++
++/** Add synchronization to all BinaryDataFile operations.
++ *  This gives the correct thread-safe operation 
++ *  but isn'tnecessarily the best way to do it.
++ */
++public class BinaryDataFileSync implements BinaryDataFile {
++    private final BinaryDataFile other ;
++    
++    public BinaryDataFileSync(BinaryDataFile other) {
++        this.other = other ;
++    }
++    
++    @Override
++    synchronized
++    public void open() { other.open() ; } 
++
++    @Override
++    synchronized
++    public boolean isOpen() { return other.isOpen() ; }
++
++    @Override
++    synchronized
++    public int read(long posn, byte[] b) {
++        return other.read(posn, b) ;
++    }
++
++    @Override
++    synchronized
++    public int read(long posn, byte[] b, int start, int length) {
++        return other.read(posn, b, start, length) ;
++    }
++
++    @Override
++    synchronized
++    public long write(byte[] b) {
++        return other.write(b) ;
++    }
++    
++    @Override
++    synchronized
++    public long write(byte[] b, int start, int length) {
++        return other.write(b, start, length) ;
++    }
++
++    @Override
++    synchronized
++    public long length() {
++        return other.length() ;
++    }
++
++    @Override
++    synchronized
++    public void truncate(long length) { other.truncate(length); }
++
++    @Override
++    synchronized
++    public void sync() { other.sync(); }
++
++    @Override
++    synchronized
++    public void close() { other.close(); }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BinaryDataFileWrapper.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BinaryDataFileWrapper.java
index 0000000,0000000..b970284
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BinaryDataFileWrapper.java
@@@ -1,0 -1,0 +1,67 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++public class BinaryDataFileWrapper implements BinaryDataFile {
++    private final BinaryDataFile other ;
++    
++    public BinaryDataFileWrapper(BinaryDataFile other) {
++        this.other = other ;
++    }
++    
++    @Override
++    public void open() { other.open() ; } 
++
++    @Override
++    public boolean isOpen() { return other.isOpen() ; }
++
++    @Override
++    public int read(long posn, byte[] b) {
++        return other.read(posn, b) ;
++    }
++
++    @Override
++    public int read(long posn, byte[] b, int start, int length) {
++        return other.read(posn, b, start, length) ;
++    }
++
++    @Override
++    public long write(byte[] b) {
++        return other.write(b) ;
++    }
++    
++    @Override
++    public long write(byte[] b, int start, int length) {
++        return other.write(b, start, length) ;
++    }
++
++    @Override
++    public long length() {
++        return other.length() ;
++    }
++
++    @Override
++    public void truncate(long length) { other.truncate(length); }
++
++    @Override
++    public void sync() { other.sync(); }
++
++    @Override
++    public void close() { other.close(); }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BinaryDataFileWriteBuffered.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BinaryDataFileWriteBuffered.java
index 0000000,0000000..da25ce1
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BinaryDataFileWriteBuffered.java
@@@ -1,0 -1,0 +1,162 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++import org.apache.jena.atlas.RuntimeIOException ;
++
++/** Implementation of {@link BinaryDataFile} adding write buffering to another 
++ * {@link BinaryDataFile} file such as a {@link BinaryDataFileRandomAccess}.
++ *  <li>Thread-safe.
++ *  <li>No read buffering provided.
++ *  <li>The write buffer is flushed when switching to read.
++ */
++
++public class BinaryDataFileWriteBuffered implements BinaryDataFile {
++    private static final int SIZE = 128*1024 ;
++    private final Object sync = new Object() ; 
++    private byte[] buffer ;
++    private int bufferLength ;
++    private boolean pendingOutput ;
++    private final BinaryDataFile other ;
++    
++    public BinaryDataFileWriteBuffered(BinaryDataFile other) {
++        this(other, SIZE) ;
++    }
++    
++    public BinaryDataFileWriteBuffered(BinaryDataFile other, int bufferSize) {
++        this.other = other ;
++        buffer = new byte[bufferSize] ;
++    }
++    
++    @Override
++    public void open() {
++        synchronized(sync) {
++            other.open() ;
++            bufferLength = 0 ;
++            pendingOutput = false ;
++        }
++    }
++
++    @Override
++    public void close() {
++        synchronized(sync) {
++            if ( ! isOpen() )
++                return ;
++            writeBuffer();
++            other.close() ;
++        }
++    }    
++
++    @Override
++    public boolean isOpen() {
++        synchronized(sync) {
++            return other.isOpen() ;
++        }
++    }    
++
++    @Override
++    public long length() {
++        synchronized(sync) {
++            return other.length()+bufferLength ;
++        }
++    }    
++
++    @Override
++    public void truncate(long posn) {
++        synchronized(sync) {
++            checkOpen() ;
++            if ( pendingOutput && posn >= other.length() )
++                writeBuffer() ;
++            other.truncate(posn) ;
++        }
++    }    
++
++    private void checkOpen() {
++        if ( ! other.isOpen() )
++            throw new RuntimeIOException("Not open") ;
++    }    
++
++    @Override
++    public int read(long posn, byte[] b, int start, int length) {
++        synchronized(sync) {
++            // Overlap with buffered area
++            // We flush the write buffer for a read so no need to check.
++            checkOpen() ;
++            switchToReadMode() ;
++            return other.read(posn, b, start, length) ;
++        }
++    }    
++
++    @Override
++    public long write(byte[] buf, int off, int len) {
++        synchronized(sync) {
++            checkOpen() ;
++            switchToWriteMode() ;
++            long x = length() ;
++        
++//        if ( false ) {
++//            // No buffering
++//            try { file.write(buf, off, len) ; }
++//            catch (IOException e) { IO.exception(e); }
++//            bufferLength = 0 ;
++//            return ;
++//        }
++
++            // No room.
++            if ( bufferLength + len >= SIZE )
++                writeBuffer() ;
++
++            if ( bufferLength + len < SIZE ) {
++                // Room to buffer
++                System.arraycopy(buf, off, buffer, bufferLength, len);
++                bufferLength += len ;
++                pendingOutput = true ;
++                return x ;
++            } 
++            // Larger than the buffer space.  Write directly.
++            other.write(buf, off, len) ;
++            return x ;
++        }
++    }    
++
++    @Override
++    public void sync()  {
++        synchronized(sync) {
++            writeBuffer() ;
++            other.sync(); 
++        }
++    }
++    
++    private void writeBuffer() {
++        if ( pendingOutput ) {
++            pendingOutput = false ;
++            other.write(buffer, 0, bufferLength) ;
++            bufferLength = 0 ;
++        }
++    }
++
++    // Inside synchronization
++    protected void switchToWriteMode() {
++    }
++
++    // Inside synchronization
++    protected void switchToReadMode() {
++        writeBuffer() ;
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BlockAccess.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BlockAccess.java
index 0000000,0000000..ba8d491
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BlockAccess.java
@@@ -1,0 -1,0 +1,64 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++import org.apache.jena.atlas.lib.Closeable ;
++import org.apache.jena.atlas.lib.Sync ;
++import org.seaborne.dboe.base.block.Block ;
++
++/**
++ * Interface to concrete storage - read and write Blocks, addressed by id. 
++ * Suitable for memory mapped I/O (returns
++ * internally allocated space for read, not provided from outside; write() can
++ * insist the block written comes from allocate()).
++ * This interfce can also be backed by an in-memory implemntation 
++ * ({@link BlockAccessMem}, {@link BlockAccessByteArray}).
++ * 
++ * This is wrapped in a BlockMgr to provide a higher level abstraction.
++ * 
++ * @see BufferChannel
++ */
++public interface BlockAccess extends Sync, Closeable
++{
++    public Block allocate(int size) ;
++    
++    public Block read(long id) ;
++    
++    public void write(Block block) ;
++    
++    public void overwrite(Block block) ;
++    
++    public boolean isEmpty() ; 
++    
++    /* The limit of the current allocation space.
++     * Allocated blocks have ids in [0, allocBoundary).
++     * Allocation units need not be in +1 increments.
++      */  
++    public long allocBoundary() ;
++
++    /** Reset the allocation space (i.e. truncate).
++     * The boundary should be a number obtained from a previous
++     * call of allocLimit. It can not be greater than the current
++     * allocation boundary.
++     */
++    public void resetAllocBoundary(long boundary) ;
++    
++    public boolean valid(long id) ;
++
++    public String getLabel() ;
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BlockAccessBase.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BlockAccessBase.java
index 0000000,0000000..1244cd8
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BlockAccessBase.java
@@@ -1,0 -1,0 +1,175 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++import static java.lang.String.format ;
++
++import java.nio.ByteBuffer ;
++import java.nio.channels.FileChannel ;
++import java.util.concurrent.atomic.AtomicLong ;
++
++import org.seaborne.dboe.base.block.Block ;
++import org.seaborne.dboe.base.block.BlockException ;
++import org.seaborne.dboe.sys.FileLib ;
++import org.seaborne.dboe.sys.Sys;
++import org.slf4j.Logger ;
++
++/** Support for a disk file backed FileAccess */
++public abstract class BlockAccessBase implements BlockAccess {
++    protected final int          blockSize ;
++    protected FileChannel        file ;
++    protected final String       filename ;
++
++    protected final String     label ;
++    // Does this need to be tread safe?
++    // Only changes in a write transaction
++    
++    // Don't overload use of this!
++    protected final AtomicLong seq ;
++    protected long             numFileBlocks = -1 ; 
++
++    public BlockAccessBase(String filename, int blockSize) {
++        this.filename = filename ;
++        this.file = FileLib.openManaged(filename) ;
++        this.blockSize = blockSize ;
++        this.label = label(filename) ;
++        // This is not related to used file length in mapped mode.
++        long filesize = FileLib.size(file) ;
++        long longBlockSize = blockSize ;
++
++        numFileBlocks = filesize / longBlockSize ; // This is not related to
++                                                   // used file length in mapped
++                                                   // mode.
++        seq = new AtomicLong(numFileBlocks) ;
++
++        if ( numFileBlocks > Integer.MAX_VALUE )
++            getLog().warn(format("File size (%d) exceeds tested block number limits (%d)", filesize, blockSize)) ;
++
++        if ( filesize % longBlockSize != 0 )
++            throw new BlockException(format("File size (%d) not a multiple of blocksize (%d)", filesize, blockSize)) ;
++    }
++
++    /** Find path compoent, with extension */
++    private static String label(String filename) {
++        int j = filename.lastIndexOf('/') ;
++        if ( j < 0 )
++            j = filename.lastIndexOf('\\') ;
++        String fn = (j >= 0) ? filename.substring(j + 1) : filename ;
++        return fn ;
++    }
++    
++    protected abstract Logger getLog() ;
++
++    @Override
++    final public boolean isEmpty() {
++        return numFileBlocks <= 0 ;
++    }
++
++    final protected void writeNotification(Block block) {}
++
++    final protected void overwriteNotification(Block block) {
++        // Write at end => extend
++        if ( block.getId() >= numFileBlocks ) {
++            numFileBlocks = block.getId() + 1 ;
++            seq.set(numFileBlocks) ;
++        }
++    }
++
++    final protected int allocateId() {
++        checkIfClosed() ;
++        int id = (int)seq.getAndIncrement() ;
++        numFileBlocks++ ; // TODO Fix this when proper freeblock management is
++                          // introduced.
++        return id ;
++    }
++
++    @Override
++    final public long allocBoundary() {
++        checkIfClosed() ;
++        return seq.get() ;
++        // Underlying area is untouched.
++    }
++
++    @Override
++    final public void resetAllocBoundary(long boundary) {
++        checkIfClosed() ;
++        seq.set(boundary) ;
++        _resetAllocBoundary(boundary) ;
++    }
++
++    protected abstract void _resetAllocBoundary(long boundary) ;
++
++    @Override
++    final synchronized public boolean valid(long id) {
++        if ( id >= numFileBlocks )
++            return false ;
++        if ( id < 0 )
++            return false ;
++        return true ;
++    }
++
++    final protected void check(long id) {
++        if ( id > Integer.MAX_VALUE )
++            throw new BlockException(format("BlockAccessBase: Id (%d) too large", id)) ;
++
++        // Access to numFileBlocks not synchronized - it's only a check
++        if ( id < 0 || id >= numFileBlocks ) {
++            // Do it properly!
++            synchronized (this) {
++                if ( id < 0 || id >= numFileBlocks )
++                    throw new BlockException(format("BlockAccessBase: Bounds exception: %s: (%d,%d)", filename, id, numFileBlocks)) ;
++            }
++        }
++    }
++
++    final protected void check(Block block) {
++        check(block.getId()) ;
++        ByteBuffer bb = block.getByteBuffer() ;
++        if ( bb.capacity() != blockSize )
++            throw new BlockException(format("BlockMgrFile: Wrong size block.  Expected=%d : actual=%d", blockSize, bb.capacity())) ;
++        if ( bb.order() != Sys.NetworkOrder )
++            throw new BlockException("BlockMgrFile: Wrong byte order") ;
++    }
++
++    protected void force() {
++        FileLib.sync(file) ;
++    }
++
++    // @Override
++    final public boolean isClosed() {
++        return file == null ;
++    }
++
++    protected final void checkIfClosed() {
++        if ( isClosed() )
++            getLog().error("File has been closed") ;
++    }
++
++    protected abstract void _close() ;
++
++    @Override
++    final public void close() {
++        _close() ;
++        file = null ;
++    }
++
++    @Override
++    public String getLabel() {
++        return label ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BlockAccessByteArray.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BlockAccessByteArray.java
index 0000000,0000000..45d932c
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BlockAccessByteArray.java
@@@ -1,0 -1,0 +1,129 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file ;
++
++import java.nio.ByteBuffer ;
++
++import org.apache.jena.atlas.lib.ByteBufferLib ;
++import org.seaborne.dboe.base.block.Block ;
++import static org.seaborne.dboe.sys.Sys.SizeOfInt ;
++
++/**
++ * FileAccess interface backed by a byte array.
++ */
++public class BlockAccessByteArray implements BlockAccess {
++    private ByteBuffer   bytes ;
++    private long         length ; // Bytes in use: 0 to length-1
++    private long         alloc ;  // Bytes allocated - next allocation number.
++    private final String label ;
++
++    public BlockAccessByteArray(String label) {
++        bytes = ByteBuffer.allocate(1024) ;
++        length = 0 ;
++        alloc = 0 ;
++        this.label = label ;
++    }
++
++    @Override
++    public String getLabel() {
++        return label ;
++    }
++
++    @Override
++    public Block allocate(int size) {
++        long addr = alloc ;
++        ByteBuffer bb = ByteBuffer.allocate(size) ;
++        alloc += (size + SizeOfInt) ;
++        return new Block((int)addr, bb) ;
++    }
++
++    @Override
++    public long allocBoundary() {
++        return alloc ;
++    }
++
++    @Override
++    public void resetAllocBoundary(long boundary) {
++        ByteBufferLib.fill(bytes, (int)boundary, (int)length, (byte)0);
++        length = boundary ;
++        alloc = boundary ;
++        bytes.limit((int)alloc) ;
++        
++    }
++    
++    @Override
++    public Block read(long id) {
++        // Variable length blocks.
++        if ( id < 0 || id >= length || id >= bytes.capacity() )
++            throw new FileException("Bad id (read): " + id) ;
++        bytes.position((int)id) ;
++        int len = bytes.getInt() ;
++        ByteBuffer bb = ByteBuffer.allocate(len) ;
++        // Copy out the bytes - copy for safety.
++        bytes.get(bb.array(), 0, len) ;
++        return new Block(id, bb) ;
++    }
++
++    @Override
++    public void write(Block block) {
++        // Variable length blocks.
++        long loc = block.getId() ;
++        if ( loc < 0 || loc > length ) // Can be equal => append.
++            throw new FileException("Bad id (write): " + loc + " (" + alloc + "," + length + ")") ;
++        ByteBuffer bb = block.getByteBuffer() ;
++        int len = bb.capacity() ;
++
++        if ( loc == length ) {
++            if ( bytes.capacity() - length < len ) {
++                int cap2 = bytes.capacity() + 1024 ;
++                while (bytes.capacity() - length < len)
++                    cap2 += 1024 ;
++
++                ByteBuffer bytes2 = ByteBuffer.allocate(cap2) ;
++                bytes2.position(0) ;
++                bytes2.put(bytes) ;
++                bytes = bytes2 ;
++            }
++            length += len + SizeOfInt ;
++        }
++        bytes.position((int)loc) ;
++        bytes.putInt(len) ;
++        bytes.put(bb.array(), 0, bb.capacity()) ;
++    }
++
++    @Override
++    public void overwrite(Block block) {
++        write(block) ;
++    }
++
++    @Override
++    public boolean isEmpty() {
++        return length == 0 ;
++    }
++
++    @Override
++    public boolean valid(long id) {
++        return (id >= 0 && id < length) ;
++    }
++
++    @Override
++    public void sync() {}
++
++    @Override
++    public void close() {}
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BlockAccessDirect.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BlockAccessDirect.java
index 0000000,0000000..62b4f43
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BlockAccessDirect.java
@@@ -1,0 -1,0 +1,130 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++import static java.lang.String.format ;
++
++import java.io.IOException ;
++import java.nio.ByteBuffer ;
++
++import org.apache.jena.atlas.lib.FileOps ;
++import org.seaborne.dboe.base.block.Block ;
++import org.seaborne.dboe.sys.FileLib ;
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++public class BlockAccessDirect extends BlockAccessBase
++{
++    // Maybe layer BlockAccess on BufferChannel - retrofitting.
++    // but need separate for memory mapped files anyway.
++
++    private static Logger log = LoggerFactory.getLogger(BlockAccessDirect.class) ;
++    
++    public BlockAccessDirect(String filename, int blockSize)
++    {
++        super(filename, blockSize) ;
++    }
++
++    @Override
++    public Block allocate(int blkSize)
++    {
++        if ( blkSize > 0 && blkSize != this.blockSize )
++            throw new FileException("Fixed blocksize only: request= "+blkSize+"fixed size="+this.blockSize) ;
++        int x = allocateId() ;
++        ByteBuffer bb = ByteBuffer.allocate(blkSize) ;
++        Block block = new Block(x, bb) ;
++        return block;
++    }
++    
++    @Override
++    public Block read(long id)
++    {
++        check(id) ;
++        checkIfClosed() ;
++        ByteBuffer bb = ByteBuffer.allocate(blockSize) ;
++        readByteBuffer(id, bb) ;
++        bb.rewind() ;
++        Block block = new Block(id, bb) ;
++        return block ;
++    }
++    
++    private void readByteBuffer(long id, ByteBuffer dst)
++    {
++        try {
++            int len = file.read(dst, filePosition(id)) ;
++            if ( len != blockSize )
++                throw new FileException(format("get: short read (%d, not %d)", len, blockSize)) ;   
++        } catch (IOException ex)
++        { throw new FileException("FileAccessDirect", ex) ; }
++    }
++    
++    private final long filePosition(long id)
++    {
++        return id*blockSize ;
++    }
++
++    @Override
++    public void write(Block block)
++    {
++        check(block) ;
++        checkIfClosed() ;
++        ByteBuffer bb = block.getByteBuffer() ;
++        // This .clear() except the javadoc suggests this is not the correct use of .clear()
++        // and the name does 
++        bb.limit(bb.capacity()) ;   // It shouldn't have been changed.
++        bb.rewind() ;
++        try {
++            int len = file.write(bb, filePosition(block.getId())) ;
++            if ( len != blockSize )
++                throw new FileException(format("write: short write (%d, not %d)", len, blockSize)) ;   
++        } catch (IOException ex)
++        { throw new FileException("FileAccessDirect", ex) ; }
++        writeNotification(block) ;
++    }
++    
++    @Override
++    public void overwrite(Block block)
++    {
++        overwriteNotification(block) ;
++        write(block) ;
++    }
++
++    @Override
++    protected void _resetAllocBoundary(long boundary) {
++        FileLib.truncate(file, filePosition(boundary)) ;
++    }
++
++    @Override
++    public void sync()
++    {
++        force() ;
++    }
++
++    @Override
++    protected void _close()
++    { super.force() ; }
++
++    @Override
++    protected Logger getLog()
++    {
++        return log ;
++    }
++    
++    @Override
++    public String toString() { return "Direct:"+FileOps.basename(filename) ; }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BlockAccessMapped.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BlockAccessMapped.java
index 0000000,0000000..636d8f6
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BlockAccessMapped.java
@@@ -1,0 -1,0 +1,267 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++import static java.lang.String.format ;
++
++import java.io.IOException ;
++import java.nio.ByteBuffer ;
++import java.nio.MappedByteBuffer ;
++import java.nio.channels.FileChannel.MapMode ;
++import java.util.Arrays ;
++
++import org.seaborne.dboe.base.block.Block ;
++import org.seaborne.dboe.sys.SystemIndex ;
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++/** FileAccess for a file, using memory mapped I/O */
++final
++public class BlockAccessMapped extends BlockAccessBase
++{
++    /* Blocks are addressed by positive ints - 
++     * Is that a limit?
++     * One billion is 2^30
++     * If a block is 8K, the 2^31*2^13 =  2^44 bits or 2^14 billion = 16K Billion. = 16 trillion bytes.
++     * No limit at the moment - later performance tuning will see what the cost of 48 or 63 bit addresses would be.    
++     */
++    
++    private static Logger log = LoggerFactory.getLogger(BlockAccessMapped.class) ;
++    private enum CopyContents { Overwrite, NoCopy } 
++    
++    // Segmentation avoids over-mapping; allows file to grow (in chunks) 
++    private final int GrowthFactor = 2 ;
++    private final int SegmentSize = SystemIndex.SegmentSize ;
++    private final int blocksPerSegment ;                              
++    
++    private int initialNumSegements = 1 ;
++    private MappedByteBuffer[] segments = new MappedByteBuffer[initialNumSegements] ;  
++    
++    // Unflushed segments.
++    private int segmentDirtyCount = 0 ;
++    private boolean[] segmentDirty = new boolean[initialNumSegements] ; 
++    
++    public BlockAccessMapped(String filename, int blockSize) {
++        super(filename, blockSize) ;
++        blocksPerSegment = SegmentSize/blockSize ;
++        if ( SegmentSize%blockSize != 0 )
++            getLog().warn(format("%s: Segment size(%d) not a multiple of blocksize (%d)", filename, SegmentSize, blockSize)) ;
++        
++        for ( int i = 0 ; i < initialNumSegements ; i++ )
++            // Not strictly necessary - default value is false.
++            segmentDirty[i] = false ;
++        segmentDirtyCount = 0 ;
++        
++        if ( getLog().isDebugEnabled() )
++            getLog().debug(format("Segment:%d  BlockSize=%d  blocksPerSegment=%d", SegmentSize, blockSize, blocksPerSegment)) ;
++    }
++    
++    
++    @Override
++    public Block allocate(int blkSize) {
++        if ( blkSize > 0 && blkSize != this.blockSize )
++            throw new FileException("Fixed blocksize only: request= "+blkSize+"fixed size="+this.blockSize) ;
++        int id = allocateId() ;
++        ByteBuffer bb = getByteBuffer(id) ;
++        bb.position(0) ;
++        Block block = new Block(id, bb) ;
++        return block ;
++    }
++    
++    @Override
++    public Block read(long id) {
++        check(id) ;
++        checkIfClosed() ;
++        ByteBuffer bb = getByteBuffer(id) ;
++        bb.position(0) ;
++        Block block = new Block(id, bb) ;
++        return block ;
++    }
++
++    @Override
++    public void write(Block block) {
++        write(block, CopyContents.NoCopy) ;
++    }
++    
++    @Override
++    public void overwrite(Block block) {
++        overwriteNotification(block) ;
++        write(block, CopyContents.Overwrite) ;
++    }
++
++    private void write(Block block, CopyContents copyContents) {
++        check(block) ;
++        checkIfClosed() ;
++        int id = block.getId().intValue() ;
++        
++        if ( copyContents == CopyContents.Overwrite ) {
++            ByteBuffer bbDst = getByteBuffer(id) ;
++            bbDst.position(0) ;
++            ByteBuffer bbSrc = block.getByteBuffer() ;
++            bbSrc.rewind() ;
++            bbDst.put(bbSrc) ;
++        }
++        
++        // Assumed MRSW - no need to sync as we are the only Writer
++        segmentDirty[segment(id)] = true ;
++        writeNotification(block) ;
++    }
++    
++    @Override
++    public void sync() {
++        checkIfClosed() ;
++        force() ;
++    }
++
++    private ByteBuffer getByteBuffer(long _id) {
++        // Limitation: ids must be integers.
++        // ids are used to index into []-arrays.
++        int id = (int)_id ;
++        
++        int seg = segment(id) ;                 // Segment.
++        int segOff = byteOffset(id) ;           // Byte offset in segment
++
++        if ( getLog().isTraceEnabled() ) 
++            getLog().trace(format("%d => [%d, %d]", id, seg, segOff)) ;
++
++        synchronized (this) {
++            try {
++                // Need to put the alloc AND the slice/reset inside a sync.
++                ByteBuffer segBuffer = allocSegment(seg) ;
++                // Now slice the buffer to get the ByteBuffer to return
++                segBuffer.position(segOff) ;
++                segBuffer.limit(segOff+blockSize) ;
++                ByteBuffer dst = segBuffer.slice() ;
++                
++                // And then reset limit to max for segment.
++                segBuffer.limit(segBuffer.capacity()) ;
++                // Extend block count when we allocate above end. 
++                numFileBlocks = Math.max(numFileBlocks, id+1) ;
++                return dst ;
++            } catch (IllegalArgumentException ex) {
++                // Shouldn't (ha!) happen because the second "limit" resets 
++                log.error("Id: "+id) ;
++                log.error("Seg="+seg) ;
++                log.error("Segoff="+segOff) ;
++                log.error(ex.getMessage(), ex) ;
++                throw ex ;
++            }
++        }
++    }
++    
++    private final int segment(int id)                               { return id/blocksPerSegment ; }
++    private final int byteOffset(int id)                            { return (id%blocksPerSegment)*blockSize ; }
++    private final long fileLocationForSegment(long segmentNumber)   { return segmentNumber*SegmentSize ; }
++    
++    // Even for MultipleReader this needs to be sync'ed.??
++    private MappedByteBuffer allocSegment(int seg)
++    {
++        // Auxiliary function for get - which holds the lock needed here.
++        // The MappedByteBuffer must be sliced and reset once found/allocated
++        // so as not to mess up the underlying MappedByteBuffer in segments[].
++        
++        // Only allocSegment(seg) and flushDirtySegements() and close()
++        // directly access segments[] 
++
++        if ( seg < 0 ) {
++            getLog().error("Segment negative: "+seg) ;
++            throw new FileException("Negative segment: "+seg) ;
++        }
++
++        while ( seg >= segments.length ) {
++            // More space needed.
++            MappedByteBuffer[] segments2 = new MappedByteBuffer[GrowthFactor*segments.length] ;
++            System.arraycopy(segments, 0, segments2, 0, segments.length) ;
++            boolean[] segmentDirty2 = new boolean[GrowthFactor*segmentDirty.length] ;
++            System.arraycopy(segmentDirty, 0, segmentDirty2, 0, segmentDirty.length) ;
++
++            segmentDirty = segmentDirty2 ;
++            segments = segments2 ;
++        }
++        
++        long offset = fileLocationForSegment(seg) ;
++        
++        if ( offset < 0 ) {
++            getLog().error("Segment offset gone negative: "+seg) ;
++            throw new FileException("Negative segment offset: "+seg) ;
++        }
++        
++        MappedByteBuffer segBuffer = segments[seg] ;
++        if ( segBuffer == null ) {
++            try {
++                segBuffer = file.map(MapMode.READ_WRITE, offset, SegmentSize) ;
++                if ( getLog().isDebugEnabled() )
++                    getLog().debug(format("Segment: %d", seg)) ;
++                segments[seg] = segBuffer ;
++            }
++            catch (IOException ex) {
++                if ( ex.getCause() instanceof java.lang.OutOfMemoryError )
++                    throw new FileException("BlockMgrMapped.segmentAllocate: Segment = " + seg + " : Offset = " + offset) ;
++                throw new FileException("BlockMgrMapped.segmentAllocate: Segment = " + seg, ex) ;
++            }
++        }
++        return segBuffer ;
++    }
++
++    private synchronized void flushDirtySegments() {
++
++        // A linked list (with uniqueness) of dirty segments may be better.
++        for ( int i = 0 ; i < segments.length ; i++ ) {
++            if ( segments[i] != null && segmentDirty[i] ) {
++                // Can we "flush" them all at once?
++                segments[i].force() ;
++                segmentDirty[i] = false ;
++                segmentDirtyCount-- ;
++            }
++        }
++        // This on its own does not force dirty segments to disk.
++        super.force() ;
++    }
++
++    @Override
++    protected void _resetAllocBoundary(long boundary) {
++        
++    }
++
++
++    @Override
++    protected void _close() {
++        force() ;
++        // There is no unmap operation for MappedByteBuffers.
++        // Sun Bug id bug_id=4724038
++        // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4724038
++        Arrays.fill(segments, null) ;
++        Arrays.fill(segmentDirty, false) ;
++        segmentDirtyCount = 0 ;
++    }
++    
++    @Override
++    protected void force() {
++        flushDirtySegments() ;
++    }
++
++    @Override
++    protected Logger getLog() {
++        return log ;
++    }
++
++    @Override
++    public String toString() {
++        return super.getLabel() ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BlockAccessMem.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BlockAccessMem.java
index 0000000,0000000..c2159f2
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BlockAccessMem.java
@@@ -1,0 -1,0 +1,188 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++import static java.lang.String.format ;
++
++import java.nio.ByteBuffer ;
++import java.util.ArrayList ;
++import java.util.List ;
++
++import org.apache.jena.atlas.RuntimeIOException ;
++import org.seaborne.dboe.base.block.Block ;
++import org.seaborne.dboe.sys.Sys;
++
++/**
++ * File access layer that simulates a disk in-memory - for testing, not written for efficiency.
++ * There is a safe mode, whereby blocks are copied in and out to guarantee no writing to an unallocated block.
++ * This is very inefficient but a better simulation of a disk.
++ * 
++ * @see BlockAccessByteArray
++ */
++
++public class BlockAccessMem implements BlockAccess
++{
++    // "SafeMode" - duplicate the block data much like a disk.
++    // Mildly expensive for large block sizes. 
++    
++    public static boolean SafeMode = true ;
++    static final boolean Checking = true ;
++    boolean fileClosed = false ;
++    private List<Block> blocks = new ArrayList<>() ;
++    private final boolean safeModeThisMgr ;
++    protected final int blockSize ;
++    private final String label ;
++    
++    public BlockAccessMem(String label, int blockSize) {
++        this(label, blockSize, SafeMode) ;
++    }
++
++    private BlockAccessMem(String label, int blockSize, boolean b) {
++        this.blockSize = blockSize ;
++        this.label = label ;
++        safeModeThisMgr = b ;
++    }
++
++    @Override
++    public Block allocate(int blkSize) {
++        checkNotClosed() ;
++        if ( blkSize > 0 && blkSize != this.blockSize )
++            throw new FileException("Fixed blocksize only: request= " + blkSize + " / fixed size=" + this.blockSize) ;
++
++        int x = blocks.size() ;
++        ByteBuffer bb = ByteBuffer.allocate(blkSize) ;
++        Block block = new Block(x, bb) ;
++        blocks.add(block) ;
++        return block ;
++    }
++
++    @Override
++    public Block read(long id) {
++        checkNotClosed() ;
++        check(id) ;
++        Block blk = blocks.get((int)id) ;
++        blk = replicateBlock(blk) ;
++        blk.setModified(false) ; 
++        return blk ;
++    }
++
++    @Override
++    public void write(Block block) {
++        checkNotClosed() ;
++        check(block) ;
++        _write(block) ;
++    }
++
++    @Override
++    public void overwrite(Block block) {
++        checkNotClosed() ;
++        write(block) ;
++    }
++    
++    private void _write(Block block) {
++        block = replicateBlock(block) ;
++        block.setModified(false) ;
++        // Memory isn't scaling to multi gigabytes.
++        blocks.set(block.getId().intValue(), block) ;
++    }
++
++    private Block replicateBlock(Block blk) {
++        if ( safeModeThisMgr )
++            // Deep replicate.
++            return  blk.replicate() ;
++        // Just the block wrapper.
++        return new Block(blk.getId(), blk.getByteBuffer()) ;
++    }
++    
++    @Override
++    public boolean isEmpty() {
++        checkNotClosed() ;
++        return blocks.isEmpty() ;
++    }
++
++    @Override
++    public long allocBoundary() {
++        checkNotClosed() ;
++        return blocks.size() ;
++    }
++
++    @Override
++    public void resetAllocBoundary(long boundary) {
++        checkNotClosed() ;
++        // Clear the list from boundary onwards.
++        blocks.subList((int)boundary, blocks.size()).clear() ;
++    }
++    
++    @Override
++    public boolean valid(long id) {
++        checkNotClosed() ;
++        return id >= 0 && id < blocks.size() ;
++    }
++
++    private void checkNotClosed() {
++        if ( fileClosed )
++            throw new RuntimeIOException("Already closed") ;
++    }
++
++    @Override
++    public void close() {
++        if ( fileClosed )
++            return ;
++        fileClosed = true ;
++        blocks = null ;
++    }
++
++    @Override
++    public void sync() {
++        checkNotClosed() ;
++    }
++
++
++    private void check(Block block) {
++        check(block.getId()) ;
++        check(block.getByteBuffer()) ;
++    }
++
++    private void check(long id) {
++        if ( id > Integer.MAX_VALUE )
++            throw new FileException("BlockAccessMem: Bounds exception (id large than an int): " + id) ;
++        if ( !Checking )
++            return ;
++        if ( id < 0 || id >= blocks.size() )
++            throw new FileException("BlockAccessMem: "+label+": Bounds exception: " + id + " in [0, "+blocks.size()+")") ;
++    }
++
++    private void check(ByteBuffer bb) {
++        if ( !Checking )
++            return ;
++        if ( bb.capacity() != blockSize )
++            throw new FileException(format("FileAccessMem: Wrong size block.  Expected=%d : actual=%d", blockSize, bb.capacity())) ;
++        if ( bb.order() != Sys.NetworkOrder )
++            throw new FileException("BlockMgrMem: Wrong byte order") ;
++    }
++
++    @Override
++    public String getLabel() {
++        return label ;
++    }
++
++    @Override
++    public String toString() {
++        return "Mem:" + label ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BufferChannel.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BufferChannel.java
index 0000000,0000000..9858181
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BufferChannel.java
@@@ -1,0 -1,0 +1,95 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++import java.nio.ByteBuffer ;
++import java.nio.channels.FileChannel ;
++
++import org.apache.jena.atlas.lib.Closeable ;
++import org.apache.jena.atlas.lib.Sync ;
++
++
++/**
++ * Interface to storage : a simplified version of FileChannel. Read and write
++ * bytes, passed via ByteBuffers, addressed by file location. This interface is
++ * not suitable for memory mapped I/O - there is no ability to use slices of a
++ * memort mapped file. This interface does not insert size of ByteBuffer - size
++ * of ByteBuffer passed to read controls the number of bytes read. Having our
++ * own abstraction enables us to implement memory-backed versions.
++ * 
++ * @see BlockAccess
++ * @see java.nio.channels.FileChannel
++ */
++public interface BufferChannel extends Sync, Closeable
++{
++    // Like java.nio.channels.FileChannel except
++    //   Allows implementation by non-files e.g. a memory backed storage.
++    //   Runtime exceptions.
++    // This is a simple, low level "file = array of bytes" interface"
++    // This interface does not support slicing - so it's not suitable for memory mapped I/O
++    // 
++    // TODO Consider use of allocateDirect 
++    
++    /** Return another channel to the same storage but with independent position.
++     * Chaos may result due to concurrent use.
++     */
++    public BufferChannel duplicate() ;
++    
++    /** return the position */
++    public long position() ;
++    
++    /** set the position */
++    public void position(long pos) ;
++
++    /** Read into a ByteBuffer. Returns the number of bytes read. -1 for end of file.
++     */
++    public int read(ByteBuffer buffer) ;
++    
++    /** Read into a ByteBuffer, starting at position loc. Return the number of bytes read.
++     * loc must be within the file.
++     */
++    public int read(ByteBuffer buffer, long loc) ;
++
++    /** Write from ByteBuffer, starting at the current position.  
++     * Return the number of bytes written
++     */
++    public int write(ByteBuffer buffer) ;
++    
++    /** Write from ByteBuffer, starting at position loc.  
++     * Return the number of bytes written.
++     * loc must be within 0 to length - writing at length is append */
++    public int write(ByteBuffer buffer, long loc) ;
++    
++    /** Truncate the file.
++     * @see FileChannel#truncate(long)
++     */
++    public void truncate(long size) ;
++    
++    /** Length of storage, in bytes.*/
++    public long size() ;
++    
++    /** Is it empty? */
++    public boolean isEmpty() ;
++
++    /** useful display string */ 
++    public String getLabel() ; 
++    
++    /** Filename for this BufferChannel (maybe null) */ 
++    public String getFilename() ; 
++    
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BufferChannelFile.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BufferChannelFile.java
index 0000000,0000000..216bff2
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BufferChannelFile.java
@@@ -1,0 -1,0 +1,171 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file ;
++
++import java.io.IOException ;
++import java.nio.ByteBuffer ;
++import java.nio.channels.FileChannel ;
++
++import org.apache.jena.atlas.io.IO ;
++import org.seaborne.dboe.sys.FileLib ;
++
++public class BufferChannelFile implements BufferChannel {
++    private String      filename ;
++    private FileChannel file ;
++
++    /** Create a BufferChannelFile */
++    public static BufferChannelFile create(String filename) {
++        return create(filename, "rw") ;
++    }
++
++    /** Create a BufferChannelFile */
++    public static BufferChannelFile create(String filename, String mode) {
++        FileChannel base = ChannelManager.acquire(filename, mode) ;
++        return new BufferChannelFile(filename, base) ;
++    }
++
++    /** Create a BufferChannelFile with unmangaged file resources - use with care */
++    public static BufferChannelFile createUnmanaged(String filename, String mode) {
++        FileChannel channel = FileLib.openUnmanaged(filename, mode) ;
++        return new BufferChannelFile(filename, channel) ;
++    }
++
++    private BufferChannelFile(String filename, FileChannel channel) {
++        this.filename = filename ;
++        this.file = channel ;
++    }
++
++    @Override
++    public BufferChannel duplicate() {
++        return new BufferChannelFile(filename, file) ;
++    }
++
++    @Override
++    public long position() {
++        try {
++            return file.position() ;
++        }
++        catch (IOException e) {
++            IO.exception(e) ;
++            return -1 ;
++        }
++    }
++
++    @Override
++    public void position(long pos) {
++        try {
++            file.position(pos) ;
++        }
++        catch (IOException e) {
++            IO.exception(e) ;
++        }
++    }
++
++    @Override
++    public void truncate(long length) {
++        try {
++            // http://bugs.sun.com/view_bug.do?bug_id=6191269
++            if ( length < file.position() )
++                file.position(length) ;
++            file.truncate(length) ;
++        }
++        catch (IOException e) {
++            IO.exception(e) ;
++        }
++    }
++
++    @Override
++    public int read(ByteBuffer buffer) {
++        try {
++            return file.read(buffer) ;
++        }
++        catch (IOException e) {
++            IO.exception(e) ;
++            return -1 ;
++        }
++    }
++
++    @Override
++    public int read(ByteBuffer buffer, long loc) {
++        try {
++            return file.read(buffer, loc) ;
++        }
++        catch (IOException e) {
++            IO.exception(e) ;
++            return -1 ;
++        }
++    }
++
++    @Override
++    public int write(ByteBuffer buffer) {
++        try {
++            return file.write(buffer) ;
++        }
++        catch (IOException e) {
++            IO.exception(e) ;
++            return -1 ;
++        }
++    }
++
++    @Override
++    public int write(ByteBuffer buffer, long loc) {
++        try {
++            return file.write(buffer, loc) ;
++        }
++        catch (IOException e) {
++            IO.exception(e) ;
++            return -1 ;
++        }
++    }
++
++    @Override
++    public long size() {
++        return FileLib.size(file) ; 
++    }
++
++    @Override
++    public boolean isEmpty() {
++        return size() == 0 ;
++    }
++
++    @Override
++    public void sync() {
++        FileLib.sync(file) ;
++    }
++
++    @Override
++    public void close() {
++        FileLib.close(file) ;
++    }
++
++    @Override
++    public String getLabel() {
++        return filename ;
++    }
++
++    @Override
++    public String toString() {
++        return filename ;
++    }
++
++    @Override
++    public String getFilename() {
++        return filename ;
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BufferChannelMem.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BufferChannelMem.java
index 0000000,0000000..ea3c6ba
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/BufferChannelMem.java
@@@ -1,0 -1,0 +1,240 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file ;
++
++import java.nio.ByteBuffer ;
++
++import org.apache.jena.atlas.lib.ByteBufferLib ;
++import org.apache.jena.atlas.logging.FmtLog ;
++import org.seaborne.dboe.base.StorageException ;
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++// XXX Merge/replace with SegmentedByteBuffer which has more predicable performance. 
++public class BufferChannelMem implements BufferChannel {
++    private static Logger log       = LoggerFactory.getLogger(BufferChannelMem.class) ;
++    // The "file pointer" is the position of this buffer. 
++    // The "size" is the limit of this buffer.
++    private ByteBuffer    bytes ;
++    private String        name ;
++    private static int    INC_SIZE  = 1024 ;
++
++    private final boolean TRACKING ;
++
++    static public BufferChannel create() {
++        return new BufferChannelMem("unnamed") ;
++    }
++
++    static public BufferChannel create(String name) {
++        return new BufferChannelMem(name) ;
++    }
++
++    private BufferChannelMem() {
++        // Unitialized blank.
++        TRACKING = false ;
++    }
++
++    private BufferChannelMem(String name) {
++        bytes = ByteBuffer.allocate(1024) ;
++        bytes.limit(0) ;
++        this.name = name ;
++        TRACKING = false ;
++        // Debugging : pick a filename.
++        // TRACKING = name.endsWith("prefixes.dat") ;
++    }
++
++    @Override
++    synchronized public BufferChannel duplicate() {
++        BufferChannelMem chan = new BufferChannelMem() ;
++        int x = bytes.position() ;
++        bytes.rewind() ;
++        chan.bytes = bytes.slice() ;
++        chan.bytes.position(0) ;
++        bytes.position(x) ;
++        return chan ;
++    }
++
++    @Override
++    synchronized public long position() {
++        checkIfClosed() ;
++        return bytes.position() ;
++    }
++
++    @Override
++    synchronized public void position(long pos) {
++        checkIfClosed() ;
++        if ( pos < 0 || pos > bytes.capacity() )
++            throw new StorageException("Out of range: " + pos) ;
++        bytes.position((int)pos) ;
++    }
++
++    @Override
++    synchronized public int read(ByteBuffer buffer) {
++        checkIfClosed() ;
++        if ( TRACKING )
++            log("read<<%s", ByteBufferLib.details(buffer));
++
++        int x = bytes.position() ;
++
++        int len = buffer.limit() - buffer.position() ;
++        if ( len > bytes.remaining() )
++            len = bytes.remaining() ;
++        // Copy out, moving the position of the bytes of stroage.
++        for (int i = 0; i < len; i++) {
++            byte b = bytes.get() ;
++            buffer.put(b) ;
++        }
++        if ( TRACKING )
++            log("read>>") ;
++        return len ;
++    }
++
++    @Override
++    synchronized public int read(ByteBuffer buffer, long loc) {
++        checkIfClosed() ;
++        if ( TRACKING )
++            log("read<<@%d", loc) ;
++        if ( loc < 0 || loc > bytes.limit() )
++            throw new StorageException("Out of range(" + name + "[read]): " + loc + " [0," + bytes.limit() + ")") ;
++        if ( loc == bytes.limit() )
++            log.warn("At the limit(" + name + "[read]): " + loc) ;
++        int x = bytes.position() ;
++        bytes.position((int)loc) ;
++        int len = read(buffer) ;
++        bytes.position(x) ;
++        if ( TRACKING )
++            log("read>>@%d",loc) ;
++        return len ;
++    }
++
++    @Override
++    synchronized public int write(ByteBuffer buffer) {
++        checkIfClosed() ;
++        if ( TRACKING )
++            log("write<<%s", ByteBufferLib.details(buffer));
++        int len = buffer.limit() - buffer.position() ;
++        int posn = bytes.position() ;
++
++        int freespace = bytes.capacity() - bytes.position() ;
++
++        if ( len > freespace ) {
++            int inc = len - freespace ;
++            inc += INC_SIZE ;
++            ByteBuffer bb2 = ByteBuffer.allocate(bytes.capacity() + inc) ;
++            bytes.position(0) ;
++            // Copy contents; make written bytes area the same as before.
++            bb2.put(bytes) ;
++            bb2.limit(bytes.limit()) ; // limit is used as the end of active
++                                       // bytes.
++            bb2.position(posn) ;
++            bytes = bb2 ;
++        }
++
++        if ( bytes.limit() < posn + len )
++            bytes.limit(posn + len) ;
++
++        bytes.put(buffer) ;
++
++        if ( TRACKING )
++            log("write>>") ;
++        return len ;
++    }
++
++    // Invert : write(ByteBuffer) = write(ByteBuffer,posn)
++    @Override
++    synchronized public int write(ByteBuffer buffer, long loc) {
++        checkIfClosed() ;
++        if ( TRACKING )
++            log("write<<@%d", loc) ;
++        if ( loc < 0 || loc > bytes.limit() )
++            // Can write at loc = bytes()
++            throw new StorageException("Out of range(" + name + "[write]): " + loc + " [0," + bytes.limit() + ")") ;
++        int x = bytes.position() ;
++        bytes.position((int)loc) ;
++        int len = write(buffer) ;
++        bytes.position(x) ;
++        if ( TRACKING )
++            log("write>>@%d", loc) ;
++        return len ;
++    }
++
++    @Override
++    synchronized public void truncate(long size) {
++        checkIfClosed() ;
++        if ( TRACKING )
++            log("truncate(%d)", size) ;
++        int x = (int)size ;
++        if ( x < 0 )
++            throw new StorageException("Out of range: " + size) ;
++        if ( x > bytes.limit() )
++            return ;
++
++        if ( bytes.position() > x )
++            bytes.position(x) ;
++        bytes.limit(x) ;
++    }
++
++    @Override
++    synchronized public long size() {
++        checkIfClosed() ;
++        return bytes.limit() ;
++    }
++
++    @Override
++    synchronized public boolean isEmpty() {
++        checkIfClosed() ;
++        return size() == 0 ;
++    }
++
++    @Override
++    synchronized public void sync() {
++        checkIfClosed() ;
++    }
++
++    @Override
++    synchronized public void close() {
++        if ( bytes == null )
++            return ;
++        bytes = null ;
++    }
++
++    private void checkIfClosed() {
++        if ( bytes == null )
++            throw new StorageException("Closed: " + name) ;
++    }
++
++    @Override
++    synchronized public String getLabel() {
++        return name ;
++    }
++
++    @Override
++    synchronized public String toString() {
++        return name ;
++    }
++
++    @Override
++    public String getFilename() {
++        return null ;
++    }
++
++    private void log(String fmt, Object... args) {
++        if ( TRACKING )
++            FmtLog.debug(log, fmt, args); 
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/ChannelManager.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/ChannelManager.java
index 0000000,0000000..7b1d041
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/ChannelManager.java
@@@ -1,0 -1,0 +1,114 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++import java.io.IOException ;
++import java.io.RandomAccessFile ;
++import java.nio.channels.FileChannel ;
++import java.util.ArrayList ;
++import java.util.HashMap ;
++import java.util.List ;
++import java.util.Map ;
++
++public class ChannelManager
++{
++    // Make per "location"?
++    
++    // Because "FileManager" is already in use
++    // ChannelManager
++    
++    // FileBase ==> OpenFileRef, ChannelRef
++
++    public static FileChannel acquire(String filename) {
++        return acquire(filename, "rw") ;
++    }
++
++    public static FileChannel acquire(String filename, String mode) {
++        return openref$(filename, mode) ;
++    }
++
++    static private Map<String, FileChannel> name2channel = new HashMap<>() ;
++    static private Map<FileChannel, String> channel2name = new HashMap<>() ;
++
++    private static FileChannel openref$(String filename, String mode) {
++        // Temp - for now, only journal files are tracked.
++        if ( !filename.endsWith(".jrnl") ) {
++            return open$(filename, mode) ;
++        }
++
++        FileChannel chan = name2channel.get(filename) ;
++        if ( chan != null ) {
++            // Scream - it's currently open.
++            throw new FileException("Already open: " + filename) ;
++        }
++        chan = open$(filename, mode) ;
++        name2channel.put(filename, chan) ;
++        channel2name.put(chan, filename) ;
++        return chan ;
++    }
++
++    private static FileChannel open$(String filename, String mode) {
++        try {
++            // "rwd" - Syncs only the file contents
++            // "rws" - Syncs the file contents and metadata
++            // "rw"  - OS write behind possible
++            // "r"   - read only
++            @SuppressWarnings("resource")
++            RandomAccessFile out = new RandomAccessFile(filename, mode) ;
++            FileChannel channel = out.getChannel() ;
++            return channel ;
++        } catch (IOException ex) { throw new FileException("Failed to open: "+filename+" (mode="+mode+")", ex) ; }
++    }
++    
++    public static void release(String filename) {
++        FileChannel channel = name2channel.get(filename) ;
++        if ( channel != null )
++            release(channel) ;
++    }
++
++    public static void release(FileChannel chan) {
++        // Always close even if not managed.
++        try {
++            chan.close() ;
++        }
++        catch (Exception ex) {}
++        String name = channel2name.remove(chan) ;
++        if ( name != null )
++            name2channel.remove(name) ;
++    }
++
++    public static void reset() {
++        releaseAll(null) ;
++    }
++    
++    /** Shutdown all the files matching the prefix (typically a directory) */
++    public static void releaseAll(String prefix) {
++        // Use an iterator explicitly so we can remove from the map.
++        List<FileChannel> x = new ArrayList<>() ;
++        for ( String fn : name2channel.keySet() ) {
++            if ( prefix == null || fn.startsWith(prefix) ) {
++                x.add(name2channel.get(fn)) ;
++                // Don't call release here - potential CME problems.
++                // Could use an explicit iterator on teh keySet and .remove from that but
++                // then we nearly duplicate the code in release.
++            }
++        }
++        for ( FileChannel chan : x )
++            release(chan) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/FileException.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/FileException.java
index 0000000,0000000..5174033
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/FileException.java
@@@ -1,0 -1,0 +1,28 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++import org.seaborne.dboe.DBOpEnvException ;
++
++public class FileException extends DBOpEnvException
++{
++    public FileException()                          { super() ; }
++    public FileException(String msg)                { super(msg) ; }
++    public FileException(Throwable th)              { super(th) ; }
++    public FileException(String msg, Throwable th)  { super(msg, th) ; }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/FileFactory.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/FileFactory.java
index 0000000,0000000..ebdc1e4
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/FileFactory.java
@@@ -1,0 -1,0 +1,48 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file ;
++
++public class FileFactory {
++    
++    public static BinaryDataFile createBinaryDataFile(FileSet fileset, String ext) {
++        String x = fileset.filename(ext) ;
++        if ( fileset.isMem() ) {
++            return new BinaryDataFileMem() ;
++        } else {
++            BinaryDataFile bdf = new BinaryDataFileRandomAccess(x) ;
++            bdf = new BinaryDataFileWriteBuffered(bdf) ;
++            return bdf ;
++        }
++    }
++
++    public static BinaryDataFile createBinaryDataFile() {
++        return new BinaryDataFileMem() ;
++    }
++
++    public static BufferChannel createBufferChannel(FileSet fileset, String ext) {
++        String x = fileset.filename(ext) ;
++        if ( fileset.isMem() )
++            return BufferChannelMem.create(x) ;
++        else
++            return BufferChannelFile.create(x) ;
++    }
++
++    public static BufferChannel createBufferChannelMem() {
++        return createBufferChannel(FileSet.mem(), null) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/FileSet.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/FileSet.java
index 0000000,0000000..f04e42d
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/FileSet.java
@@@ -1,0 -1,0 +1,115 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file ;
++
++import java.io.File ;
++
++import org.apache.jena.atlas.lib.FileOps ;
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++/**
++ * Naming metadata management to a collection of related files (same directory,
++ * same basename within directory, various extensions).
++ */
++public class FileSet {
++    // Cope with "in-memory" fileset (location == null)
++
++    private static Logger log = LoggerFactory.getLogger(FileSet.class) ;
++
++    private Location      location ;
++    private String        basename ;
++
++    /** FileSet for "in-memory" */
++    public static FileSet mem() {
++        FileSet fs = new FileSet(Location.mem(), "mem") ;
++        return fs ;
++    }
++
++    // private FileSet() {} // Uninitialized.
++
++    /**
++     * Create a FileSet given Location (directory) and name within the directory
++     */
++    public FileSet(String directory, String basename) {
++        initFileSet(Location.create(directory), basename) ;
++    }
++
++    /**
++     * Create a FileSet given Location (directory) and name within the directory
++     */
++    public FileSet(String filename) {
++        Tuple<String> t = FileOps.splitDirFile(filename) ;
++        String dir = t.get(0) ;
++        String fn = t.get(1) ;
++        if ( dir == null )
++            dir = "." ;
++        initFileSet(Location.create(dir), fn) ;
++    }
++
++    /**
++     * Create a FileSet given Location (directory) and name within the directory
++     */
++    public FileSet(Location directory, String basename) {
++        initFileSet(directory, basename) ;
++    }
++
++    private void initFileSet(Location directory, String basename) {
++        // Default - don't use the locations metadata
++        initFileSet(directory, basename, false) ;
++    }
++
++    private void initFileSet(Location directory, String basename, boolean useLocationMetadata) {
++        this.location = directory ;
++        this.basename = basename ;
++    }
++
++    public Location getLocation() {
++        return location ;
++    }
++
++    public String getBasename() {
++        return basename ;
++    }
++
++    // public MetaFile getMetaFile() { return metafile ; }
++
++    public boolean isMem() {
++        return location.isMem() ;
++    }
++
++    public boolean exists(String ext) {
++        if ( location.isMem() )
++            return true ;
++        String fn = filename(ext) ;
++        File f = new File(fn) ;
++        if ( f.isDirectory() )
++            log.warn("File clashes with a directory") ;
++        return f.exists() && f.isFile() ;
++    }
++
++    @Override
++    public String toString() {
++        return "FileSet:" + filename(null) ;
++    }
++
++    public String filename(String ext) {
++        return location.getPath(basename, ext) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/Location.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/Location.java
index 0000000,0000000..9fbba55
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/Location.java
@@@ -1,0 -1,0 +1,264 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file ;
++
++import java.io.File ;
++import java.io.IOException ;
++import java.nio.file.Path;
++import java.util.Objects ;
++
++import org.seaborne.dboe.sys.Names ;
++
++/**
++ * Wrapper for a file system directory; can create filenames in that directory.
++ * Enforces some simple consistency policies and provides a "typed string" for a
++ * filename to reduce errors.
++ */
++
++public class Location {
++    static String    pathSeparator = File.separator ; // Or just "/"
++
++    private static String memNamePath = Names.memName+pathSeparator ;
++        
++    private String   pathname ;
++    private MetaFile metafile      = null ;
++    private boolean  isMem         = false ;
++    private boolean  isMemUnique   = false ;
++
++    static int       memoryCount   = 0 ;
++
++    /**
++     * Return a fresh memory location : always unique, never .equals to another
++     * location.
++     */
++    static public Location mem() {
++        return mem(null) ;
++    }
++
++    /** Return a memory location with a name */
++    static public Location mem(String name) {
++        Location loc = new Location() ;
++        memInit(loc, name) ;
++        return loc ;
++    }
++
++    /** Return a location for a directory on disk */
++    static public Location create(String directoryName) {
++        if ( directoryName == null )
++            // Fresh, anonymous, memory area 
++            return mem() ; 
++        Location loc = new Location(directoryName) ;
++        return loc ;
++    }
++
++    /** Return a location for a directory on disk */
++    static public Location create(Path directoryName) {
++        if ( directoryName == null )
++            // Fresh, anonymous, memory area 
++            return mem() ; 
++        Location loc = new Location(directoryName.toString()) ;
++        return loc ;
++    }
++
++    private Location() {}
++
++    private static void memInit(Location location, String name) {
++        location.pathname = Names.memName ;
++        if ( name != null ) {
++            name = name.replace('\\', '/') ;
++            location.pathname = location.pathname + '/' + name ;
++        } else
++            location.isMemUnique = true ;
++        if ( !location.pathname.endsWith(pathSeparator) )
++            location.pathname = location.pathname + '/' ;
++        location.isMem = true ;
++        location.metafile = new MetaFile(Names.memName, Names.memName) ;
++    }
++
++    private Location(String rootname) {
++        super() ;
++        if ( rootname.equals(Names.memName) ) {
++            memInit(this, null) ;
++            return ;
++        }
++        if ( rootname.startsWith(memNamePath) ) {
++            String name = rootname.substring(memNamePath.length()) ;
++            memInit(this, name) ;
++            return ;
++        }
++
++        ensure(rootname) ;
++        pathname = fixupName(rootname) ;
++        // Metafilename for a directory.
++        String metafileName = getPath(Names.directoryMetafile, Names.extMeta) ;
++
++        metafile = new MetaFile("Location: " + rootname, metafileName) ;
++    }
++
++    // MS Windows:
++    // getCanonicalPath is only good enough for existing files.
++    // It leaves the case as it finds it (upper, lower) and lower cases
++    // not-existing segments. But later creation of a segment with uppercase
++    // changes the exact string returned.
++    private String fixupName(String fsName) {
++        if (  isMem() )
++            return fsName ;
++        File file = new File(fsName) ;
++        try {
++            fsName = file.getCanonicalPath() ;
++        } catch (IOException ex) {
++            throw new FileException("Failed to get canoncial path: " + file.getAbsolutePath(), ex) ;
++        }
++
++        if ( !fsName.endsWith(File.separator) && !fsName.endsWith(pathSeparator) )
++            fsName = fsName + pathSeparator ;
++        return fsName ;
++    }
++    
++    public String getDirectoryPath() {
++        return pathname ;
++    }
++
++    public MetaFile getMetaFile() {
++        return metafile ;
++    }
++
++    public boolean isMem() {
++        return isMem ;
++    }
++
++    public boolean isMemUnique() {
++        return isMemUnique ;
++    }
++    
++    public Location getSubLocation(String dirname) {
++        String newName = pathname + dirname ;
++        ensure(newName) ;
++        return Location.create(newName) ;
++    }
++
++    private void ensure(String dirname) {
++        if ( isMem() )
++            return ;
++        File file = new File(dirname) ;
++        if ( file.exists() && !file.isDirectory() )
++            throw new FileException("Existing file: " + file.getAbsolutePath()) ;
++        if ( !file.exists() )
++            file.mkdir() ;
++    }
++    
++    public String getSubDirectory(String dirname) {
++        return getSubLocation(dirname).getDirectoryPath() ;
++    }
++
++    /**
++     * Return an absolute filename where relative names are resolved from the
++     * location
++     */
++    public String absolute(String filename, String extension) {
++        return (extension == null) ? absolute(filename) : absolute(filename + "." + extension) ;
++    }
++
++    /**
++     * Return an absolute filename where relative names are resolved from the
++     * location
++     */
++    public String absolute(String filename) {
++        File f = new File(filename) ;
++        // Location relative.
++        if ( !f.isAbsolute() )
++            filename = pathname + filename ;
++        return filename ;
++    }
++
++    /** Does the location exist (and it a directory, and is accessible) */
++    public boolean exists() {
++        File f = new File(getDirectoryPath()) ;
++        return f.exists() && f.isDirectory() && f.canRead() ;
++    }
++
++    public boolean exists(String filename) {
++        return exists(filename, null) ;
++    }
++
++    public boolean exists(String filename, String ext) {
++        String fn = getPath(filename, ext) ;
++        File f = new File(fn) ;
++        return f.exists() ;
++    }
++
++    /** Return the name of the file relative to this location */
++    public String getPath(String filename) {
++        return getPath(filename, null) ;
++    }
++
++    /** Return the name of the file, and extension, relative to this location */
++    public String getPath(String filename, String ext) {
++        check(filename, ext) ;
++        if ( ext == null )
++            return pathname + filename ;
++        return pathname + filename + "." + ext ;
++    }
++
++    private void check(String filename, String ext) {
++        if ( filename == null )
++            throw new FileException("Location: null filename") ;
++        if ( filename.contains("/") || filename.contains("\\") )
++            throw new FileException("Illegal file component name: " + filename) ;
++        if ( filename.contains(".") && ext != null )
++            throw new FileException("Filename has an extension: " + filename) ;
++        if ( ext != null ) {
++            if ( ext.contains(".") )
++                throw new FileException("Extension has an extension: " + filename) ;
++        }
++    }
++    
++    @Override
++    public int hashCode() {
++        final int prime = 31 ;
++        int result = isMem ? 1 : 2 ;
++        result = prime * result + ((pathname == null) ? 0 : pathname.hashCode()) ;
++        return result ;
++    }
++
++    @Override
++    public boolean equals(Object obj) {
++        if ( this == obj )
++            return true ;
++        if ( obj == null )
++            return false ;
++        if ( getClass() != obj.getClass() )
++            return false ;
++
++        Location other = (Location)obj ;
++        if ( isMem && !other.isMem )
++            return false ;
++        if ( !isMem && other.isMem )
++            return false ;
++        // Not == so ...
++        if ( isMemUnique )
++            return false ;
++
++        return Objects.equals(pathname, other.pathname) ;
++    }
++
++    @Override
++    public String toString() {
++        return "location:" + pathname ;
++    }
++}


[31/50] [abbrv] jena git commit: JENA-1396: Merge //github/afs/mantis as subdirectory jena-db/

Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/NodeId.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/NodeId.java
index 0000000,0000000..f2139d9
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/NodeId.java
@@@ -1,0 -1,0 +1,219 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store;
++
++import static org.apache.jena.sparql.expr.Expr.CMP_EQUAL;
++import static org.seaborne.tdb2.store.NodeIdType.PTR;
++import static org.seaborne.tdb2.store.NodeIdType.*;
++
++import org.apache.jena.atlas.lib.BitsLong;
++import org.apache.jena.atlas.logging.FmtLog;
++import org.apache.jena.graph.Node;
++import org.seaborne.dboe.sys.Sys;
++import org.seaborne.tdb2.sys.SystemTDB;
++
++final
++public class NodeId implements Comparable<NodeId>
++{
++    public static final int SIZE = Sys.SizeOfLong ;
++    
++    public static final NodeId NodeIdInvalid        = makeSpecial(0xA1);
++    public static final NodeId NodeDoesNotExist     = makeSpecial(0xA3);
++    public static final NodeId NodeIdAny            = makeSpecial(0xA4);
++    public static final NodeId NodeIdDefined        = makeSpecial(0xA5);
++    public static final NodeId NodeIdUndefined      = makeSpecial(0xA6);
++    
++    private static NodeId makeSpecial(long v) {
++        // Careful of an initialzer loop (create(type, v1, v2) looks at specials).
++        return NodeIdFactory.createValue(SPECIAL, v);
++    }
++    
++    /*package*/ static final boolean enableInlineLiterals = SystemTDB.enableInlineLiterals;
++    
++    // Internal consistency checks.
++    private static final boolean CHECKING = true;
++    
++    // Code search:
++    // [64 bits]
++
++    
++    // Encoding:
++    //   8 bits type, except PTR which is 0 high bit.
++    //   24 bits int
++    //   64 bit value
++    // The high byte of value1==type as integer value
++    // In-memory:
++    // Long: 
++    //   type PTR high bit zero, then 31+64 bits of value.
++    //  Inlines:
++    //   type   is 8 bits
++    //   value1 is 24 bits
++    //   value2 is 64 bits
++    //   type is a type and is OR'ed into value1 to store.
++    // Short: 64 bit:
++    //   type PTR high bit zero, then 63 bits of value.
++    //  Inlines:
++    //   value1 is not used.
++    //   value2 is 56 bits
++    // type is a type and is OR'ed into value2 to store.
++    
++    final NodeIdType type;
++    final int  value1;
++    final long value2;
++    
++    private NodeId(NodeIdType type, int v1, long v2) {
++        if ( CHECKING ) check(type, v1, v2);
++        this.type = type;
++        value1 = v1;    // Zero for 64 bit.
++        value2 = v2;
++    }
++
++    private final void check(NodeIdType type, int v1, long v2) {
++        if ( type == PTR )
++            return;
++        if ( type == SPECIAL )
++            return;
++        // 64 bit NodeId.
++        int x = ( type == XSD_DOUBLE )
++            ? (int)BitsLong.unpack(v2, 62, 64)   // High two bits
++            : (int)BitsLong.unpack(v2, 56, 64);  // else 
++        if ( x != 0 )
++            FmtLog.warn(getClass(), "Type set in long: type=%s value=%016X", type, v2);
++    }
++
++    public boolean isPtr() { return type == PTR; }
++    
++//    public long getPtrLocation() { return value2; }
++//    public long getPtrLo() { return value2; }
++//    public int  getPtrHi() { return value1 & 0x00FFFFFF; }
++
++    // 64 bit
++    public long getPtrLocation()    { return value2; }
++    // Long.
++//    public long getPtrLo()          { return value2; }
++//    public int  getPtrHi()          { return value1; }
++    
++    public int getTypeValue()       { return type.type(); }
++    
++    public boolean isInline() {
++        return isInline(this);
++    }
++    
++    public static boolean isInline(NodeId nodeId) {
++        return NodeIdType.isInline(nodeId.type);
++    }
++        
++    public boolean isValue() {
++        return type != PTR && NodeIdType.isStorable(type); 
++    }
++
++    // Migration
++    public static NodeId inline(Node node) { return NodeIdInline.inline(node); }
++    public static boolean hasInlineDatatype(Node node) { return NodeIdInline.hasInlineDatatype(node); }
++    public static Node extract(NodeId nodeId) { return NodeIdInline.extract(nodeId); }
++    
++    // XXX Later.
++//    //Static forms only?
++//    public boolean isAny()          { return isAny(this); }
++//    public boolean isDoesNotExist() { return isDoesNotExist(this); }
++//    public boolean isDefined()      { return isDefined(this); }
++//    public boolean isUndefined()    { return isDefined(this); }
++//
++    public static final boolean isAny(NodeId nodeId)           { return nodeId == NodeIdAny || nodeId == null; }
++    public static final boolean isDoesNotExist(NodeId nodeId)  { return nodeId == NodeDoesNotExist; }
++//    public static boolean isDefined(NodeId nodeId)             { return nodeId == NodeIdDefined; }
++//    public static boolean isUndefined(NodeId nodeId)           { return nodeId == NodeIdUndefined; }
++    
++    /** Create from a long-encoded value */
++    /*package*/ static NodeId createRaw(NodeIdType type, long value) {
++        return new NodeId(type, 0, value);
++    }
++
++    /** Create from a (int,long)-encoded value */
++    /*package*/ static NodeId createRaw(NodeIdType type, int value1, long value2) {
++        return new NodeId(type, value1, value2);
++    }
++
++    public NodeIdType type() { return type; } 
++
++    /*package*/ int  getValue1() { return value1; }
++    /*package*/ long getValue2() { return value2; }
++    
++    @Override
++    public int hashCode() {
++        // Ensure all parts have an effect on the 32 bit hash value.
++        return value1 ^ ((int)value2) ^ ((int)(value2 >> 32));
++    }
++
++    @Override
++    public boolean equals(Object other) {
++        if ( this == other ) return true;
++        if ( other == null ) return false;
++        if ( !(other instanceof NodeId) ) return false;
++        NodeId nOther = ((NodeId)other);
++        return equals(nOther, value1, value2);  
++    }
++    
++    public boolean equals(NodeId nodeIdOther) {
++        if ( nodeIdOther == null ) return false;
++        if ( this == nodeIdOther ) return true;
++        return equals(nodeIdOther, value1, value2);  
++    }
++    
++    /*package*/ static boolean equals(NodeId nodeId, int v1, long v2) {
++        return v2 == nodeId.value2 && v1 == nodeId.value1;  
++    }
++    @Override
++    public String toString() { 
++        if ( this == NodeDoesNotExist ) return "[DoesNotExist]";
++        if ( this == NodeIdAny ) return "[Any]";
++        if ( this == NodeIdInvalid ) return "[Invalid]";
++        if ( this == NodeIdDefined ) return "[Defined]";
++        if ( this == NodeIdUndefined ) return "[Undefined]";
++        
++        if ( this.isInline() ) {
++            String displayName = this.type().toString();
++            return String.format("[%s 0x%014X]", displayName, value2);
++        }
++        // [64 bits]
++        //return String.format("[%08X-%016X]", value1, value2);
++        return String.format("[0x%16X]", value2);
++    }
++    
++    // ---- Encoding special - inlines.
++
++   /** Compare - provides an ordering of {@code NodeIds}. */ 
++    @Override
++    public int compareTo(NodeId other) {
++        return compare(this, other);
++    }
++    
++    /** Compare - provides an ordering of {@code NodeIds}. */ 
++    public static int compare(NodeId n1, NodeId n2) {
++        int x = Integer.compare(n1.value1, n2.value1);
++        if ( x == 0 )
++            return CMP_EQUAL;
++        return Long.compare(n1.value2, n2.value2);
++    }
++    
++    public final boolean isConcrete() { return isConcrete(this); }
++    
++    public static final boolean isConcrete(NodeId nodeId) { 
++        return ! NodeIdType.isSpecial(nodeId.type);
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/NodeIdFactory.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/NodeIdFactory.java
index 0000000,0000000..8bddb88
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/NodeIdFactory.java
@@@ -1,0 -1,0 +1,260 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store;
++
++import static org.seaborne.tdb2.store.NodeIdType.PTR;
++import static org.seaborne.tdb2.store.NodeIdType.isSpecial;
++
++import java.nio.ByteBuffer;
++import java.util.concurrent.atomic.AtomicInteger;
++
++import org.apache.jena.atlas.lib.BitsInt;
++import org.apache.jena.atlas.lib.BitsLong;
++import org.apache.jena.atlas.lib.Bytes;
++import org.seaborne.tdb2.TDBException;
++import org.seaborne.tdb2.store.value.DoubleNode62;
++
++/** Factory for NodeIds, including to/from disk forms via ByteBuffers and byte[].*/
++final
++public class NodeIdFactory
++{
++    // On-disk: may be shorter (controlled by get/set ByteBuffer and byte[]).
++    // In-memory - always int-long
++    
++    // XXX Chance for a cache?
++
++    private static NodeId create(NodeIdType type, int v1, long v2) {
++        if ( isSpecial(type) ) {
++            if ( NodeId.equals(NodeId.NodeDoesNotExist, v1, v2) )
++                return NodeId.NodeDoesNotExist;
++            if ( NodeId.equals(NodeId.NodeIdAny, v1, v2) )
++                return NodeId.NodeIdAny;
++            if ( NodeId.equals(NodeId.NodeIdDefined, v1, v2) )
++                return NodeId.NodeIdDefined;
++            if ( NodeId.equals(NodeId.NodeIdDefined, v1, v2) )
++                return NodeId.NodeIdDefined;
++            if ( NodeId.equals(NodeId.NodeIdUndefined, v1, v2) )
++                return NodeId.NodeIdUndefined;
++            //throw new IllegalArgumentException("Special not recognized");
++        }
++        return createNew(type, v1, v2);
++    }
++    
++    public static NodeId createValue(NodeIdType type, long value) {
++        // 64 bit.
++        return createNew(type, 0, value);
++    }
++    
++    private static NodeId createNew(NodeIdType type, int v1, long v2) {
++        return NodeId.createRaw(type, v1, v2);
++    }
++
++    public static NodeId createPtr(long lo) {
++        return createNew(PTR, 0, lo);
++    }
++
++    /*package*/ /*long*/ static NodeId createPtrLong(int hi, long lo) {
++        return create(PTR, hi, lo);
++    }
++    
++    // ---- Create from binary.
++    
++    // 64 bit create
++    private static NodeId create(long value2) {
++        if ( !BitsLong.isSet(value2, 63) )
++            return createPtr(value2);
++        // Inline.
++        long v2 = value2;
++        if ( BitsLong.isSet(v2, 62) ) {
++            // XSD_DOUBLE
++            v2 = DoubleNode62.removeType(v2); 
++            return NodeId.createRaw(NodeIdType.XSD_DOUBLE, v2);
++        }
++        int t = (int)BitsLong.unpack(v2, 56, 63);   // 7 bits
++        v2 = BitsLong.clear(v2, 56, 64);
++        NodeIdType type = NodeIdType.intToEnum(t);
++        if ( type == NodeIdType.SPECIAL )
++            throw new TDBException(String.format("Attempt to create a special from a long: 0x%016", v2));
++        return NodeId.createRaw(type, v2);
++    }
++    
++    // Long create.
++    private static NodeId create(int v1, long v2) {
++        if ( !BitsInt.isSet(v1, 32) )
++            return createPtrLong(v1, v2);
++        int t = v1 >> 24;
++        NodeIdType type = NodeIdType.intToEnum(t);
++        if ( type == NodeIdType.SPECIAL )
++            throw new TDBException(String.format("Attempt to create a special from a long: 0x%016", v2));
++        return createNew(type, 0, v2);
++    }
++
++    // ------- On-disk forms.
++    // 
++    
++    /** Not relative - get from position zero */
++    public static NodeId get(byte[] b) { return get(b,0); } 
++
++    // **** 64 bit NodeId ****
++    /** Relative {@code ByteBuffer} {@code get} */
++    public static NodeId get(ByteBuffer b)   { 
++        long value2 = b.getLong();
++        return decode(value2);
++    }
++
++    public static NodeId get(byte[] b, int idx) {
++        long value2 = Bytes.getLong(b, idx);
++        return decode(value2);
++    }
++
++    public static NodeId get(ByteBuffer b, int idx) {
++        long value2 = b.getLong(idx);
++        return decode(value2);
++    }
++
++    // 64 bit version
++    private static NodeId decode(long value2) {
++        return NodeIdFactory.create(value2);
++    }
++
++    /** Not relative - set at position zero */
++    public static void set(NodeId nodeId, byte[] b) {
++        long v2 = encode(nodeId);
++        Bytes.setLong(v2, b, 0);
++    }
++
++    private static long encode(NodeId nodeId) {
++        long x = nodeId.value2;
++        switch(nodeId.type()) {
++            case PTR:
++                return x;
++            case XSD_DOUBLE:
++                // XSD_DOUBLE is special.
++                // Set value bit (63) and bit 62 
++                x = DoubleNode62.insertType(x);
++                return x ;
++            default:
++                // Bit 62 is zero - tagt is for doubles.
++                x = BitsLong.pack(x, nodeId.getTypeValue(), 56, 62);
++                // Set the high, value bit.
++                x = BitsLong.set(x, 63);
++                return x;
++        }
++    }
++
++    /** Relative {@code set} */
++    public static void set(NodeId nodeId, ByteBuffer b) {
++        long v2 = encode(nodeId);
++        b.putLong(v2);
++    }
++
++    public static void set(NodeId nodeId, byte[] b, int idx) {
++        long v2 = encode(nodeId);
++        Bytes.setLong(v2, b, idx);
++    }
++
++    public static void set(NodeId nodeId, ByteBuffer b, int idx) {
++        long v2 = encode(nodeId);
++        b.putLong(idx, v2);
++    }
++    
++    public static void setNext(NodeId nodeId, byte[] b, int idx) {
++        long v2 = encode(nodeId);
++        Bytes.setLong(v2+1, b, idx);
++    }
++
++    // (int,long) versions : check before use
++//    /** Relative {@code ByteBuffer} {@code get} */
++//    public static NodeId get(ByteBuffer b)   {
++//        int value1 = b.getInt();
++//        long value2 = b.getLong();
++//        return decode(value1,value2);
++//    }
++//    
++//    public static NodeId get(byte[] b, int idx) {
++//        int value1 = Bytes.getInt(b, idx);
++//        long value2 = Bytes.getLong(b, idx+SystemTDB.SizeOfInt);
++//        return decode(value1, value2);
++//    }
++//
++//    public static NodeId get(ByteBuffer b, int idx) {
++//        int value1 = b.getInt(idx);
++//        long value2 = b.getLong(idx+SystemTDB.SizeOfInt);
++//        return decode(value1,value2);
++//    }
++//    
++//    private static NodeId decode(int value1, long value2) {
++//        return NodeIdFactory.create(value1, value2);
++//    }
++//    
++//    /** Not relative - set at position zero */
++//    public static void set(NodeId nodeId, byte[] b) {
++//        int v1 = encode(nodeId.value1);    
++//        Bytes.setInt(v1, b, 0);
++//        Bytes.setLong(nodeId.value2, b, SystemTDB.SizeOfInt);
++//    }
++//
++//    /** Relative {@code set} */
++//    public static void set(NodeId nodeId, ByteBuffer b) {
++//        int v1 = encode(nodeId.value1);    
++//        b.putInt(v1);
++//        b.putLong(nodeId.value2);
++//    }
++//
++//    public static void set(NodeId nodeId, byte[] b, int idx) {
++//        int v1 = encode(nodeId.value1);    
++//        Bytes.setInt(v1, b, idx);
++//        Bytes.setLong(nodeId.value2, b, idx+SystemTDB.SizeOfInt);
++//    }
++//
++//    public static void set(NodeId nodeId, ByteBuffer b, int idx) {
++//        int v1 = encode(nodeId.value1);    
++//        b.putInt(idx, v1);
++//        b.putLong(idx+SystemTDB.SizeOfInt, nodeId.value2);
++//    }
++//    
++//    public static void setNext(NodeId nodeId, byte[] b, int idx) {
++//        int v1 = encode(nodeId.value1);
++//        long v2 = nodeId.value2;
++//        // Unsigned 96 bit add! 
++//        if ( v2 < 0 ) {
++//            if ( v2 == Long.MIN_VALUE )
++//                v1++ ;
++//            else
++//                v2--;
++//        } else
++//            v2++ ;
++//        Bytes.setInt(v1, b, idx);
++//        Bytes.setLong(v2, b, idx+Integer.SIZE);
++//    }
++//    private int encode(NodeId nodeId) {
++//        long x = nodeId.value1;
++//        if ( nodeId.isPtr() )
++//            return x;
++//        x = BitsInt.pack(x, nodeId.getTypeValue(), 24, 32);
++//        return x; 
++//    }
++
++
++
++
++    private static AtomicInteger counter = new AtomicInteger(0xB0);
++    public static NodeId genUnique() {
++        return NodeIdFactory.create(NodeIdType.SPECIAL, counter.incrementAndGet(), 0);
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/NodeIdInline.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/NodeIdInline.java
index 0000000,0000000..4a7858c
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/NodeIdInline.java
@@@ -1,0 -1,0 +1,328 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store;
++
++import static org.seaborne.tdb2.store.NodeIdType.PTR;
++import static org.seaborne.tdb2.store.NodeIdType.*;
++import static org.seaborne.tdb2.store.NodeIdType.XSD_DATE;
++import static org.seaborne.tdb2.store.NodeIdType.XSD_DATETIME;
++import static org.seaborne.tdb2.store.NodeIdType.XSD_DECIMAL;
++import static org.seaborne.tdb2.store.NodeIdType.XSD_FLOAT;
++
++import java.math.BigDecimal;
++import java.util.HashSet;
++import java.util.Set;
++
++import org.apache.jena.atlas.logging.Log;
++import org.apache.jena.datatypes.RDFDatatype;
++import org.apache.jena.datatypes.xsd.XSDDatatype;
++import org.apache.jena.ext.com.google.common.collect.BiMap;
++import org.apache.jena.ext.com.google.common.collect.EnumHashBiMap;
++import org.apache.jena.graph.Node;
++import org.apache.jena.graph.NodeFactory;
++import org.apache.jena.graph.impl.LiteralLabel;
++import org.apache.jena.sparql.graph.NodeConst;
++import org.apache.jena.sparql.util.NodeUtils;
++import org.apache.jena.sparql.util.Utils;
++import org.seaborne.tdb2.TDBException;
++import org.seaborne.tdb2.store.value.*;
++
++/** Encoding values in a {@link NodeId}.
++ *  
++ * TDB2 encoding:
++ * <ul>
++ *  <li>High bit (bit 63) 0 means the node is in the object table (PTR).
++ *  <li>High bit (bit 63) 1, bit 62 1: double as 62 bits. See {@link DoubleNode62}.
++ *  <li>High bit (bit 63) 1, bit 62 0: 6 bits of type, 56 bits of value.
++ * </ul> 
++ *  
++ * If a value would not fit, it will be stored externally so there is no
++ * guarantee that all integers, say, are store inline. 
++ *  
++ * <ul>
++ * <li>Integer format: signed 56 bit number, the type filed has the XSD type.
++ * <li>Decimal format: 8 bits scale, 48bits of signed valued. See {@link DecimalNode56}.
++ * <li>Date and DateTime
++ * <li>Boolean
++ * <li>Float
++ * </ul>
++ *  
++ * @see IntegerNode
++ * @see DecimalNode56
++ * @see DoubleNode62
++ * @see DateTimeNode DateTimeNode for xsd:date and xsd:DateTime  
++ * @see FloatNode
++ */
++public class NodeIdInline {
++    /** Datatypes that are candidates for inlining */ 
++    private static Set<RDFDatatype> datatypes = new HashSet<>();
++    static { 
++        datatypes.add(XSDDatatype.XSDdecimal);
++        datatypes.add(XSDDatatype.XSDinteger);
++        datatypes.add(XSDDatatype.XSDdouble);
++        datatypes.add(XSDDatatype.XSDfloat);
++
++        datatypes.add(XSDDatatype.XSDlong);
++        datatypes.add(XSDDatatype.XSDint);
++        datatypes.add(XSDDatatype.XSDshort);
++        datatypes.add(XSDDatatype.XSDbyte);
++        
++        datatypes.add(XSDDatatype.XSDpositiveInteger);
++        datatypes.add(XSDDatatype.XSDnonPositiveInteger);
++        datatypes.add(XSDDatatype.XSDnegativeInteger);
++        datatypes.add(XSDDatatype.XSDnonNegativeInteger);
++
++        datatypes.add(XSDDatatype.XSDunsignedLong);
++        datatypes.add(XSDDatatype.XSDunsignedInt);
++        datatypes.add(XSDDatatype.XSDunsignedShort);
++        datatypes.add(XSDDatatype.XSDunsignedByte);
++        datatypes.add(XSDDatatype.XSDdateTimeStamp);
++        datatypes.add(XSDDatatype.XSDdateTime);
++        datatypes.add(XSDDatatype.XSDdate);
++        datatypes.add(XSDDatatype.XSDboolean);
++    };
++    
++    // Integer derived types. 
++    private static BiMap<NodeIdType, RDFDatatype> derivedTypeMap = EnumHashBiMap.create(NodeIdType.class);
++    static {
++        derivedTypeMap.put(NodeIdType.XSD_LONG, XSDDatatype.XSDlong);
++        derivedTypeMap.put(NodeIdType.XSD_INT, XSDDatatype.XSDint);
++        derivedTypeMap.put(NodeIdType.XSD_SHORT, XSDDatatype.XSDshort);
++        derivedTypeMap.put(NodeIdType.XSD_BYTE, XSDDatatype.XSDbyte);
++        
++        derivedTypeMap.put(NodeIdType.XSD_POSITIVE_INTEGER, XSDDatatype.XSDpositiveInteger);
++        derivedTypeMap.put(NodeIdType.XSD_NON_POSITIVE_INTEGER, XSDDatatype.XSDnonPositiveInteger);
++        derivedTypeMap.put(NodeIdType.XSD_NEGATIVE_INTEGER, XSDDatatype.XSDnegativeInteger);
++        derivedTypeMap.put(NodeIdType.XSD_NON_NEGATIVE_INTEGER, XSDDatatype.XSDnonNegativeInteger);
++        
++        derivedTypeMap.put(NodeIdType.XSD_UNSIGNEDLONG, XSDDatatype.XSDunsignedLong);
++        derivedTypeMap.put(NodeIdType.XSD_UNSIGNEDINT, XSDDatatype.XSDunsignedInt);
++        derivedTypeMap.put(NodeIdType.XSD_UNSIGNEDSHORT, XSDDatatype.XSDunsignedShort);     
++        derivedTypeMap.put(NodeIdType.XSD_UNSIGNEDBYTE, XSDDatatype.XSDunsignedByte);
++    }
++    
++    /** Encode a node as an inline literal.  Return null if it can't be done */
++    public static NodeId inline(Node node) {
++        if ( node == null ) {
++            Log.warn(NodeId.class, "Null node: " + node);
++            return null;
++        }
++    
++        if ( !NodeId.enableInlineLiterals )
++            return null;
++    
++        if ( !node.isLiteral() )
++            return null;
++    
++        if ( NodeUtils.isSimpleString(node) || NodeUtils.isLangString(node) )
++            return null;
++        
++        try { return inline$(node); }
++        catch (Throwable th) {
++            Log.warn(NodeId.class, "Failed to process "+node);
++            return null; 
++        }
++    }
++
++    /** Return true if this node has a datatype that look like it is inlineable.
++     * The node may still be out of range (e.g. very large integer).
++     * Only inline(Node)->NodeId can determine that. 
++     */
++    public static boolean hasInlineDatatype(Node node) {
++        if ( ! node.isLiteral() )
++            return false;
++        RDFDatatype dtn = node.getLiteralDatatype();
++        return datatypes.contains(dtn);
++    }
++
++    private static NodeId inline$(Node node) {
++        if ( ! hasInlineDatatype(node) )
++            return null;
++        LiteralLabel lit = node.getLiteral();
++        // Decimal is a valid supertype of integer but we handle integers and decimals
++        // differently.
++    
++        if ( node.getLiteralDatatype().equals(XSDDatatype.XSDdecimal) ) {
++            // Check lexical form.
++            if ( !XSDDatatype.XSDdecimal.isValidLiteral(lit) )
++                return null;
++            
++            // Not lit.getValue() because that may be a narrower type e.g. Integer.
++            // .trim is how Jena does it but it rather savage. spc, \n \r \t.
++            // But at this point we know it's a valid literal so the excessive
++            // chopping by .trim is safe.
++            BigDecimal decimal = new BigDecimal(lit.getLexicalForm().trim());
++            
++            // Does range checking.
++            DecimalNode56 dn = DecimalNode56.valueOf(decimal);
++            // null is "does not fit"
++            if ( dn != null )
++                // setType
++                return NodeId.createRaw(XSD_DECIMAL, dn.pack());
++            else
++                return null;
++        } else { 
++            // Not decimal.
++            if ( XSDDatatype.XSDinteger.isValidLiteral(lit) ) {
++                // Check length of lexical form to see if it's in range of a long.
++                // Long.MAX_VALUE =  9223372036854775807
++                // Long.MIN_VALUE = -9223372036854775808
++                // 9,223,372,036,854,775,807 is 19 digits.
++                
++                // Quick check.
++                if ( lit.getLexicalForm().length() > 19 )
++                    return null;
++    
++                // Derived types.
++                NodeIdType type = derivedTypeMap.inverse().getOrDefault(lit.getDatatype(), NodeIdType.XSD_INTEGER);
++                
++                try {
++                    long v = ((Number)lit.getValue()).longValue();
++                    v = IntegerNode.pack56(v);
++                    // Value -1 is "does not fit"
++                    if ( v == -1 )
++                        return null;
++                    return NodeId.createRaw(type, v);
++                }
++                // Out of range for the type, not a long etc etc.
++                catch (Throwable ex) { return null; }
++            }
++        }
++        
++        if ( XSDDatatype.XSDdouble.isValidLiteral(lit) ) {
++            double d =  ((Number)lit.getValue()).doubleValue();
++            long v = DoubleNode62.pack(d);
++            if ( v == DoubleNode62.NO_ENCODING )
++                return null ;
++            // The special encoding of XSD_DOUBLE is handling in the "toBytes" and "toByteBuffer" operations.  
++            return NodeId.createRaw(XSD_DOUBLE, v);
++        }
++        
++        if ( XSDDatatype.XSDfloat.isValidLiteral(lit) ) {
++            float f =  ((Number)lit.getValue()).floatValue();
++            long v = FloatNode.pack(f);
++            return NodeId.createRaw(XSD_FLOAT, v);
++        }
++        
++        if ( lit.getDatatype().equals(XSDDatatype.XSDdateTimeStamp) && XSDDatatype.XSDdateTimeStamp.isValidLiteral(lit) ) {
++            long v = DateTimeNode.packDateTime(lit.getLexicalForm());
++            if ( v == -1 )
++                return null; 
++            return NodeId.createRaw(XSD_DATETIMESTAMP, v);
++        }
++        if ( XSDDatatype.XSDdateTime.isValidLiteral(lit) ) {
++            // Could use the Jena/XSDDateTime object here rather than reparse the lexical form.
++            // But this works and it's close to a release ... 
++            long v = DateTimeNode.packDateTime(lit.getLexicalForm());
++            if ( v == -1 )
++                return null; 
++            return NodeId.createRaw(XSD_DATETIME, v);
++        }
++        
++        if ( XSDDatatype.XSDdate.isValidLiteral(lit) ) {
++            long v = DateTimeNode.packDate(lit.getLexicalForm());
++            if ( v == -1 )
++                return null;
++            return NodeId.createRaw(XSD_DATE, v);
++        }
++    
++        if ( XSDDatatype.XSDboolean.isValidLiteral(lit) ) {
++            long v = 0;
++            boolean b = (Boolean)lit.getValue();
++            // return new NodeValueBoolean(b, node);
++            if ( b )
++                v = v | 0x01;
++            return NodeId.createRaw(XSD_BOOLEAN, v);
++        }
++        
++        return null;
++    }
++
++    /** Decode an inline nodeID, return null if not an inline node */
++    public static Node extract(NodeId nodeId) {
++        if ( nodeId == NodeId.NodeDoesNotExist )
++            return null;
++        
++        NodeIdType type = nodeId.type();
++        if ( type == PTR || type == NodeIdType.SPECIAL)
++            return null;
++        switch (type) {
++//            case PTR:       return null;
++//            case SPECIAL:   return null;
++            case XSD_INTEGER :
++                // Derived from integer
++            case XSD_POSITIVE_INTEGER:
++            case XSD_NEGATIVE_INTEGER:
++            case XSD_NON_NEGATIVE_INTEGER:
++            case XSD_NON_POSITIVE_INTEGER:
++            case XSD_LONG:
++            case XSD_INT:
++            case XSD_SHORT:
++            case XSD_BYTE:
++            case XSD_UNSIGNEDLONG:
++            case XSD_UNSIGNEDINT:
++            case XSD_UNSIGNEDSHORT:
++            case XSD_UNSIGNEDBYTE:
++            {
++                long val = IntegerNode.unpack56(nodeId.value2);
++                RDFDatatype dt = derivedTypeMap.getOrDefault(type, XSDDatatype.XSDinteger);
++                Node n = NodeFactory.createLiteral(Long.toString(val), dt);
++                return n;
++            }
++            case XSD_DECIMAL : {
++                BigDecimal d = DecimalNode56.unpackAsBigDecimal(nodeId.value2);
++                String x = d.toPlainString();
++                return NodeFactory.createLiteral(x, XSDDatatype.XSDdecimal);
++            }
++            case XSD_DOUBLE: {
++                double d = DoubleNode62.unpack(nodeId.value2);
++                String xsdStr = Utils.stringForm(d);
++                Node n = NodeFactory.createLiteral(xsdStr, XSDDatatype.XSDdouble);
++                return n ;
++            }
++            case XSD_FLOAT: {
++                float f = FloatNode.unpack(nodeId.value2);
++                String xsdStr = Utils.stringForm(f);
++                Node n = NodeFactory.createLiteral(xsdStr, XSDDatatype.XSDfloat);
++                return n ;
++            }
++            case XSD_DATETIMESTAMP:
++            case XSD_DATETIME: {
++                RDFDatatype dt = (type==XSD_DATETIMESTAMP) ? XSDDatatype.XSDdateTimeStamp : XSDDatatype.XSDdateTime; 
++                long val = nodeId.getValue2();
++                String lex = DateTimeNode.unpackDateTime(val);
++                return NodeFactory.createLiteral(lex, dt);
++            }
++            case XSD_DATE : {
++                long val = nodeId.getValue2();
++                String lex = DateTimeNode.unpackDate(val);
++                return NodeFactory.createLiteral(lex, XSDDatatype.XSDdate);
++            }
++            case XSD_BOOLEAN : {
++                long val = nodeId.getValue2();
++                if ( val == 0 )
++                    return NodeConst.nodeFalse;
++                if ( val == 1 )
++                    return NodeConst.nodeTrue;
++                throw new TDBException("Unrecognized boolean node id : " + val);
++            }
++            default :
++                throw new TDBException("Unrecognized node id type: " + type);
++        }
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/NodeIdType.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/NodeIdType.java
index 0000000,0000000..02691ee
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/NodeIdType.java
@@@ -1,0 -1,0 +1,259 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store;
++
++import static org.seaborne.tdb2.store.NodeIdType.TYPES.*;
++
++/** Constants for NodeIds.
++ * Note that "PTR" is special - it uses the high bit only set to zero.
++ * Note that "XSD_DOUBLE" is special - it sets the high bit (value/ptr)
++ * and the next bit only, leaving 62 bits of value.
++ * Otherwise a type is encoded as the high byte.
++ * 
++ *  @see NodeIdInline
++ */
++public enum NodeIdType {
++    PTR(T_PTR, null),
++
++    XSD_INTEGER(T_INTEGER, "Integer"),
++    XSD_DECIMAL(T_DECIMAL, "Decimal"),
++    XSD_FLOAT(T_FLOAT, "Float"),
++    XSD_DOUBLE(T_DOUBLE, "Double"),
++    XSD_DATETIME(T_DATETIME, "DateTime"),
++    XSD_DATETIMESTAMP(T_DATETIMESTAMP, "DateTimeStamp"),
++    XSD_DATE(T_DATE, "Date"),
++    XSD_BOOLEAN(T_BOOLEAN, "Boolean"),
++    XSD_SHORTSTRING(T_SHORTSTRING, "ShortString"),
++    // 1 and greater
++    XSD_POSITIVE_INTEGER(T_POSITIVE_INTEGER, "Positive Integer"),
++    // -1 and down
++    XSD_NEGATIVE_INTEGER(T_NEGATIVE_INTEGER, "Negative Integer"),
++    // postive and zero
++    XSD_NON_NEGATIVE_INTEGER(T_NON_NEGATIVE_INTEGER, "Non-negative Integer"),
++    // negative or zero
++    XSD_NON_POSITIVE_INTEGER(T_NON_POSITIVE_INTEGER, "Non-positive Integer"),
++    XSD_LONG(T_LONG, "Long"),
++    XSD_INT(T_INT, "Int"),
++    XSD_SHORT(T_SHORT, "Short"),
++    XSD_BYTE(T_BYTE, "Byte"),
++    XSD_UNSIGNEDLONG(T_UNSIGNEDLONG, "UnsignedLong"),
++    XSD_UNSIGNEDINT(T_UNSIGNEDINT, "UnsignedInt"),
++    XSD_UNSIGNEDSHORT(T_UNSIGNEDSHORT, "UnsignedShort"),
++    XSD_UNSIGNEDBYTE(T_UNSIGNEDBYTE, "UnsignedByte"),
++    
++    // Never stored.
++    SPECIAL(T_SPECIAL, "Special"),
++    // Used here only.
++    INVALID$(T_INVALID, "Invalid")
++    
++    //, EXTENSION(T_EXTENSION, "Extension")
++    ;
++    /** The type values - must be stable as many of these go on disk.
++     * enum ordinals are not enough.
++     *
++     * Encode as:
++     * <ul>
++     * <li>PTR : high bit zero, everything else written with a high bit one (done in {@link NodeIdFactory#encode}
++     * <li>T_DOUBLE : Special case: next bit one.  01?? ???? i.e. 11?? on disk (value bit, double bit).
++     *    This leaves 62 bits for encoding doubles in the future.
++     * <li>Otherwise, a number in the low byte of the constant, high bits "10".
++     * </ul>
++     * The {@code T_*} constants do not include the high bit.
++     */
++    public static class TYPES {
++        public static final int T_PTR = 0 ;
++
++        public static final int T_DOUBLE = enc(0x40);
++
++        // Value types : 1 to 100  
++        public static final int T_INTEGER = enc(1);
++        public static final int T_DECIMAL = enc(2);
++        public static final int T_FLOAT = enc(3);
++        //private static final int T_DOUBLE_X = enc(4);
++        
++        public static final int T_DATETIME = enc(5);
++        public static final int T_DATETIMESTAMP = enc(6);
++        public static final int T_DATE = enc(7);
++        public static final int T_BOOLEAN = enc(8);
++        public static final int T_SHORTSTRING = enc(9);
++        
++        public static final int T_POSITIVE_INTEGER = enc(10);
++        public static final int T_NEGATIVE_INTEGER = enc(11);
++        public static final int T_NON_NEGATIVE_INTEGER = enc(12);
++        public static final int T_NON_POSITIVE_INTEGER = enc(13);
++        public static final int T_LONG = enc(14);
++        public static final int T_INT = enc(15);
++        public static final int T_SHORT = enc(16);
++        public static final int T_BYTE = enc(17);
++        public static final int T_UNSIGNEDLONG = enc(18);
++        public static final int T_UNSIGNEDINT = enc(19);
++        public static final int T_UNSIGNEDSHORT = enc(20);
++        public static final int T_UNSIGNEDBYTE = enc(21); 
++        // 21 is 00010101
++        
++        // Never stored : bits 1011 0000 so as not to look like a double.
++        public static final int T_SPECIAL = enc(0x30);
++        public static final int T_INVALID = enc(0x31);
++        public static final int T_EXTENSION = enc(0x3F);
++        
++        // Encode/decode of the type value.
++        static int enc(int v) { return v; }
++        static int dec(int v) { return v; }
++    }
++    
++    // We provide lots of natural questions to ask of an NodeId type.
++    // All are efficient. There is redundancy but limited to this file. 
++
++    static boolean isStorable(NodeIdType type) {
++        return !isSpecial(type); 
++    }
++    
++    // For numbers, an out-of-range number maybe stored a PTR.
++    
++    static boolean isInteger(NodeIdType type) {
++        switch(type) {
++            case XSD_INTEGER:
++            case XSD_POSITIVE_INTEGER:
++            case XSD_NEGATIVE_INTEGER:
++            case XSD_NON_NEGATIVE_INTEGER:
++            case XSD_NON_POSITIVE_INTEGER:
++            case XSD_LONG:
++            case XSD_INT:
++            case XSD_SHORT:
++            case XSD_BYTE:
++            case XSD_UNSIGNEDLONG:
++            case XSD_UNSIGNEDINT:
++            case XSD_UNSIGNEDSHORT:
++            case XSD_UNSIGNEDBYTE:
++                return true;
++            default:
++                return false;
++        }
++    }
++    
++    static boolean isDecimal(NodeIdType type) {
++        return type == XSD_DECIMAL;
++    }
++    
++    static boolean isDouble(NodeIdType type) {
++        return type == XSD_DOUBLE;
++    }
++    
++    static boolean isFloat(NodeIdType type) {
++        return type == XSD_FLOAT;
++    }
++
++    static boolean isNumber(NodeIdType type) {
++        return isInteger(type) || isDecimal(type) || isDouble(type) || isFloat(type);
++    }
++    
++    static boolean isSpecial(NodeIdType type) {
++        return type == SPECIAL;
++    }
++
++    static boolean isInline(NodeIdType type) {
++        switch(type) {
++            case XSD_INTEGER:
++            case XSD_DECIMAL:
++            case XSD_DOUBLE:
++            case XSD_FLOAT:
++                
++            case XSD_DATETIME:
++            case XSD_DATETIMESTAMP:
++            case XSD_DATE:
++            case XSD_BOOLEAN:
++            case XSD_SHORTSTRING:
++                
++            case XSD_POSITIVE_INTEGER:
++            case XSD_NEGATIVE_INTEGER:
++            case XSD_NON_NEGATIVE_INTEGER:
++            case XSD_NON_POSITIVE_INTEGER:
++            case XSD_LONG:
++            case XSD_INT:
++            case XSD_SHORT:
++            case XSD_BYTE:
++            case XSD_UNSIGNEDLONG:
++            case XSD_UNSIGNEDINT:
++            case XSD_UNSIGNEDSHORT:
++            case XSD_UNSIGNEDBYTE:
++                return true;
++            default:
++                return false;
++        }
++    }
++
++    private final int value;
++    private final String displayName;
++    
++    public int type() { return TYPES.dec(value); } 
++
++    @Override
++    public String toString() { return displayName != null ? displayName : name(); }
++
++    private NodeIdType(int value, String displayName){
++//        if ( value != 0 )
++//            // Set high bit.
++//            value = TYPES.enc(value);
++        this.value = value;
++        this.displayName = displayName;
++    }
++
++    public static NodeIdType intToEnum(int x) {
++        if ( x >= 0x80 )
++            throw new IllegalArgumentException("Value '"+x+"' not legal: too large");
++        if ( x != 0 )
++            x = TYPES.enc(x);
++        NodeIdType t = intToEnum$(x);
++        if ( t == INVALID$ )
++            throw new IllegalArgumentException("Value '"+x+"' not legal for "+NodeIdType.class.getSimpleName());
++        return t ;
++    }
++    
++    private static NodeIdType intToEnum$(int x) {
++        //x = TYPES.enc(x);
++        if (x == PTR.value )                        return PTR;
++        // XSD_DOUBL is special encoded - handled elsewhere.
++        if (x == XSD_DOUBLE.value )                 return XSD_DOUBLE ;
++        if (x == XSD_INTEGER.value )                return XSD_INTEGER ;
++        if (x == XSD_DECIMAL.value )                return XSD_DECIMAL ;
++        if (x == XSD_FLOAT.value )                  return XSD_FLOAT ;
++        
++        if (x == XSD_DATETIME.value )               return XSD_DATETIME ;
++        if (x == XSD_DATETIMESTAMP.value )          return XSD_DATETIMESTAMP ;
++        if (x == XSD_DATE.value )                   return XSD_DATE ;
++
++        if (x == XSD_BOOLEAN.value )                return XSD_BOOLEAN ;
++        if (x == XSD_SHORTSTRING.value )            return XSD_SHORTSTRING ;
++        
++        if (x == XSD_POSITIVE_INTEGER.value )       return XSD_POSITIVE_INTEGER;
++        if (x == XSD_NEGATIVE_INTEGER.value )       return XSD_NEGATIVE_INTEGER ;
++        if (x == XSD_NON_NEGATIVE_INTEGER.value )   return XSD_NON_NEGATIVE_INTEGER ;
++        if (x == XSD_NON_POSITIVE_INTEGER.value )   return XSD_NON_POSITIVE_INTEGER ;
++        
++        if (x == XSD_LONG.value )                   return XSD_LONG ;
++        if (x == XSD_INT.value )                    return XSD_INT ;
++        if (x == XSD_SHORT.value )                  return XSD_SHORT ;
++        if (x == XSD_BYTE.value )                   return XSD_BYTE ;
++        if (x == XSD_UNSIGNEDLONG.value )           return XSD_UNSIGNEDLONG ;
++        if (x == XSD_UNSIGNEDINT.value )            return XSD_UNSIGNEDINT ;
++        if (x == XSD_UNSIGNEDSHORT.value )          return XSD_UNSIGNEDSHORT ;
++        if (x == XSD_UNSIGNEDBYTE.value )           return XSD_UNSIGNEDBYTE ;
++        //if (x == EXTENSION.value )                  return EXTENSION ;
++        return INVALID$;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/PrefixMappingCache.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/PrefixMappingCache.java
index 0000000,0000000..ca950d7
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/PrefixMappingCache.java
@@@ -1,0 -1,0 +1,202 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store;
++
++import java.util.Map ;
++import java.util.Optional ;
++import java.util.Map.Entry ;
++
++import org.apache.jena.atlas.iterator.Iter;
++import org.apache.jena.atlas.lib.Cache ;
++import org.apache.jena.atlas.lib.CacheFactory ;
++import org.apache.jena.rdf.model.impl.Util ;
++import org.apache.jena.shared.PrefixMapping ;
++
++// Unsafe. Non-transactional cache.
++public class PrefixMappingCache implements PrefixMapping {
++
++    private final PrefixMapping other ;
++    private Cache<String, String> prefixToUri = CacheFactory.createCache(100) ;
++    private Cache<String, String> uriToPrefix = CacheFactory.createCache(100) ;
++
++    public PrefixMappingCache(PrefixMapping other) {
++        this.other = other ;
++    }
++    
++    private void add(String prefix, String uri) {
++        prefixToUri.put(prefix, uri) ; 
++        uriToPrefix.put(uri, prefix);
++    }
++    
++    private void remove(String prefix, String uri) {
++        prefixToUri.remove(prefix) ; 
++        uriToPrefix.remove(uri);
++    }
++
++    private void clear() {
++        prefixToUri.clear() ; 
++        uriToPrefix.clear() ;
++    }
++
++    @Override
++    public PrefixMapping setNsPrefix(String prefix, String uri) {
++        other.setNsPrefix(prefix, uri) ;
++        add(prefix, uri);
++        return this ;
++    }
++
++    @Override
++    public PrefixMapping removeNsPrefix(String prefix) {
++        String uri = getNsPrefixURI(prefix) ;
++        if ( uri != null )
++            remove(prefix, uri);
++        other.removeNsPrefix(prefix) ;
++        return this ;
++    }
++
++    @Override
++    public PrefixMapping clearNsPrefixMap() {
++        prefixToUri.clear() ;
++        uriToPrefix.clear();
++        other.clearNsPrefixMap() ;
++        return this ;
++    }
++
++    @Override
++    public PrefixMapping setNsPrefixes(PrefixMapping pmap) {
++        setNsPrefixes(pmap.getNsPrefixMap()) ;
++        return this ;
++    }
++
++    @Override
++    public PrefixMapping setNsPrefixes(Map<String, String> map) {
++        map.entrySet().forEach(entry->{
++            setNsPrefix(entry.getKey(), entry.getValue()) ;
++        });
++        return this ;
++    }
++
++    @Override
++    public PrefixMapping withDefaultMappings(PrefixMapping map) {
++        other.withDefaultMappings(map) ;
++        clear() ;
++        return this ;
++    }
++
++    @Override
++    public String getNsPrefixURI(String prefix) {
++        String x = prefixToUri.getIfPresent(prefix) ;
++        if ( x == null ) {
++            x = other.getNsPrefixURI(prefix) ;
++            if ( x != null )
++                prefixToUri.put(prefix, x); 
++        }
++        return x ;
++    }
++
++    @Override
++    public String getNsURIPrefix(String uri) {
++        String x = uriToPrefix.getIfPresent(uri) ;
++        if ( x == null ) {
++            x = other.getNsURIPrefix(uri) ;
++            if ( x != null )
++                uriToPrefix.put(uri, x); 
++        }
++        return x ;
++    }
++
++    @Override
++    public Map<String, String> getNsPrefixMap() {
++        // Ignore cache - get everything from the provider.
++        return other.getNsPrefixMap() ;
++    }
++
++    // From PrefixMappingImpl
++    // Libraryize?
++    @Override
++    public String expandPrefix(String prefixed) {
++        int colon = prefixed.indexOf(':');
++        if ( colon < 0 )
++            return prefixed;
++        else {
++            String prefix = prefixed.substring(0, colon);
++            String uri = getNsPrefixURI(prefix);
++            return uri == null ? prefixed : uri + prefixed.substring(colon + 1);
++        }
++    }
++
++    @Override
++    public String qnameFor(String uri) {
++        int split = Util.splitNamespaceXML(uri) ;
++        String ns = uri.substring(0, split);
++        String local = uri.substring(split) ;
++        if ( local.equals("") )
++            return null ;
++        String prefix = getNsURIPrefix(ns) ;
++        return prefix == null ? null : prefix + ":" + local ;
++    }
++    
++    @Override
++    public String shortForm(String uri) {
++        Optional<Entry<String, String>> e = findMapping(uri, true) ;
++        if ( ! e.isPresent() )
++            return uri ;
++        return e.get().getKey() + ":" + uri.substring((e.get().getValue()).length()) ;
++    }
++
++    private Optional<Entry<String, String>> findMapping( String uri, boolean partial ) {
++        return getNsPrefixMap().entrySet().stream().sequential().filter(e->{
++            String ss = e.getValue();
++            if (uri.startsWith( ss ) && (partial || ss.length() == uri.length())) 
++                return true;
++            return false ;
++        }).findFirst() ;
++    }    
++
++    @Override
++    public boolean samePrefixMappingAs(PrefixMapping other) {
++        return other.samePrefixMappingAs(other) ;
++    }
++
++    @Override
++    public PrefixMapping lock() {
++        return this ;
++    }
++
++    @Override 
++    public boolean hasNoMappings() {
++        return other.hasNoMappings() ;
++    }
++    
++    @Override
++    public int numPrefixes() {
++        return other.numPrefixes() ;
++    }
++
++    private static String str(PrefixMapping pmap) {
++        return pmap.getNsPrefixMap().toString();
++    }
++    
++    @Override
++    public String toString() {
++        // Problem : only prints the cache.
++        String x = Iter.iter(prefixToUri.keys()).map(k->k+"->"+prefixToUri.getIfPresent(k)).asString(", ");
++        return "pm cache: ["+prefixToUri.size()+"] "+x+" : " + str(other);
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/QuadTable.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/QuadTable.java
index 0000000,0000000..6d4b366
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/QuadTable.java
@@@ -1,0 -1,0 +1,90 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store ;
++
++import java.util.Iterator ;
++
++import org.apache.jena.atlas.iterator.Iter;
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.graph.Node ;
++import org.apache.jena.graph.Triple ;
++import org.apache.jena.sparql.core.Quad ;
++import org.seaborne.tdb2.lib.TupleLib ;
++import org.seaborne.tdb2.store.nodetable.NodeTable ;
++import org.seaborne.tdb2.store.tupletable.TupleIndex ;
++
++/**
++ * Quad table - a collection of TupleIndexes for 4-tuples together with a node
++ * table.
++ */
++
++public class QuadTable extends TableBase {
++    public QuadTable(TupleIndex[] indexes, NodeTable nodeTable) {
++        super(4, indexes, nodeTable) ;
++    }
++
++    /** Add a quad - return true if it was added, false if it already existed */
++    public void add(Quad quad) {
++        add(quad.getGraph(), quad.getSubject(), quad.getPredicate(), quad.getObject()) ;
++    }
++
++    /**
++     * Add a quad (as graph node and triple) - return true if it was added,
++     * false if it already existed
++     */
++    public void add(Node gn, Triple triple) {
++        add(gn, triple.getSubject(), triple.getPredicate(), triple.getObject()) ;
++    }
++
++    /** Add a quad - return true if it was added, false if it already existed */
++    public void add(Node g, Node s, Node p, Node o) {
++        table.addRow(g, s, p, o) ;
++    }
++
++    /** Delete a quad - return true if it was deleted, false if it didn't exist */
++    public void delete(Quad quad) {
++        delete(quad.getGraph(), quad.getSubject(), quad.getPredicate(), quad.getObject()) ;
++    }
++
++    /**
++     * Delete a quad (as graph node and triple) - return true if it was deleted,
++     * false if it didn't exist
++     */
++    public void delete(Node gn, Triple triple) {
++        delete(gn, triple.getSubject(), triple.getPredicate(), triple.getObject()) ;
++    }
++
++    /** Delete a quad - return true if it was deleted, false if it didn't exist */
++    public void delete(Node g, Node s, Node p, Node o) {
++        table.deleteRow(g, s, p, o) ;
++    }
++
++    /** Find matching quads */
++    public Iterator<Quad> find(Node g, Node s, Node p, Node o) {
++        Iterator<Tuple<NodeId>> iter = table.findAsNodeIds(g, s, p, o) ;
++        if ( iter == null )
++            return Iter.nullIterator() ;
++        Iterator<Quad> iter2 = TupleLib.convertToQuads(table.getNodeTable(), iter) ;
++        return iter2 ;
++    }
++
++    /** Clear - does not clear the associated node tuple table */
++    public void clearQuads() {
++        table.clear() ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/StorageTDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/StorageTDB.java
index 0000000,0000000..f9b39ec
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/StorageTDB.java
@@@ -1,0 -1,0 +1,41 @@@
++/*
++ * 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.seaborne.tdb2.store;
++
++import org.apache.jena.sparql.core.DatasetPrefixStorage;
++import org.seaborne.dboe.base.file.Location;
++import org.seaborne.tdb2.setup.StoreParams;
++
++public class StorageTDB {
++
++    /*package*/ final TripleTable tripleTable;
++    /*package*/ final QuadTable quadTable;
++    /*package*/ final DatasetPrefixStorage prefixes;
++    /*package*/ final Location location;
++    /*package*/ final StoreParams storeParams;
++    
++    public StorageTDB(TripleTable tripleTable, QuadTable quadTable, DatasetPrefixStorage prefixes, Location location, StoreParams params) {
++        super();
++        this.tripleTable = tripleTable;
++        this.quadTable = quadTable;
++        this.prefixes = prefixes;
++        this.location = location;
++        this.storeParams = params;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/TableBase.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/TableBase.java
index 0000000,0000000..1241a0b
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/TableBase.java
@@@ -1,0 -1,0 +1,48 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store;
++
++import org.apache.jena.atlas.lib.Closeable ;
++import org.apache.jena.atlas.lib.Sync ;
++import org.seaborne.tdb2.store.nodetable.NodeTable ;
++import org.seaborne.tdb2.store.nodetupletable.NodeTupleTable ;
++import org.seaborne.tdb2.store.nodetupletable.NodeTupleTableConcrete ;
++import org.seaborne.tdb2.store.tupletable.TupleIndex ;
++
++public class TableBase implements Sync, Closeable
++{
++    final protected NodeTupleTable table ;
++    
++    protected TableBase(int N, TupleIndex[] indexes, NodeTable nodeTable) {
++        table = new NodeTupleTableConcrete(N, indexes, nodeTable) ;
++    }
++
++    public NodeTupleTable getNodeTupleTable()   { return table ; }
++    
++    @Override
++    public void sync() {
++        table.sync() ;
++    }
++
++    @Override
++    public void close()
++    { table.close() ; }
++    
++    public boolean isEmpty()        { return table.isEmpty() ; }
++    
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/TripleTable.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/TripleTable.java
index 0000000,0000000..54dcac3
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/TripleTable.java
@@@ -1,0 -1,0 +1,74 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store ;
++
++import java.util.Iterator ;
++
++import org.apache.jena.atlas.iterator.Iter;
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.graph.Node ;
++import org.apache.jena.graph.Triple ;
++import org.seaborne.tdb2.lib.TupleLib ;
++import org.seaborne.tdb2.store.nodetable.NodeTable ;
++import org.seaborne.tdb2.store.tupletable.TupleIndex ;
++
++/**
++ * TripleTable - a collection of TupleIndexes for 3-tuples together with a node
++ * table. Normally, based on 3 indexes (SPO, POS, OSP) but other indexing
++ * structures can be configured.
++ */
++
++public class TripleTable extends TableBase {
++    public TripleTable(TupleIndex[] indexes, NodeTable nodeTable) {
++        super(3, indexes, nodeTable) ;
++    }
++
++    /** Add triple */
++    public void add(Triple triple) {
++        add(triple.getSubject(), triple.getPredicate(), triple.getObject()) ;
++    }
++
++    /** Add triple */
++    public void add(Node s, Node p, Node o) {
++        table.addRow(s, p, o) ;
++    }
++
++    /** Delete a triple */
++    public void delete(Triple triple) {
++        delete(triple.getSubject(), triple.getPredicate(), triple.getObject()) ;
++    }
++
++    /** Delete a triple */
++    public void delete(Node s, Node p, Node o) {
++        table.deleteRow(s, p, o) ;
++    }
++
++    /** Find matching triples */
++    public Iterator<Triple> find(Node s, Node p, Node o) {
++        Iterator<Tuple<NodeId>> iter = table.findAsNodeIds(s, p, o) ;
++        if ( iter == null )
++            return Iter.nullIterator() ;
++        Iterator<Triple> iter2 = TupleLib.convertToTriples(table.getNodeTable(), iter) ;
++        return iter2 ;
++    }
++
++    /** Clear - does not clear the associated node tuple table */
++    public void clearTriples() {
++        table.clear() ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTable.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTable.java
index 0000000,0000000..4cc367e
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTable.java
@@@ -1,0 -1,0 +1,81 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.nodetable;
++
++import java.util.Iterator ;
++import java.util.List ;
++
++import org.apache.jena.atlas.lib.Closeable ;
++import org.apache.jena.atlas.lib.Pair ;
++import org.apache.jena.atlas.lib.Sync ;
++import org.apache.jena.graph.Node ;
++import org.seaborne.tdb2.store.NodeId ;
++
++/** Node table - conceptually a two way mapping of Node<->NodeId 
++ *  where Nodes can be staored and a NodeId allocated
++ *  @see NodeId
++ */ 
++
++public interface NodeTable extends Sync, Closeable
++{
++    /** Store the node in the node table (if not already present) and return the allocated Id. */
++    public NodeId getAllocateNodeId(Node node) ;
++    
++    /** Look up node and return the NodeId - return NodeId.NodeDoesNotExist if not found */
++    public NodeId getNodeIdForNode(Node node) ;
++    
++    /** Look up node id and return the Node - return null if not found */
++    public Node getNodeForNodeId(NodeId id) ;
++    
++    /** Test whether the node table contains an entry for node */
++    public boolean containsNode(Node node) ;
++
++    /** Test whether the node table contains an entry for node */
++    public boolean containsNodeId(NodeId nodeId) ;
++    
++    /** Bulk mapping from {@code Node} to {@code NodeId}, with allocation
++     * if the {@code withAllocation} is true.
++     * The returned list aligns with the input list.
++     */
++    public List<NodeId> bulkNodeToNodeId(List<Node> nodes, boolean withAllocation) ;
++
++    /** Bulk mapping from {@code NodeId} to {@code Node} */
++    public List<Node> bulkNodeIdToNode(List<NodeId> nodeIds) ;
++
++    /** Bulk lookup
++    public List<NodeId> getAllocateNodeIdBulk(List<Node> nodes) ;
++
++    /** Iterate over all nodes (not necessarily fast).  Does not include inlined NodeIds */
++    public Iterator<Pair<NodeId, Node>> all() ;
++    
++    /** Anything there? */  
++    public boolean isEmpty() ; 
++
++    /** Return a NodeTable if this instance wraps another, else return null */  
++    public NodeTable wrapped() ; 
++
++    /** Return the base NodeTable, the end of the wrapped chain */  
++    default public NodeTable baseNodeTable() {
++        NodeTable nt = this ;
++        NodeTable nt2 = null ;
++        while ( (nt2 = nt.wrapped()) != null ) {
++            nt = nt2 ;
++        }
++        return nt ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableCache.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableCache.java
index 0000000,0000000..c153d81
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableCache.java
@@@ -1,0 -1,0 +1,352 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.nodetable ;
++
++import java.util.ArrayList ;
++import java.util.Iterator ;
++import java.util.List ;
++
++import org.apache.jena.atlas.iterator.Iter ;
++import org.apache.jena.atlas.lib.Cache ;
++import org.apache.jena.atlas.lib.CacheFactory ;
++import org.apache.jena.atlas.lib.CacheSet ;
++import org.apache.jena.atlas.lib.Pair ;
++import org.apache.jena.atlas.logging.Log ;
++import org.apache.jena.graph.Node ;
++import org.seaborne.tdb2.TDBException ;
++import org.seaborne.tdb2.setup.StoreParams ;
++import org.seaborne.tdb2.store.NodeId ;
++
++/**
++ * Cache wrapper around a NodeTable. Assumes all access goes through this
++ * wrapper. Read-cache - write caching is done via the object file used by the
++ * base NodeTable.
++ */
++public class NodeTableCache implements NodeTable {
++    // These caches are updated together.
++    // See synchronization in _retrieveNodeByNodeId and _idForNode
++    // The cache is assumed to be single operation-thread-safe.
++    private Cache<Node, NodeId> node2id_Cache = null ;
++    private Cache<NodeId, Node> id2node_Cache = null ;
++
++    // A small cache of "known unknowns" to speed up searching for impossible
++    // things.
++    // Cache update needed on NodeTable changes because a node may become
++    // "known"
++    private CacheSet<Node>      notPresent    = null ;
++    private NodeTable           baseTable ;
++    private final Object        lock          = new Object() ;
++
++    public static NodeTable create(NodeTable nodeTable, StoreParams params) {
++        int nodeToIdCacheSize = params.getNode2NodeIdCacheSize() ;
++        int idToNodeCacheSize = params.getNodeId2NodeCacheSize() ;
++        if ( nodeToIdCacheSize <= 0 && idToNodeCacheSize <= 0 )
++            return nodeTable ;
++        return new NodeTableCache(nodeTable, nodeToIdCacheSize, idToNodeCacheSize, params.getNodeMissCacheSize()) ;
++    }
++
++    public static NodeTable create(NodeTable nodeTable, int nodeToIdCacheSize, int idToNodeCacheSize, int nodeMissesCacheSize) {
++        if ( nodeToIdCacheSize <= 0 && idToNodeCacheSize <= 0 )
++            return nodeTable ;
++        return new NodeTableCache(nodeTable, nodeToIdCacheSize, idToNodeCacheSize, nodeMissesCacheSize) ;
++    }
++
++    private NodeTableCache(NodeTable baseTable, int nodeToIdCacheSize, int idToNodeCacheSize, int nodeMissesCacheSize) {
++        this.baseTable = baseTable ;
++        if ( nodeToIdCacheSize > 0 )
++            node2id_Cache = CacheFactory.createCache(nodeToIdCacheSize) ;
++        if ( idToNodeCacheSize > 0 )
++            id2node_Cache = CacheFactory.createCache(idToNodeCacheSize) ;
++        if ( nodeMissesCacheSize > 0 )
++            notPresent = CacheFactory.createCacheSet(nodeMissesCacheSize) ;
++    }
++
++    // ---- Cache access, no going to underlying table.
++    
++    public Node getNodeForNodeIdCache(NodeId id) {
++        return id2node_Cache.getIfPresent(id) ;
++    }
++    
++    public NodeId getNodeIdForNodeCache(Node node) {
++        return node2id_Cache.getIfPresent(node) ;
++    }
++
++    public boolean isCachedNodeId(NodeId id) {
++        return getNodeForNodeIdCache(id) != null ;
++    }
++    
++    public boolean isCachedNode(Node node) {
++        return getNodeIdForNodeCache(node) != null ;
++    }
++
++    // ---- Cache access
++
++    @Override
++    public final NodeTable wrapped() {
++        return baseTable ;
++    }
++
++    /** Get the Node for this NodeId, or null if none */
++    @Override
++    public Node getNodeForNodeId(NodeId id) {
++        return _retrieveNodeByNodeId(id) ;
++    }
++
++    /** Find the NodeId for a node, or return NodeId.NodeDoesNotExist */
++    @Override
++    public NodeId getNodeIdForNode(Node node) {
++        return _idForNode(node, false) ;
++    }
++
++    /**
++     * Find the NodeId for a node, allocating a new NodeId if the Node does not
++     * yet have a NodeId
++     */
++    @Override
++    public NodeId getAllocateNodeId(Node node) {
++        return _idForNode(node, true) ;
++    }
++
++    @Override
++    public boolean containsNode(Node node) {
++        NodeId x = getNodeIdForNode(node) ;
++        return NodeId.isDoesNotExist(x) ;
++    }
++
++    @Override
++    public boolean containsNodeId(NodeId nodeId) {
++        Node x = getNodeForNodeId(nodeId) ;
++        return x == null ;
++    }
++    
++    // If LRU cache is larger than the bulk unit, all allocated nodes are in the cache.   
++//    @Override
++//    public List<NodeId> bulkNodeToNodeId(List<Node> nodes, boolean withAllocation) {
++//        return NodeTable.super.bulkNodeToNodeId(nodes, withAllocation) ;
++//    }
++//
++//    @Override
++//    public List<Node> bulkNodeIdToNode(List<NodeId> nodeIds) {
++//        return NodeTable.super.bulkNodeIdToNode(nodeIds) ;
++//    }
++
++    @Override
++    public List<NodeId> bulkNodeToNodeId(List<Node> required, boolean withAllocation) {
++        synchronized(lock) {
++            List<Node> nodes = new ArrayList<>() ;
++            for ( Node n : required ) {
++                // 
++                if ( getNodeIdForNodeCache(n) == null )
++                    nodes.add(n) ;
++            }
++            // Check bulk access.
++            List<NodeId> x = baseTable.bulkNodeToNodeId(nodes, true) ;
++            for ( int i = 0 ; i < nodes.size() ; i++ ) {
++                Node n = nodes.get(i) ;
++                NodeId nid = x.get(i) ;
++                cacheUpdate(n ,nid) ;
++            }
++            return x ;
++        }
++    }
++
++    @Override
++    public List<Node> bulkNodeIdToNode(List<NodeId> nodeIds) {
++        return NodeTableOps.bulkNodeIdToNodeImpl(this, nodeIds) ;
++    }
++
++    // ---- The worker functions
++    // NodeId ==> Node
++    private Node _retrieveNodeByNodeId(NodeId id) {
++        if ( NodeId.isDoesNotExist(id) )
++            return null ;
++        if ( NodeId.isAny(id) )
++            return null ;
++        // Try once outside the synchronized
++        // (Cache access is thread-safe)
++        Node n = cacheLookup(id) ;
++        if ( n != null )
++            return n ;
++
++        synchronized (lock) {
++            // Lock to update two caches consisently.
++            // Verify cache miss
++            n = cacheLookup(id) ;
++            if ( n != null )
++                return n ;
++
++            if ( baseTable == null )
++                System.err.println("" + this) ;
++
++            n = baseTable.getNodeForNodeId(id) ;
++            cacheUpdate(n, id) ;
++            return n ;
++        }
++    }
++
++    // Node ==> NodeId
++    private NodeId _idForNode(Node node, boolean allocate) {
++        if ( node == Node.ANY )
++            return NodeId.NodeIdAny ;
++        // Try once outside the synchronized
++        // (Cache access is thread-safe.)
++        NodeId nodeId = cacheLookup(node) ;
++        if ( nodeId != null )
++            return nodeId ;
++        synchronized (lock) {
++            // Update two caches inside synchronized.
++            // Check stil valid.
++            nodeId = cacheLookup(node) ;
++            if ( nodeId != null )
++                return nodeId ;
++
++            if ( allocate )
++                nodeId = baseTable.getAllocateNodeId(node) ;
++            else
++                nodeId = baseTable.getNodeIdForNode(node) ;
++            // Ensure caches have it. Includes recording "no such node"
++            cacheUpdate(node, nodeId) ;
++            return nodeId ;
++        }
++    }
++
++    // ----------------
++    // ---- Only places that the caches are touched
++
++    /**
++     * Check caches to see if we can map a NodeId to a Node. Returns null on no
++     * cache entry.
++     */
++    private Node cacheLookup(NodeId id) {
++        if ( id2node_Cache == null )
++            return null ;
++        return id2node_Cache.getIfPresent(id) ;
++    }
++
++    /**
++     * Check caches to see if we can map a Node to a NodeId. Returns null on no
++     * cache entry.
++     */
++    private NodeId cacheLookup(Node node) {
++        // Remember things known (currently) not to exist.
++        // Does not matter if notPresent is being updated elsewhere.
++        if ( notPresent != null && notPresent.contains(node) )
++            return null ;
++        if ( node2id_Cache == null )
++            return null ;
++        return node2id_Cache.getIfPresent(node) ;
++    }
++
++    /** Update the Node->NodeId caches */
++    private void cacheUpdate(Node node, NodeId id) {
++        if ( node == null )
++            return ;
++
++        // synchronized is further out.
++        // The "notPresent" cache is used to note whether a node
++        // is known not to exist.
++        // This must be specially handled later if the node is added.
++        if ( NodeId.isDoesNotExist(id) ) {
++            if ( notPresent != null )
++                notPresent.add(node) ;
++            return ;
++        }
++
++        if ( id == NodeId.NodeIdAny ) {
++            Log.warn(this, "Attempt to cache NodeIdAny - ignored") ;
++            return ;
++        }
++
++        if ( node2id_Cache != null )
++            node2id_Cache.put(node, id) ;
++        if ( id2node_Cache != null )
++            id2node_Cache.put(id, node) ;
++        // Remove if previously marked "not present"
++        if ( notPresent != null && notPresent.contains(node) )
++            notPresent.remove(node) ;
++    }
++
++    // ----
++
++    @Override
++    public boolean isEmpty() {
++        synchronized (lock) {
++            if ( node2id_Cache != null )
++                return node2id_Cache.isEmpty() ;
++            if ( id2node_Cache != null )
++                id2node_Cache.isEmpty() ;
++            // Write through.
++            return baseTable.isEmpty() ;
++        }
++    }
++
++    @Override
++    public synchronized void close() {
++        if ( baseTable == null )
++            // Already closed (NodeTables can be shared so .close via two
++            // routes).
++            return ;
++
++        baseTable.close() ;
++        node2id_Cache = null ;
++        id2node_Cache = null ;
++        notPresent = null ;
++        baseTable = null ;
++    }
++
++    @Override
++    public void sync() {
++        baseTable.sync() ;
++    }
++
++    @Override
++    public Iterator<Pair<NodeId, Node>> all() {
++        if ( false )
++            testForConsistency() ;
++        return baseTable.all() ;
++    }
++
++    private void testForConsistency() {
++        Iterator<Node> iter1 = Iter.toList(node2id_Cache.keys()).iterator() ;
++
++        for ( ; iter1.hasNext() ; ) {
++            Node n = iter1.next() ;
++
++            NodeId nId = node2id_Cache.getIfPresent(n) ;
++            if ( !id2node_Cache.containsKey(nId) )
++                throw new TDBException("Inconsistent: " + n + " => " + nId) ;
++            if ( notPresent.contains(n) )
++                throw new TDBException("Inconsistent: " + n + " in notPresent cache (1)") ;
++        }
++        Iterator<NodeId> iter2 = Iter.toList(id2node_Cache.keys()).iterator() ;
++        for ( ; iter2.hasNext() ; ) {
++            NodeId nId = iter2.next() ;
++            Node n = id2node_Cache.getIfPresent(nId) ;
++            if ( !node2id_Cache.containsKey(n) )
++                throw new TDBException("Inconsistent: " + nId + " => " + n) ;
++            if ( notPresent.contains(n) )
++                throw new TDBException("Inconsistent: " + n + " in notPresent cache (2)") ;
++        }
++
++    }
++
++    @Override
++    public String toString() {
++        return "Cache(" + baseTable.toString() + ")" ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableInline.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableInline.java
index 0000000,0000000..4632aa8
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableInline.java
@@@ -1,0 -1,0 +1,70 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.nodetable;
++
++
++import org.apache.jena.graph.Node ;
++import org.seaborne.tdb2.store.NodeId ;
++
++/** NodeTable wrapper to handle inline node ids.
++ * If a node can be made inline, then the underlying table never sees it.
++ * If an inline Nodeid is seen, it is decoded and returned without
++ * the underlying table being called. 
++ */
++
++public class NodeTableInline extends NodeTableWrapper
++{
++    // Stack order: Inline > Cache > Actual
++    
++    public static NodeTable create(NodeTable nodeTable)
++    {
++        return new NodeTableInline(nodeTable) ;
++    }
++    
++    private NodeTableInline(NodeTable nodeTable)
++    {
++        super(nodeTable) ;
++    }
++    
++    @Override
++    public final NodeId getAllocateNodeId(Node node)
++    {
++        NodeId nid = NodeId.inline(node) ;
++        if ( nid != null ) return nid ;
++        return super.getAllocateNodeId(node) ;
++    }
++
++    @Override
++    public final NodeId getNodeIdForNode(Node node)
++    {
++        NodeId nid = NodeId.inline(node) ;
++        if ( nid != null ) return nid ;
++        return super.getNodeIdForNode(node) ;
++    }
++    @Override
++    public final Node getNodeForNodeId(NodeId id)
++    {
++        Node n = NodeId.extract(id) ;
++        if ( n != null )
++            return n ;
++        return super.getNodeForNodeId(id) ;
++    }
++    
++    @Override
++    public String toString() { return "Inline("+nodeTable.toString()+")" ; }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableNative.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableNative.java
index 0000000,0000000..8f7efdc
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableNative.java
@@@ -1,0 -1,0 +1,214 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store.nodetable ;
++
++import java.util.Iterator ;
++import java.util.List ;
++
++import org.apache.jena.atlas.lib.NotImplemented ;
++import org.apache.jena.atlas.lib.Pair ;
++import org.apache.jena.graph.Node ;
++import org.seaborne.dboe.base.record.Record ;
++import org.seaborne.dboe.index.Index ;
++import org.seaborne.tdb2.TDBException ;
++import org.seaborne.tdb2.lib.NodeLib ;
++import org.seaborne.tdb2.store.Hash ;
++import org.seaborne.tdb2.store.NodeId ;
++import org.seaborne.tdb2.store.NodeIdFactory;
++
++/** A framework for a NodeTable based on native storage (string file and an index).
++ *  This class manages the index, and delegates the node storage.
++ */ 
++public abstract class NodeTableNative implements NodeTable
++{
++    protected Index nodeHashToId ;        // hash -> int
++    private boolean syncNeeded = false ;
++    
++    public NodeTableNative(Index nodeToId) {
++        this.nodeHashToId = nodeToId ;
++    }
++    // ---- Public interface for Node <==> NodeId
++
++    /** Get the Node for this NodeId, or null if none */
++    @Override
++    public Node getNodeForNodeId(NodeId id)
++    {
++        return _retrieveNodeByNodeId(id) ;
++    }
++
++    /** Find the NodeId for a node, or return NodeId.NodeDoesNotExist */ 
++    @Override
++    public NodeId getNodeIdForNode(Node node)  { return _idForNode(node, false) ; }
++
++    /** Find the NodeId for a node, allocating a new NodeId if the Node does not yet have a NodeId */ 
++    @Override
++    public NodeId getAllocateNodeId(Node node)  { return _idForNode(node, true) ; }
++
++    @Override
++    public boolean containsNode(Node node) {
++        NodeId x = getNodeIdForNode(node) ;
++        return NodeId.isDoesNotExist(x) ;
++    }
++
++    @Override
++    public boolean containsNodeId(NodeId nodeId) {
++        Node x = getNodeForNodeId(nodeId) ;
++        return x == null ;
++    }
++
++    @Override
++    public List<NodeId> bulkNodeToNodeId(List<Node> nodes, boolean withAllocation) {
++        return NodeTableOps.bulkNodeToNodeIdImpl(this, nodes, withAllocation) ;
++    }
++
++    @Override
++    public List<Node> bulkNodeIdToNode(List<NodeId> nodeIds) {
++        return NodeTableOps.bulkNodeIdToNodeImpl(this, nodeIds) ;
++    }
++    
++    // ---- The worker functions
++    // Synchronization:
++    // accessIndex and readNodeFromTable
++    
++    // Cache around this class further out in NodeTableCache are synchronized
++    // to maintain cache validatity which indirectly sync access to the NodeTable.
++    // But to be sure, we provide MRSW guarantees on this class.
++    // (otherwise if no cache => disaster)
++    // synchonization happens in accessIndex() and readNodeByNodeId
++    
++    // NodeId to Node worker.
++    private Node _retrieveNodeByNodeId(NodeId id)
++    {
++        if ( NodeId.isDoesNotExist(id) )
++            return null ;
++        if ( NodeId.isAny(id) )
++            return null ;
++        synchronized (this) {
++            Node n = readNodeFromTable(id) ;
++            return n ;
++        }
++    }
++
++    // ----------------
++    
++    // Node to NodeId worker
++    // Find a node, possibly placing it in the node file as well
++    private NodeId _idForNode(Node node, boolean allocate)
++    {
++        if ( node == Node.ANY )
++            return NodeId.NodeIdAny ;
++        
++        // synchronized in accessIndex
++        NodeId nodeId = accessIndex(node, allocate) ;
++        return nodeId ;
++    }
++    
++    protected final NodeId accessIndex(Node node, boolean create)
++    {
++        Hash hash = new Hash(nodeHashToId.getRecordFactory().keyLength()) ;
++        NodeLib.setHash(hash, node) ;
++        byte k[] = hash.getBytes() ;        
++        // Key only.
++        Record r = nodeHashToId.getRecordFactory().create(k) ;
++        
++        synchronized (this)  // Pair to readNodeFromTable.
++        {
++            // Key and value, or null
++            Record r2 = nodeHashToId.find(r) ;
++            if ( r2 != null )
++            {
++                // Found.  Get the NodeId.
++                NodeId id = NodeIdFactory.get(r2.getValue(), 0) ;
++                return id ;
++            }
++
++            // Not found.
++            if ( ! create )
++                return NodeId.NodeDoesNotExist ;
++            // Write the node, which allocates an id for it.
++            syncNeeded = true ;
++            NodeId id = writeNodeToTable(node) ;
++
++            // Update the r record with the new id.
++            // r.value := id bytes ;
++            NodeIdFactory.set(id, r.getValue(), 0) ;
++
++            // Put in index - may appear because of concurrency
++            if ( ! nodeHashToId.insert(r) )
++                throw new TDBException("NodeTableBase::nodeToId - record mysteriously appeared") ;
++            return id ;
++        }
++    }
++    
++    // -------- NodeId<->Node
++    // Synchronization:
++    //   write: in accessIndex
++    //   read: in _retrieveNodeByNodeId
++    // Only places for accessing the StringFile.
++    
++    abstract protected NodeId writeNodeToTable(Node node) ;
++    abstract protected Node readNodeFromTable(NodeId id) ;
++    abstract protected void syncSub() ;
++    abstract protected void closeSub() ;
++    
++    // -------- NodeId<->Node
++
++    @Override
++    public synchronized void close()
++    {
++        // Close once.  This may be shared (e.g. triples table and quads table). 
++        if ( nodeHashToId != null )
++        {
++            nodeHashToId.close() ; 
++            closeSub() ;
++            nodeHashToId = null ;
++        }
++    }
++
++    // Not synchronized
++    @Override
++    public Iterator<Pair<NodeId, Node>> all() { return all2() ; }
++    
++    private Iterator<Pair<NodeId, Node>> all2()
++    {
++        throw new NotImplemented() ;
++    }
++
++    @Override
++    public void sync() 
++    { 
++        if ( syncNeeded )
++        {
++            syncSub() ;
++            if ( nodeHashToId != null )
++                nodeHashToId.sync() ;
++            syncNeeded = false ;
++        }
++    }
++
++    @Override
++    public boolean isEmpty()
++    {
++        return nodeHashToId.isEmpty() ;
++    }
++
++    @Override
++    public NodeTable wrapped() {
++        return null ;
++    }
++}


[47/50] [abbrv] jena git commit: JENA-1396: Merge //github/afs/mantis as subdirectory jena-db/

Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/MetaFile.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/MetaFile.java
index 0000000,0000000..bbe3221
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/MetaFile.java
@@@ -1,0 -1,0 +1,364 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++import java.io.* ;
++import java.util.* ;
++
++import org.apache.jena.atlas.lib.* ;
++import org.apache.jena.atlas.lib.Closeable ;
++import org.seaborne.dboe.DBOpEnvException ;
++import org.seaborne.dboe.sys.Names ;
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++/** Abstraction and many convenience operations on metadata. 
++ * Metadata is recorded in Java properties style - not RDF - because it's relative to the file or context used.
++ * Keys and values are always strings.
++ */
++public class MetaFile implements Sync, Closeable
++{
++    private static Comparator<String> comparator = new ComparatorKeys() ;
++    private static Logger log = LoggerFactory.getLogger(MetaFile.class) ;
++    private String metaFilename = null ;
++    private Properties properties = null ;
++    private String label = null ;
++    private boolean changed = false ;
++    private boolean closed = false ;
++    
++    /** Create a MetaFile
++     *  
++     * @param label     Convenience label.
++     * @param fn        On disk filename; {@link Names#memName} for in-memory
++     */
++    public MetaFile(String label, String fn)
++    {
++        this.label = label ;
++        this.metaFilename = fn ;
++
++        if ( fn == null || Names.isMem(fn) )
++            // In-memory.
++            return ;
++        
++        // Make absolute (current directory may change later)
++        if ( ! fn.endsWith(Names.extMeta) )
++            fn = fn+"."+Names.extMeta ;
++        File f = new File(fn) ;
++        this.metaFilename = f.getAbsolutePath() ;
++        // Does not load the details yet.
++        // JDI
++        ensureInit() ; 
++    }
++    
++    private void ensureInit()
++    { 
++        if ( properties == null )
++        {
++            properties = new PropertiesSorted(comparator) ;
++            if ( metaFilename != null )
++                loadProperties() ;
++        }
++    }
++    
++    /** Does this metafile exist on disk? (In-memory MetaFiles always exist) */
++    public boolean existsMetaData()
++    {
++        if ( isMem() )
++            return true ;
++        File f = new File(metaFilename) ;
++        if ( f.isDirectory() )
++            log.warn("Metadata file clashes with a directory") ;
++        return f.exists() && f.isFile() ;
++    }
++    
++    public String getFilename()         { return metaFilename ; } 
++
++    /** Test for the presence of a property */
++    public boolean hasProperty(String key)
++    {
++        return _getProperty(key, null) != null ;
++    }
++
++    /** Get the property value or null. */
++    public String getProperty(String key)
++    {
++        return _getProperty(key, null) ;
++    }
++    
++    /** Get the property value or return supplied default. */
++    public String getProperty(String key, String defaultString)
++    {
++        return _getProperty(key, defaultString) ;
++    }
++
++    /** Get the property value and parse as an integer */
++    public int getPropertyAsInteger(String key)
++    {
++        return Integer.parseInt(_getProperty(key, null)) ;
++    }
++    
++    /** Get the property value and parse as an integer or return default value. */
++    public int getPropertyAsInteger(String key, int defaultValue)
++    {
++        String x = getProperty(key) ;
++        if ( x == null )
++            return defaultValue ;
++        return Integer.parseInt(x) ;
++    }
++
++    /** Get property as a string and split on ",". */
++    public String[] getPropertySplit(String key)
++    {
++        String str = getProperty(key) ;
++        if ( str == null )
++            return null ;
++        return str.split(",") ;
++    }
++    
++    /** Get property as a string and split on ",", using the default string if not present in the MetaFile. */
++    public String[] getPropertySplit(String key, String defaultString)
++    {
++        String str = getProperty(key, defaultString) ;
++        return str.split(",") ;
++    }
++    
++    /** Set property */
++    public void setProperty(String key, String value)
++    {
++        _setProperty(key, value) ;
++    }
++    
++    /** Set property, turning integer into a string. */
++    public void setProperty(String key, int value)
++    {
++        _setProperty(key, Integer.toString(value)) ;
++    }
++    
++    /** Test whether a property has a value.  Null tests equal to not present. */
++    public boolean propertyEquals(String key, String value)
++    {
++        return Objects.equals(getProperty(key), value) ;
++    }
++
++    /** Set property if not already set. */
++    public void ensurePropertySet(String key, String expected)
++    {
++        getOrSetDefault(key, expected) ;
++    }
++
++    /** Get property or the default value - also set the default value if not present */
++    public String getOrSetDefault(String key, String expected)
++    {
++        String x = getProperty(key) ;
++        if ( x == null )
++        {
++            setProperty(key, expected) ;
++            x = expected ;
++        }
++        return x ;
++    }
++    
++    /** Check property is an expected value or set if missing */
++    public void checkOrSetMetadata(String key, String expected)
++    {
++        String x = getProperty(key) ;
++        if ( x == null )
++        {
++            setProperty(key, expected) ;
++            return ; 
++        }
++        if ( x.equals(expected) )
++            return ;
++        
++        inconsistent(key, x, expected) ; 
++    }
++
++    /** Check property has the value given - throw exception if not. */
++    public void checkMetadata(String key, String expected)
++    {
++        String value = getProperty(key) ;
++        
++        if ( !Objects.equals(value, value) )
++            inconsistent(key, value, expected) ;
++    }
++
++    private static void inconsistent(String key, String actual, String expected) 
++    {
++        String msg = String.format("Inconsistent: key=%s value=%s expected=%s", 
++                                   key, 
++                                   (actual==null?"<null>":actual),
++                                   (expected==null?"<null>":expected) ) ;
++        throw new MetaFileException(msg) ; 
++    }
++    
++    /** Clear all properties. */
++    public void clear()
++    {
++        _clear() ;
++    }
++
++    // ---- All get/set access through these  operations
++    private String _getProperty(String key, String dft)
++    {
++        ensureInit() ;
++        return properties.getProperty(key, dft) ;
++    }
++    
++    private void _setProperty(String key, String value)
++    {
++        ensureInit() ;
++        properties.setProperty(key, value) ;
++        changedEvent() ;
++    }
++    
++    /** Clear all properties. */
++    private void _clear()
++    {
++        ensureInit() ;
++        properties.clear() ;
++        changedEvent() ;
++    }
++
++    private void changedEvent() { changed = true ; }
++    // ----
++    
++    private boolean isMem() { return Names.isMem(metaFilename) ; }
++    
++    /** Write to backing file if changed */
++    public void flush()
++    {
++        if ( log.isDebugEnabled() )
++            log.debug("Flush metadata ("+changed+"): "+this.label) ;
++        if ( ! changed )
++            return ;
++        
++        
++        if ( log.isDebugEnabled() )
++        {
++            ByteArrayOutputStream out = new ByteArrayOutputStream() ;
++            PrintStream ps = new PrintStream(out) ;
++            properties.list(ps) ;
++            ps.flush() ;
++            log.debug("\n"+out.toString()) ;
++        }
++        
++        //properties.list(System.out) ;
++        
++        saveProperties() ;
++        changed = false ;
++    }
++
++    private void saveProperties()
++    {
++        if ( isMem() )
++            return ;
++        String str = label ;
++        if ( str == null )
++            str = metaFilename ;
++        str = "Metadata: "+str ;
++
++        try {
++            PropertyUtils.storeToFile(properties, str, metaFilename) ;
++        } 
++        catch (IOException ex)
++        {
++            log.error("Failed to store properties: "+metaFilename, ex) ;
++        }
++    }
++
++    
++    private void loadProperties()
++    {
++        if ( isMem() )
++        {
++            properties = new Properties() ;
++            return ;
++        }
++        
++        if ( properties == null )
++            properties = new Properties() ;
++        
++        // if ( metaFilename == null )
++        InputStream in = null ;
++        try { 
++            //  Copes with UTF-8 for Java5. 
++            PropertyUtils.loadFromFile(properties, metaFilename) ;
++        }
++        catch (FileNotFoundException ex) {} 
++        catch (IOException ex)
++        {
++            log.error("Failed to load properties: "+metaFilename, ex) ;
++        }
++    }
++    
++    /** Debugging */
++    public void dump(PrintStream output)
++    {
++        output.println("Metafile: "+metaFilename) ;
++        output.println("Label: "+label) ;
++        output.println("Status: "+(changed?"changed":"unchanged")) ;
++        
++        if ( properties == null )
++        {
++            output.println("#<null>") ;
++            return ;
++        }
++        // properties.list() ;
++        SortedSet<Object> x = new TreeSet<>() ;
++        x.addAll(properties.keySet()) ;
++        
++        for ( Object k : x )
++        {
++            String key = (String)k ;
++            String value = properties.getProperty(key) ;
++            output.print(key) ;
++            output.print("=") ;
++            output.print(value) ;
++            output.println() ;
++        }
++    }
++
++    @Override
++    public void sync()                  { flush() ; }
++    
++    @Override
++    public void close()
++    {
++        flush() ;
++        closed = true ;
++        metaFilename = null ;
++        properties = null ;
++
++    }
++
++    private static class ComparatorKeys implements Comparator<String>
++    {
++        @Override
++        public int compare(String o1, String o2)
++        {
++            return - o1.compareTo(o2) ;
++        }
++        
++    }
++    
++    private static class MetaFileException extends DBOpEnvException
++    {
++        MetaFileException(String msg) { super(msg) ; }
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/ProcessFileLock.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/ProcessFileLock.java
index 0000000,0000000..990dcab
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/ProcessFileLock.java
@@@ -1,0 -1,0 +1,215 @@@
++/*
++ * 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.seaborne.dboe.base.file;
++
++import static java.nio.file.StandardOpenOption.CREATE;
++import static java.nio.file.StandardOpenOption.DSYNC;
++import static java.nio.file.StandardOpenOption.READ;
++import static java.nio.file.StandardOpenOption.WRITE;
++
++import java.io.FileNotFoundException;
++import java.io.IOException;
++import java.nio.ByteBuffer;
++import java.nio.channels.FileChannel;
++import java.nio.channels.FileLock;
++import java.nio.file.NoSuchFileException;
++import java.nio.file.Path;
++import java.nio.file.Paths;
++import java.util.concurrent.ConcurrentHashMap;
++
++import org.apache.jena.atlas.RuntimeIOException;
++import org.apache.jena.atlas.io.IO;
++import org.apache.jena.atlas.lib.StrUtils;
++import org.apache.jena.atlas.logging.Log;
++import org.seaborne.dboe.DBOpEnvException;
++import org.seaborne.dboe.sys.ProcessUtils;
++
++/** A simple packaging around a {@link java.nio.channels.FileLock}. 
++ *  {@code ProcessFileLock} are not reentrant.
++ */
++public class ProcessFileLock {
++
++    // Static (process-wide) sync.
++    private static Object sync = new Object();
++    
++    // Map from path of the file to a ProcessFileLock
++    private static ConcurrentHashMap<Path, ProcessFileLock> locks =  new ConcurrentHashMap<>();
++
++    private final Path filepath;
++    private final FileChannel fileChannel;
++    private FileLock fileLock;
++
++    // Different variations for what to do when a lock can not be taken.
++    private enum NoLockAction { EXCEPTION, RETURN, WAIT }
++
++    /** Create a {@code ProcessFileLock} using the named file.
++     *  Locks are JVM-wide; each filename is assocauted with one lock object.  
++     */
++    public static ProcessFileLock create(String filename) {
++        try {
++            Path abspath = Paths.get(filename).toRealPath();
++            return locks.computeIfAbsent(abspath, ProcessFileLock::new);
++        }
++        catch (IOException e) { IO.exception(e); return null; }
++    }
++    
++    /** Return the lock, unlocking the file if this process has it locked. */
++    public static void release(ProcessFileLock lockFile) {
++        if ( lockFile == null )
++            return ;
++        if ( lockFile.isLockedHere() )
++            lockFile.unlock();
++        locks.remove(lockFile.getPath());
++    }
++    /** Create the structure for a ProcessFileLock on file {@code filename}.
++     * This does not take the lock
++     * 
++     * @see #lockEx()
++     * @see #tryLock()
++     * @see #unlock()
++     */
++    private ProcessFileLock(Path filename) {
++        try {
++            this.filepath = filename ;
++            // Much the same as ... 
++//            randomAccessFile = new RandomAccessFile(filename, "rw");
++//            fileChannel = randomAccessFile.getChannel();
++            // Quite heavy weight but only used to lock long-term objects.
++            this.fileChannel = FileChannel.open(filename, CREATE, WRITE, READ, DSYNC);
++            fileLock = null;
++        }
++        catch (NoSuchFileException | FileNotFoundException ex) {
++            // The path does not name a possible file in an exists directory.
++            throw new RuntimeIOException("No such file '"+filename+"'", ex);
++        }
++        catch (IOException ex) {
++            throw new RuntimeIOException("Failed to open '"+filename+"'", ex); 
++        }
++    }
++
++    /** Lock the file or throw {@link DBOpEnvException} */ 
++    public void lockEx() {
++        lockOperation(NoLockAction.EXCEPTION);
++    }
++    
++    /** Lock the file or wait. */ 
++    public void lockWait() {
++        lockOperation(NoLockAction.EXCEPTION);
++    }
++    
++    /** Lock a file, return true on success else false. */
++    public boolean tryLock() {
++        return lockOperation(NoLockAction.RETURN);
++    }
++
++    /** Release the lock - this must be paired with a "lock" operation. */
++    public void unlock() {
++        synchronized(sync) {
++            try {
++                fileLock.release();
++            } catch (IOException ex) { throw new RuntimeIOException("Failed to unlock '"+filepath+"'", ex); }
++        }
++    }
++
++    /** Return true if this process holds the lock. */
++    public boolean isLockedHere() {
++        if ( fileLock == null )
++            return false;
++        return fileLock.isValid();
++    }
++    
++    public Path getPath() {
++        return filepath;
++    }
++    
++    /** Take the lock.
++     * <p>
++     * Write our PID into the file and return true if it succeeds.
++     * <p>
++     * Try to get the existing PID if it fails.
++     */
++    private boolean lockOperation(NoLockAction action) {
++        synchronized(sync) {
++            if ( fileLock != null )
++                throw new AlreadyLocked("Failed to get a lock: file='"+filepath+"': Lock already held");
++            
++            try {
++                fileLock = (action == NoLockAction.WAIT) ? fileChannel.lock() : fileChannel.tryLock();
++                if ( fileLock == null ) {
++                    if ( action == NoLockAction.RETURN ) 
++                        return false ;
++                    // Read without the lock.
++                    // This isn't perfect but it is only providing helpful information.
++                    int pid = readProcessId(-99);
++                    if ( pid >= 0 )
++                        throw new DBOpEnvException("Failed to get a lock: file='"+filepath+"': held by process "+pid);
++                    throw new DBOpEnvException("Failed to get a lock: file='"+filepath+"': failed to get the holder's process id");
++                }
++                // Got the lock. Record our process id.
++                int pid = ProcessUtils.getPid(-1);
++                writeProcessId(pid);
++                return true;
++            } catch (IOException ex) {
++                if ( action == NoLockAction.RETURN ) 
++                    return false;
++                throw new DBOpEnvException("Failed to get a lock: file='"+filepath+"'", ex);
++            }
++        }
++    }
++    
++    // I/O for a process id. 
++    /** Read the file to get a process id */
++    private int readProcessId(int dft) throws IOException {
++        ByteBuffer bb = ByteBuffer.allocate(128);
++        fileChannel.position(0);
++        int len = fileChannel.read(bb);
++        fileChannel.position(0);
++        if ( len == 0 )
++            return dft;
++        if ( len == 128 )
++            // Too much.
++            return dft;
++        // Bug in Jena 3.3.0
++        //byte[] b = ByteBufferLib.bb2array(bb, 0, len);
++        bb.flip();
++        byte[] b = new byte[len];
++        bb.get(b);
++        
++        String pidStr = StrUtils.fromUTF8bytes(b);
++        // Remove all leadign and trailing (vertical and horizontal) whitespace.
++        pidStr = pidStr.replaceAll("[\\s\\t\\n\\r]+$", "");
++        pidStr = pidStr.replaceAll("^[\\s\\t\\n\\r]+", "");
++        try {
++            // Process id.
++            return Integer.parseInt(pidStr);
++        } catch (NumberFormatException ex) {
++            ex.printStackTrace();
++            Log.warn(this, "Bad process id: file='"+filepath+"': read='"+pidStr+"'");
++            return dft; 
++        }
++    }
++
++    /** Write the process id to the file. */
++    private void writeProcessId(int pid) throws IOException {
++        byte b[] = StrUtils.asUTF8bytes(Integer.toString(pid)+"\n");
++        fileChannel.truncate(0);
++        int len = fileChannel.write(ByteBuffer.wrap(b));
++        fileChannel.position(0);
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/SegmentedMemBuffer.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/SegmentedMemBuffer.java
index 0000000,0000000..abe06fd
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/file/SegmentedMemBuffer.java
@@@ -1,0 -1,0 +1,279 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++import java.nio.ByteBuffer ;
++import java.util.ArrayList ;
++import java.util.List ;
++
++import org.apache.jena.atlas.io.IO ;
++import org.apache.jena.atlas.lib.ByteBufferLib ;
++import org.apache.jena.atlas.logging.FmtLog ;
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++/** A segmented, expanding buffer of bytes.
++ *  This class does not copy the underlying bytes when the file grows.
++ *  Hence, the performance is more predictable.
++ *  (Resizing a fixed size buffer is a copy c.f. performance issues with
++ *  {@code ArrayList} or {@code ByteArrayOutputStream}
++ *  as they go from small to large. 
++ */
++public class SegmentedMemBuffer {
++    private static Logger log = LoggerFactory.getLogger(SegmentedMemBuffer.class) ;
++    
++    // See java.nio.channels.FileChannel
++    private static int DFT_CHUNK = 1024*1024 ;
++    private static int DFT_SEGMENTS = 1000 ;
++    private final int CHUNK ;
++    // The file pointer and marker of the allocated end point.
++    private long dataLength = 0 ;
++    private List<byte[]> space = null ;
++    private boolean isOpen ;
++    private final boolean TRACKING = false ;
++
++    public SegmentedMemBuffer() { this(DFT_CHUNK) ; }
++    
++    public SegmentedMemBuffer(int chunk) { 
++        this.CHUNK = chunk ;
++        space = new ArrayList<>(DFT_SEGMENTS) ;
++        // "Auto open"
++        isOpen = true ;
++    }
++
++    public void open() {
++        isOpen = true ;
++//        if ( storage != null )
++//            throw new RuntimeIOException("Already open") ;
++//        storage = new ArrayList<>(DFT_SEGMENTS) ;
++    }
++    
++    public boolean isOpen() {
++        return space != null ;
++    }
++    
++    public int read(long posn, ByteBuffer bb) {
++        if ( TRACKING )
++            log("read<<%s", ByteBufferLib.details(bb));
++        checkOpen() ;
++        checkPosition(posn) ;
++        int len = bb.remaining() ;
++        if ( posn+dataLength > dataLength )
++            len = (int)(dataLength-posn) ;
++        arrayCopyOut(space, posn, bb);
++        return len ;
++    }
++
++    public int read(long posn, byte[] b) {
++        if ( TRACKING )
++            log("read<<[%d]", b.length) ;
++        return read$(posn, b, 0, b.length) ; 
++    }
++    
++    public int read(long posn, byte[] b, int start, int length) {
++        if ( TRACKING )
++            log("read<<[%d],%s,%s", b.length, start, length) ;
++        return read$(posn, b, start, length) ;
++    }
++    
++    private int read$(long posn, byte[] b, int start, int length) {
++        checkOpen() ;
++        if ( posn >= dataLength ) 
++            return -1 ;
++        checkPosition(posn) ;
++        if ( length == 0 )
++            return 0 ;
++        checkByteArray(b, start, length) ;
++        int len = length ;
++        if ( posn+length > dataLength )
++            len = (int)(dataLength-posn) ;
++        arrayCopyOut(space, posn, b, start, len) ;
++        return len ;
++    }
++
++    public void write(long posn, ByteBuffer bb ) {
++        if ( TRACKING )
++            log("read<<%s", ByteBufferLib.details(bb));
++        checkOpen() ;
++        if ( posn != dataLength )
++            checkPosition(posn) ;
++        arrayCopyIn(bb, space, posn) ;
++    }
++
++    public void write(long posn, byte[] b ) {
++        if ( TRACKING )
++            log("read<<[%d]", b.length) ;
++        write$(posn, b, 0, b.length) ; 
++    }
++
++    public void write(long posn, byte[] b, int start, int length) {
++        if ( TRACKING )
++            log("read<<[%d],%d,%d", b.length, start, length) ;
++        write$(posn, b, start, length);
++    }
++    
++    private void write$(long posn, byte[] b, int start, int length) {
++        checkOpen() ;
++        checkPosition(posn) ;
++        if ( length == 0 )
++            return ;
++        checkByteArray(b,start,length) ;
++        arrayCopyIn(b, start, space, dataLength, length) ;
++    }
++
++    public void truncate(long length) {
++        if ( TRACKING )
++            log("truncate(%d)", length) ;
++        if ( length < 0 )
++            IO.exception(String.format("truncate: bad length : %d", length)) ;
++        checkOpen() ;
++        dataLength = Math.min(dataLength, length) ;
++        // clear above?
++    }
++
++    public void sync() {
++        checkOpen() ;
++    }
++
++    public void close() {
++        if ( ! isOpen() )
++            return ;
++        isOpen = false ;
++        space.clear() ;
++        space = null ;
++    }
++
++    public long length() {
++        return dataLength ;
++    }
++
++    private void checkOpen() {
++        if ( ! isOpen )
++            IO.exception("Not open") ;
++    }
++
++    private void checkPosition(long posn) {
++        // Allows posn to be exactly the byte beyond the end  
++        if ( posn < 0 || posn > dataLength )
++            IO.exception(String.format("Position out of bounds: %d in [0,%d]", posn, dataLength)) ;
++    }
++
++    private void checkByteArray(byte[] b, int start, int length) {
++        if ( start < 0 || start >= b.length )
++            IO.exception(String.format("Start point out of bounds of byte array: %d in [0,%d)", start, b.length)) ;
++        if ( length < 0 || start+length > b.length )
++            IO.exception(String.format("Start/length out of bounds of byte array: %d/%d in [0,%d)", start, length, b.length)) ;
++    }
++    
++    private int getSegment(long posn) { return (int)(posn / CHUNK) ; }
++
++    private int getOffset(long posn) { return (int) (posn % CHUNK) ; }
++
++    // c.f. System.arrayCopy 
++    private void arrayCopyIn(byte[] src, final int srcStart, List<byte[]> dest, final long destStart, final int length) {
++        if ( length == 0 )
++            return ;
++        int len = length ;
++        int srcPosn = srcStart ;
++        int seg = getSegment(destStart) ;
++        int offset = getOffset(destStart) ;
++        while( len > 0 ) {
++            int z = Math.min(len, CHUNK-offset) ;
++            // Beyond end of file?
++            if ( seg >= dest.size() ) {
++                byte[] buffer = new byte[CHUNK] ;
++                space.add(buffer) ;
++            }
++            System.arraycopy(src, srcPosn, dest.get(seg), offset, z);
++            srcPosn += z ;
++            len -= z ;
++            seg += 1 ;
++            offset += z ;
++            offset %= CHUNK ;
++        }
++        dataLength = Math.max(dataLength, destStart+length) ;
++    }
++
++    private void arrayCopyOut(List<byte[]> src, final long srcStart, byte[] dest, final int destStart, final int length) {
++        int len = length ;
++        len = Math.min(len, (int)(dataLength-srcStart)) ;
++        int dstPosn = destStart ;
++        int seg = getSegment(srcStart) ;
++        int offset = getOffset(srcStart) ;
++        while( len > 0 ) {
++            int z = Math.min(len, CHUNK-offset) ;
++            System.arraycopy(src.get(seg), offset, dest, dstPosn, z);
++            dstPosn += z ;
++            len -= z ;
++            seg += 1 ;
++            offset += z ;
++            offset %= CHUNK ;
++        }
++    }
++
++    private void arrayCopyIn(ByteBuffer bb, List<byte[]> dest, long destStart) {
++        if ( bb.remaining() == 0 )
++            return ;
++        int length = bb.remaining() ;
++        int len = bb.remaining() ;
++        int srcPosn = bb.position() ;
++        int seg = getSegment(destStart) ;
++        int offset = getOffset(destStart) ;
++        while( len > 0 ) {
++            int z = Math.min(len, CHUNK-offset) ;
++            // Beyond end of file?
++            if ( seg >= dest.size() ) {
++                byte[] buffer = new byte[CHUNK] ;
++                space.add(buffer) ;
++            }
++            byte[] bytes = dest.get(seg) ;
++            bb.get(bytes, offset, z) ;
++            srcPosn += z ;
++            len -= z ;
++            seg += 1 ;
++            offset += z ;
++            offset %= CHUNK ;
++        }
++        dataLength = Math.max(dataLength, destStart+length) ;
++    }
++
++    private void arrayCopyOut(List<byte[]> src, long srcStart, ByteBuffer bb) {
++        int len = bb.remaining() ;
++        len = Math.min(len, (int)(dataLength-srcStart)) ;
++        int dstPosn = bb.position() ;
++        int seg = getSegment(srcStart) ;
++        int offset = getOffset(srcStart) ;
++        while( len > 0 ) {
++            int z = Math.min(len, CHUNK-offset) ;
++            byte[] bytes = src.get(seg) ;
++            bb.put(bytes, offset, z) ;
++            dstPosn += z ;
++            len -= z ;
++            seg += 1 ;
++            offset += z ;
++            offset %= CHUNK ;
++        }
++    }
++    
++    private void log(String fmt, Object... args) {
++        if ( TRACKING )
++            FmtLog.debug(log, fmt, args); 
++    }
++
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/page/BlockConverter.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/page/BlockConverter.java
index 0000000,0000000..d77165c
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/page/BlockConverter.java
@@@ -1,0 -1,0 +1,34 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.page;
++
++import org.seaborne.dboe.base.block.Block ;
++import org.seaborne.dboe.base.block.BlockType ;
++
++/** Convert between Blocks and typed Pages */ 
++public interface BlockConverter<T extends Page>
++{
++    /** Create a T, given an existing Block */
++    public T fromBlock(Block block) ;
++    
++    /** Make a block, given a T */
++    public Block toBlock(T t) ;
++    
++    /** Create a new T from an uninitialized Block */ 
++    public T createFromBlock(Block block, BlockType bType) ;
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/page/Page.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/page/Page.java
index 0000000,0000000..02dca0b
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/page/Page.java
@@@ -1,0 -1,0 +1,44 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.page;
++
++import org.apache.jena.atlas.io.Printable ;
++import org.seaborne.dboe.base.block.Block ;
++
++public interface Page extends Printable
++{
++    public static final int NO_ID   = -1 ;
++    
++    /** Pages are addressed ints (a page ref does in on-disk blocks)
++     * although block are address in longs
++     */  
++    public int getId() ;
++    
++    /** Return a string for display that identifies this Page */
++    public String getRefStr() ;
++    
++    /** Return the block associated with this page */ 
++    public Block getBackingBlock() ;
++    
++    /**
++     * The underlying block for this page has changed (e.g. it's been
++     * promoted and the promotion may have caused the block to change.
++     */
++    public abstract void reset(Block block) ;
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/page/PageBase.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/page/PageBase.java
index 0000000,0000000..ff1f5db
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/page/PageBase.java
@@@ -1,0 -1,0 +1,57 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.page;
++
++import org.seaborne.dboe.DBOpEnvException ;
++import org.seaborne.dboe.base.block.Block ;
++
++/** A page with a byte buffer */
++public abstract class PageBase implements Page
++{
++    private int id ;
++    private Block block ;
++
++    protected PageBase(Block block)
++    {
++        this.block = block ;
++        long x = block.getId() ;
++        if ( x < 0 )
++            throw new DBOpEnvException("Page id is negative: "+x) ;
++        if ( x > Integer.MAX_VALUE )
++            throw new DBOpEnvException("Page id is large than MAX_INT: "+x) ;
++        this.id = block.getId().intValue() ;
++    }
++    
++    @Override
++    final public void reset(Block block2)
++    { 
++//        if ( block2.getId() != id )
++//            Log.warn(this, "Block id changed: "+id+" => "+block2.getId()) ;
++        id = block2.getId().intValue() ;
++        _reset(block2) ; 
++        this.block = block2 ;
++    } 
++
++    protected abstract void _reset(Block block) ;
++
++    @Override
++    final public Block getBackingBlock()    { return block ; }
++
++    @Override
++    final public int getId()                { return id ; }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/page/PageBlockMgr.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/page/PageBlockMgr.java
index 0000000,0000000..533d6d9
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/page/PageBlockMgr.java
@@@ -1,0 -1,0 +1,212 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.page;
++
++import org.apache.jena.atlas.lib.Closeable ;
++import org.apache.jena.atlas.lib.InternalErrorException ;
++import org.apache.jena.atlas.logging.Log ;
++import org.seaborne.dboe.base.block.Block ;
++import org.seaborne.dboe.base.block.BlockMgr ;
++import org.seaborne.dboe.base.block.BlockType ;
++
++/** Engine that wraps from blocks to typed pages. */
++
++public class PageBlockMgr<T extends Page> implements Closeable
++{
++    protected final BlockMgr blockMgr ;
++    protected BlockConverter<T> pageFactory ;
++
++    protected PageBlockMgr(BlockConverter<T> pageFactory, BlockMgr blockMgr) { 
++        this.pageFactory = pageFactory ;
++        this.blockMgr = blockMgr ;
++    }
++   
++    // Sometimes, the subclass must pass null to the constructor then call this. 
++    protected void setConverter(BlockConverter<T> pageFactory) { this.pageFactory = pageFactory ; }
++    
++    public BlockMgr getBlockMgr()   { return blockMgr ; } 
++    
++    public long allocLimit()        { return blockMgr.allocLimit() ; }
++
++    public void resetAlloc(long boundary) {
++        blockMgr.resetAlloc(boundary) ;
++    }
++
++    /** Allocate a new thing */
++    public T create(BlockType bType) {
++        Block block = blockMgr.allocate(-1) ;
++        block.setModified(true) ;
++        T page = pageFactory.createFromBlock(block, bType) ;
++        return page ;
++    }
++    
++    /**
++     * Fetch a block for reading.
++     * @param id Block to fetch
++     */
++    public T getRead(int id) {
++        return getRead$(id) ;
++    }
++
++    /**
++     * Fetch a block for reading.
++     * 
++     * @param id    Block to fetch
++     * @param referencingId
++     *            Id of block referring to this one. 
++     *            For example, a parent in a tree.
++     *            May be negative for "none" or "meaningless".
++     */
++    public T getRead(int id, int referencingId) {
++        return getRead$(id) ;
++    }
++    
++    /**
++     * Fetch a block for writing.
++     * @param id Block to fetch
++     */
++    public T getWrite(int id) {
++        return getWrite$(id) ;
++    }
++    
++    /**
++     * Fetch a block for writing.
++     * 
++     * @param id  Block to fetch
++     * @param referencingId
++     *            Id of block referring to this one. 
++     *            For example, a parent in a tree.
++     *            May be -1 for "none" or "meaningless".
++     */
++    public T getWrite(int id, int referencingId) {
++        return getWrite$(id) ;
++    }
++
++    // ---- The read and write worker operations.
++    
++    final protected T getRead$(int id) { 
++        Block block = blockMgr.getRead(id) ;
++        // Blocks from the BlockMgrCache may be write-dirty so this test
++        // is wrong in that situation.  It is better to use the block as-is
++        // otherwise we'd have two blocks of a given id with different flags
++        // that confuse the block caching.  
++//        if ( block.isModified() ) {
++//            System.err.println("getRead - isModified - "+blockMgr.getLabel()+"["+id+"]") ;
++////            block = new Block(block.getId(), block.getByteBuffer());
++////            block.setModified(false); 
++//            // Debug.
++//            //blockMgr.getRead(id) ;
++//        }
++        T page = pageFactory.fromBlock(block) ;
++        return page ;
++    }
++    
++    final protected T getWrite$(int id) {
++        Block block = blockMgr.getWrite(id) ;
++        block.setReadOnly(false) ;
++        T page = pageFactory.fromBlock(block) ;
++        return page ;
++    }
++
++    // ---- 
++    
++    public void put(T page) {
++        write(page) ;
++        release(page) ;
++    }
++
++    public void write(T page) {
++        Block blk = pageFactory.toBlock(page) ;
++        blockMgr.write(blk) ;
++    }
++
++    public void release(Page page) {
++        Block block = page.getBackingBlock() ;
++        blockMgr.release(block) ;
++    }
++
++    private void warn(String string) {
++        Log.warn(this, string) ;
++    }
++
++    public void free(Page page) {
++        Block block = page.getBackingBlock() ;
++        blockMgr.free(block) ;
++    }
++
++    
++    /** Promote a page to be writable in-place (block id does not change, hnce page does not change id). */
++    public void promoteInPlace(Page page) {
++        Block block = page.getBackingBlock() ;
++        block.getByteBuffer().rewind() ;
++        Block block2 = blockMgr.promote(block) ; 
++        block2.setReadOnly(false) ;
++        if ( block2.getId() != block.getId() )
++            throw new InternalErrorException("Block id changed") ;
++        if ( block2 == block )
++            return ;
++        // Change - reset Block in page.
++        // The details should not have changed.
++        // page.reset(block2) ;
++    }
++    
++    /** Promote a page - return 'true' if the block changed (.reset()) will have been called */ 
++    public boolean promoteDuplicate(Page page) {
++        Block block = page.getBackingBlock() ;
++        block.getByteBuffer().rewind() ;
++        
++        // --- TODO Always new
++        Block block2 =  blockMgr.allocate(-1) ;
++        block2.getByteBuffer().put(block.getByteBuffer()) ;
++        block2.getByteBuffer().rewind() ;
++        block2.setReadOnly(false) ;
++
++        if ( block2 == block )
++            return false ;
++        // Change - reset Block in page.
++        page.reset(block2) ;
++        return true ;
++    }
++
++    public boolean valid(int id) {
++        return blockMgr.valid(id) ;
++    }
++
++    public void dump() {
++        for ( int idx = 0 ; valid(idx) ; idx++ ) {
++            T page = getRead(idx, -1) ;
++            System.out.println(page) ;
++            release(page) ;
++        }
++    }
++
++    /** Signal the start of an update operation */
++    public void startUpdate()       { blockMgr.beginUpdate() ; }
++    
++    /** Signal the completion of an update operation */
++    public void finishUpdate()      { blockMgr.endUpdate() ; }
++
++    /** Signal the start of an update operation */
++    public void startRead()         { blockMgr.beginRead() ; }
++    
++    /** Signal the completeion of an update operation */
++    public void finishRead()        { blockMgr.endRead() ; }
++
++    @Override
++    public void close()             { blockMgr.close(); }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/record/Record.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/record/Record.java
index 0000000,0000000..869e705
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/record/Record.java
@@@ -1,0 -1,0 +1,188 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.record;
++
++import static java.lang.String.format ;
++
++import java.util.Arrays ;
++
++import org.apache.jena.atlas.lib.Bytes ;
++import org.seaborne.dboe.sys.SystemIndex ;
++
++/** A record is pair of key and value.  It may be all key, in which case value is null. */
++
++final
++public class Record //implements Comparable<Record>
++{
++    /*
++     * Records of fixed size (controlled by the factory).
++     */
++    public static final Record NO_REC = null ;
++    
++    final private byte[] key ;
++    final private byte[] value ;
++    
++    public Record(byte[] key, byte[] value)
++    { 
++        this.key = key ;
++        this.value = value ;
++        if ( SystemIndex.Checking )
++        {
++            if ( value != null && value.length == 0 )
++                throw new RecordException("Zero length value") ;
++        }
++    }
++    
++    public byte[] getKey()          { return key ; }
++    public byte[] getValue()        { return value ; }
++
++    public boolean hasSeparateValue() { return value!=null ; }
++    
++    @Override
++    public int hashCode()
++    { 
++        return Arrays.hashCode(key) ^ Arrays.hashCode(value) ;
++    } 
++
++    @Override
++    public boolean equals(Object other)
++    {
++        if ( this == other ) return true ;
++        if ( ! ( other instanceof Record ) ) return false ;
++        Record r = (Record)other ;
++        return compareByKeyValue(this, r) == 0 ;
++    }
++    
++    @Override
++    public String toString()
++    {
++        if ( value == null )
++            return str(key) ;
++        return str(key)+":"+str(value) ;
++    }
++    
++    /** Is the key of record1 == key of record2 */
++    public static boolean keyEQ(Record record1, Record record2)
++    {
++        int x = compareByKey(record1, record2) ;
++        return x == 0 ;
++    }
++
++    /** Is the key of record1 != key of record2 */
++    public static boolean keyNE(Record record1, Record record2)
++    {
++        int x = compareByKey(record1, record2) ;
++        return x != 0 ;
++    }
++
++    /** Is the key of record1 < key of record2 */
++    public static boolean keyLT(Record record1, Record record2)
++    {
++        int x = compareByKey(record1, record2) ;
++        return x < 0 ;
++    }
++
++    /** Is the key of record1 <= key of record2 */
++    public static boolean keyLE(Record record1, Record record2)
++    {
++        int x = compareByKey(record1, record2) ;
++        return x <= 0 ;
++    }
++    
++    /** Is the key of record1 >= key of record2 */
++    public static boolean keyGE(Record record1, Record record2)
++    {
++        int x = compareByKey(record1, record2) ;
++        return x >= 0 ;
++    }
++    
++    /** Is the key of record1 > key of record2 */
++    public static boolean keyGT(Record record1, Record record2)
++    {
++        int x = compareByKey(record1, record2) ;
++        return x > 0 ;
++    }
++    
++    /** Is (key, value) of record1 equal to (key,value) of record2 */
++    public static boolean equals(Record record1, Record record2)
++    {
++        int x = compareByKeyValue(record1, record2) ;
++        return x == 0 ;
++    }
++
++
++    
++    static public String str(byte[] b)
++    {
++        StringBuilder str = new StringBuilder() ;
++        for ( byte aB : b )
++        {
++            str.append( format( "%02x", aB ) );
++        }
++        return str.toString() ;
++    }
++    
++    public static int compareByKey(Record record1, Record record2)
++    {
++        checkKeyCompatible(record1, record2) ;
++        return Bytes.compare(record1.key, record2.key) ; 
++    }
++    
++    public static int compareByKeyValue(Record record1, Record record2)
++    {
++        checkCompatible(record1, record2) ;
++        int x = Bytes.compare(record1.key, record2.key) ;
++        if ( x == 0 )
++        {
++            if ( record1.value != null )
++                x = Bytes.compare(record1.value, record2.value) ;
++        }
++        return x ;
++    }
++
++    static void checkCompatible(Record record1, Record record2)
++    {
++        if ( ! compatible(record1, record2, true) )
++            throw new RecordException(format("Incompatible: %s, %s", record1, record2)) ;
++    }
++    
++    static void checkKeyCompatible(Record record1, Record record2)
++    {
++        if ( ! compatible(record1, record2, false) )
++            throw new RecordException(format("Incompatible: %s, %s", record1, record2)) ;
++    }
++    
++    static boolean compatible(Record record1, Record record2, boolean checkValue)
++    {
++        if ( record1.key.length != record2.key.length )
++            return false ;
++        
++        if ( checkValue )
++        {
++            if ( record1.value == null && record2.value == null )
++                return true ;
++            if ( record1.value == null )
++                return false ;
++            if ( record2.value == null )
++                return false ;
++            if ( record1.value.length != record2.value.length )
++                return false;
++        }
++        return true ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/record/RecordException.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/record/RecordException.java
index 0000000,0000000..7c192c8
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/record/RecordException.java
@@@ -1,0 -1,0 +1,26 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.record;
++
++import org.seaborne.dboe.DBOpEnvException ;
++
++public class RecordException extends DBOpEnvException
++{
++    public RecordException() {}
++    public RecordException(String msg) { super(msg) ; }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/record/RecordFactory.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/record/RecordFactory.java
index 0000000,0000000..1413312
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/record/RecordFactory.java
@@@ -1,0 -1,0 +1,183 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.record;
++
++import static java.lang.String.format;
++
++import java.nio.ByteBuffer;
++
++/** Record creator */
++final
++public class RecordFactory
++{
++    private final int keyLength;
++    private final int valueLength;
++    private final int slotLen;
++    private final boolean checking = false;
++
++    public RecordFactory(int keyLength, int valueLength) {
++        if ( keyLength <= 0 )
++            throw new IllegalArgumentException("Bad key length: " + keyLength);
++        if ( valueLength < 0 )
++            throw new IllegalArgumentException("Bad value length: " + valueLength);
++
++        this.keyLength = keyLength;
++        this.valueLength = valueLength;
++        this.slotLen = keyLength + valueLength;
++    }
++
++    /** Return a RecordFactory that makes key-only records of the same key size */
++    public RecordFactory keyFactory() {
++        return new RecordFactory(keyLength, 0);
++    }
++
++    /** Create a key-only record, allocating blank space for the key */
++    public Record createKeyOnly() {
++        return create(new byte[keyLength], null);
++    }
++
++    /** Create a key-only record */
++    public Record createKeyOnly(Record record) {
++        checkKey(record.getKey());
++        if ( record.getValue() == null )
++            return record;
++
++        return create(record.getKey(), null);
++    }
++
++    /** Create a key and value record (value uninitialized) */
++    public Record create(byte[] key) {
++        checkKey(key);
++        byte[] v = null;
++        if ( valueLength > 0 )
++            v = new byte[valueLength];
++        return create(key, v);
++    }
++
++    /** Create a record, allocating space for the key and value (if any) */
++    public Record create() {
++        return create(new byte[keyLength], (valueLength > 0) ? new byte[valueLength] : null);
++    }
++
++    /** Create a key and value record */
++    public Record create(byte[] key, byte[] value) {
++        check(key, value);
++        return new Record(key, value);
++    }
++
++    public void insertInto(Record record, ByteBuffer bb, int idx) {
++        check(record);
++        bb.position(idx * slotLen);
++        bb.put(record.getKey(), 0, keyLength);
++        if ( hasValue() && record.getValue() != null )
++            bb.put(record.getValue(), 0, valueLength);
++    }
++
++    public static final RecordMapper<Record> mapperRecord = new RecordMapper<Record>() {
++        /*
++         * RecordRangeIterator calls getRecordBuffer().get(slotidx);
++         */
++        @Override
++        public Record map(ByteBuffer bb, int idx, byte[] keyBytes, RecordFactory factory) {
++            byte[] key = new byte[factory.keyLength];
++            byte[] value = (factory.hasValue() ? new byte[factory.valueLength] :null );
++            // Is it better to avoid the synchronize needed for bb.position./bb.get
++            // but not use a (relative) bulk bb.get(byte[],,) which may be a native operation?
++            
++//            int posnKey = idx*slotLen;
++//            // Avoid using position() so we can avoid needing synchronized.
++//            copyInto(key, bb, posnKey, keyLength);
++//            if ( value != null )
++//            {
++//                int posnValue = idx*slotLen+keyLength;
++//                copyInto(value, bb, posnValue, valueLength);
++//            }
++            
++            // Using bb.get(byte[],,) may be potentially faster but requires the synchronized
++            // There's no absolute version.
++            synchronized(bb)
++            {
++                bb.position(idx*factory.slotLen);
++                bb.get(key, 0, factory.keyLength);
++                if ( value != null )
++                    bb.get(value, 0, factory.valueLength);
++            }
++            if ( keyBytes != null )
++                System.arraycopy(key, 0, keyBytes, 0, factory.keyLength);
++            return factory.create(key, value);
++        }
++    };
++    
++    public <X> X access(ByteBuffer bb, int idx, byte[] keyBytes, RecordMapper<X> mapper) {
++        return mapper.map(bb, idx, keyBytes, this); 
++    }
++    
++    public Record buildFrom(ByteBuffer bb, int idx) {
++        return access(bb, idx, null, mapperRecord); 
++    }
++    
++    private final void copyInto(byte[] dst, ByteBuffer src, int start, int length) {
++        // Thread safe.
++        for ( int i = 0; i < length; i++ )
++            dst[i] = src.get(start+i);
++        // Would otherwise be ...
++//        src.position(start);
++//        src.get(dst, 0, length);
++    }
++    
++    public boolean hasValue()   { return valueLength > 0; }
++
++    public int recordLength()   { return keyLength + valueLength; }
++    
++    public int keyLength()      { return keyLength; }
++
++    public int valueLength()    { return valueLength; }
++    
++    @Override
++    public String toString() {
++        return format("<RecordFactory k=%d v=%d>", keyLength, valueLength);
++    }
++
++    private final void check(Record record) {
++        if ( ! checking ) return;
++        check(record.getKey(), record.getValue());
++    }
++
++    private final void checkKey(byte[] k) {
++        if ( ! checking ) return;
++        if ( k == null )
++            throw new RecordException("Null key byte[]");
++        if ( keyLength != k.length )
++            throw new RecordException(format("Key length error: This RecordFactory manages records of key length %d, not %d", keyLength,
++                                             k.length));
++    }
++
++    private final void check(byte[] k, byte[] v) {
++        if ( ! checking ) return;
++        checkKey(k);
++        if ( valueLength <= 0 ) {
++            if ( v != null )
++                throw new RecordException("Value array error: This RecordFactory manages records that are all key");
++        } else {
++            // v == null for a key-only record from this factory.
++            if ( v != null && v.length != valueLength )
++                throw new RecordException(format("This RecordFactory manages record of value length %d, not (%d,-)", valueLength,
++                                                 v.length));
++        }
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/record/RecordMapper.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/record/RecordMapper.java
index 0000000,0000000..34968e2
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/record/RecordMapper.java
@@@ -1,0 -1,0 +1,30 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.record;
++
++import java.nio.ByteBuffer ;
++
++/** Allow bytes to pull directly out of storage with no copy to record;
++ *  also extract the key bytes into an array.
++ *  This operation MUST not retain any references to the ByteBuffer storage space.
++ *  All in order to avoid remapping Records to higher level objects,
++ *  which churns objects (GC issue) and avoids a copy.
++ */
++public interface RecordMapper<X> {
++    X map(ByteBuffer bb, int entryIdx, byte key[], RecordFactory recFactory) ;
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/recordbuffer/RecordBufferPage.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/recordbuffer/RecordBufferPage.java
index 0000000,0000000..84b251d
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/recordbuffer/RecordBufferPage.java
@@@ -1,0 -1,0 +1,100 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.recordbuffer;
++
++import org.seaborne.dboe.base.block.Block ;
++import org.seaborne.dboe.base.page.Page ;
++import org.seaborne.dboe.base.record.RecordFactory ;
++import org.seaborne.dboe.sys.Sys ;
++
++/**
++ * B+Tree records nodes and hash buckets.
++ * Add link field to a RecordBufferPageBase
++ */
++
++public final class RecordBufferPage extends RecordBufferPageBase
++{
++    // Why not straight to BPlusTreeRecords?
++    // 1 - this may be useful in its own right as a sequence of records on-disk.
++    // 2 - BPlusTreeRecords inherits from BPlusTreePage
++    
++    
++    // To Constants
++    // Offsets
++    //    final public static int COUNT      = 0 ;
++    // Adds this field over RecordBufferPageBase
++    final public static int LINK            = 4 ;
++    final private static int FIELD_LENGTH   = Sys.SizeOfInt ; // Length of the space needed here (not count)
++
++    private int link = Page.NO_ID ;
++    
++    public final int getLink() { return link ; }
++    
++    public void setLink(int link)
++    { 
++        this.link = link ;
++        getBackingBlock().getByteBuffer().putInt(LINK, link) ;
++    }
++    
++    @Override
++    protected void _reset(Block block)
++    { 
++        // TODO -- should this be format?
++        // Print this 
++        super.rebuild(block, this.getCount()) ;
++        //?? use .format
++//        // TODO WRONG : block is overlying.
++//        this.link = block.getByteBuffer().getInt(LINK) ;
++    }
++
++    public static int calcRecordSize(RecordFactory factory, int blkSize)
++    { return RecordBufferPageBase.calcRecordSize(factory, blkSize, FIELD_LENGTH) ; }
++    
++    public static int calcBlockSize(RecordFactory factory, int maxRec)
++    { return RecordBufferPageBase.calcBlockSize(factory, maxRec, FIELD_LENGTH) ; }
++    
++    /** The construction methods */
++    public static RecordBufferPage createBlank(Block block,RecordFactory factory)
++    {
++        int count = 0 ;
++        int linkId = NO_ID ;
++        return new RecordBufferPage(block, factory, count, linkId) ;
++    }
++
++    public static RecordBufferPage format(Block block, RecordFactory factory)
++    {
++        int count = block.getByteBuffer().getInt(COUNT) ;
++        int linkId = block.getByteBuffer().getInt(LINK) ;
++        return new RecordBufferPage(block, factory, count, linkId) ;
++    } 
++    
++    private RecordBufferPage(Block block, RecordFactory factory, int count, int linkId)  
++    {
++        super(block, FIELD_LENGTH, factory, count) ;
++        this.link = linkId ;
++    }
++    
++    @Override
++    public String toString()
++    { return String.format("RecordBufferPage[id=%d,link=%d]: %s", getBackingBlock().getId(), getLink(), recBuff) ; }
++
++    @Override
++    public String getRefStr() {
++        return String.format("RecordBufferPage[id=%d]", getBackingBlock().getId()) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/recordbuffer/RecordBufferPageBase.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/recordbuffer/RecordBufferPageBase.java
index 0000000,0000000..9559008
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/recordbuffer/RecordBufferPageBase.java
@@@ -1,0 -1,0 +1,105 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.recordbuffer ;
++
++import java.nio.ByteBuffer ;
++
++import org.apache.jena.atlas.io.IndentedWriter ;
++import org.seaborne.dboe.base.block.Block ;
++import org.seaborne.dboe.base.buffer.RecordBuffer ;
++import org.seaborne.dboe.base.page.PageBase ;
++import org.seaborne.dboe.base.record.RecordFactory ;
++import org.seaborne.dboe.sys.Sys ;
++
++/**
++ * The on-disk form of a block of a single RecordBuffer
++ * (i.e. this is not part of a BTree/BPlusTree branch node).
++ * This must be compatible with B+Tree records nodes and hashbuckets.
++ */
++
++public abstract class RecordBufferPageBase extends PageBase // implements Page
++{
++    // Field offsets
++    final public static int     COUNT        = 0 ;
++    // Length due to this class - subclasses may use more overhead.
++    final private static int    FIELD_LENGTH = Sys.SizeOfInt ;
++
++    protected final int         headerLength ;
++
++    // Interface: "Page" - id, byteBuffer, count
++    protected RecordBuffer      recBuff ;
++    private final RecordFactory factory ;
++
++    // private int offset ; // Bytes of overhead.
++
++    public static int calcRecordSize(RecordFactory factory, int blkSize, int headerOffset) {
++        // Length = X*recordLength + HEADER
++        int x = blkSize - totalOffset(headerOffset) ;
++        return x / factory.recordLength() ;
++    }
++
++    public static int calcBlockSize(RecordFactory factory, int maxRec, int headerOffset) {
++        return totalOffset(headerOffset) + factory.recordLength() * maxRec ;
++    }
++
++    private static int totalOffset(int headerOffset) {
++        return FIELD_LENGTH + headerOffset ;
++    }
++
++    protected RecordBufferPageBase(Block block, int offset, RecordFactory factory, int count) {
++        // This code knows the alignment of the records in the ByteBuffer.
++        super(block) ;
++        this.headerLength = FIELD_LENGTH + offset ; // NB +4 for the count field
++        this.factory = factory ;
++        rebuild(block, count) ;
++    }
++
++    protected void rebuild(Block block, int count) {
++        ByteBuffer bb = block.getByteBuffer() ;
++        bb.clear() ;
++        bb.position(headerLength) ;
++        bb = bb.slice() ;
++        this.recBuff = new RecordBuffer(bb, factory, count) ;
++    }
++
++    public final RecordBuffer getRecordBuffer() {
++        return recBuff ;
++    }
++
++    public final int getCount() {
++        return recBuff.size() ;
++    }
++
++    public final int getMaxSize() {
++        return recBuff.maxSize() ;
++    }
++
++    public void setCount(int count) {
++        recBuff.setSize(count) ;
++    }
++
++    @Override
++    public String toString() {
++        return String.format("RecordBufferPageBase[id=%d]: %s", getBackingBlock().getId(), recBuff) ;
++    }
++
++    @Override
++    public void output(IndentedWriter out) {
++        out.print(toString()) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/recordbuffer/RecordBufferPageMgr.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/recordbuffer/RecordBufferPageMgr.java
index 0000000,0000000..09275a9
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/recordbuffer/RecordBufferPageMgr.java
@@@ -1,0 -1,0 +1,90 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.recordbuffer;
++
++import static org.seaborne.dboe.base.recordbuffer.RecordBufferPage.* ;
++
++import java.nio.ByteBuffer;
++
++import org.seaborne.dboe.base.block.Block ;
++import org.seaborne.dboe.base.block.BlockMgr ;
++import org.seaborne.dboe.base.block.BlockType ;
++import org.seaborne.dboe.base.page.BlockConverter ;
++import org.seaborne.dboe.base.page.PageBlockMgr ;
++import org.seaborne.dboe.base.record.RecordException ;
++import org.seaborne.dboe.base.record.RecordFactory ;
++
++/** Manager for a block that is all records.  
++ *  This must be compatible with B+Tree records nodes and with hash buckets. 
++ */
++
++final
++public class RecordBufferPageMgr extends PageBlockMgr<RecordBufferPage>
++{
++    private final RecordFactory factory ;
++    
++    public RecordBufferPageMgr(RecordFactory factory, BlockMgr blockMgr) {
++        super(new Block2RecordBufferPage(factory), blockMgr) ;
++        this.factory = factory ;
++    }
++
++    public RecordFactory getRecordFactory() { return factory ; }
++    
++    public RecordBufferPage create() {
++        return super.create(BlockType.RECORD_BLOCK) ;
++    }
++    
++    public static class Block2RecordBufferPage implements BlockConverter<RecordBufferPage> {
++        private RecordFactory factory ;
++
++        public Block2RecordBufferPage(RecordFactory factory) {
++            this.factory = factory ;
++        }
++
++        @Override
++        public RecordBufferPage createFromBlock(Block block, BlockType blkType) {
++            if ( blkType != BlockType.RECORD_BLOCK )
++                throw new RecordException("Not RECORD_BLOCK: " + blkType) ;
++            // Initially empty
++            RecordBufferPage rb = RecordBufferPage.createBlank(block, factory) ;
++            return rb ;
++        }
++
++        @Override
++        public RecordBufferPage fromBlock(Block block) {
++            synchronized (block) // [[TxTDB:TODO] needed? Right place?
++            {
++                RecordBufferPage rb = RecordBufferPage.format(block, factory) ;
++                // int count = block.getByteBuffer().getInt(COUNT) ;
++                // int linkId = block.getByteBuffer().getInt(LINK) ;
++                // RecordBufferPage rb = new RecordBufferPage(block, linkId,
++                // factory, count) ;
++                return rb ;
++            }
++        }
++
++        @Override
++        public Block toBlock(RecordBufferPage rbp) {
++            int count = rbp.getRecordBuffer().size() ;
++            ByteBuffer bb = rbp.getBackingBlock().getByteBuffer() ;
++            bb.putInt(COUNT, rbp.getCount()) ;
++            bb.putInt(LINK, rbp.getLink()) ;
++            return rbp.getBackingBlock() ;
++        }
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/recordbuffer/RecordRangeIterator.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/recordbuffer/RecordRangeIterator.java
index 0000000,0000000..92ae3c1
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/base/recordbuffer/RecordRangeIterator.java
@@@ -1,0 -1,0 +1,161 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.recordbuffer;
++
++import static org.apache.jena.atlas.lib.Alg.decodeIndex ;
++
++import java.util.Iterator ;
++import java.util.NoSuchElementException ;
++
++import org.apache.jena.atlas.lib.Bytes ;
++import org.apache.jena.atlas.lib.Closeable ;
++import org.seaborne.dboe.base.StorageException ;
++import org.seaborne.dboe.base.block.BlockException ;
++import org.seaborne.dboe.base.record.Record ;
++import org.seaborne.dboe.base.record.RecordMapper ;
++
++final public
++class RecordRangeIterator<X> implements Iterator<X>, Closeable
++{
++    /** Iterate over a range of fromRec (inclusive) to toRec (exclusive) */
++    public static <X> Iterator<X> iterator(int pageId, Record fromRec, Record toRec, RecordBufferPageMgr pageMgr, RecordMapper<X> mapper)
++    {
++        if ( ! pageMgr.valid(pageId) ) {
++            String msg = "RecordRangeIterator.iterator -- No such block (pageId="+pageId+", fromRec="+fromRec+", toRec="+toRec+ ")" ;
++//            System.err.println(msg) ;
++//            System.exit(0) ;
++            throw new BlockException(msg) ;
++        }
++        return new RecordRangeIterator<>(pageId, fromRec, toRec, pageMgr, mapper) ;
++    }
++
++    private final RecordMapper<X> mapper ;
++    
++    private RecordBufferPage currentPage ;      // Set null when finished.
++    
++    private int currentIdx ;
++    private final RecordBufferPageMgr pageMgr ;
++    private final Record maxRec ;
++    private final Record minRec ;
++    
++    private X slot = null ;
++    private final byte[] keySlot ;
++
++    private long countRecords = 0 ;
++    private long countBlocks = 0 ;
++
++    private RecordRangeIterator(int id, Record fromRec, Record toRec, RecordBufferPageMgr pageMgr, RecordMapper<X> mapper) {
++        currentIdx = 0 ;
++        this.pageMgr = pageMgr;
++        this.minRec = fromRec ;
++        this.maxRec = toRec ;
++        this.mapper = mapper ;
++        this.keySlot = new byte[pageMgr.getRecordFactory().keyLength()] ;
++        
++        if ( toRec != null && fromRec != null && Record.keyLE(toRec, fromRec) ) {
++            currentPage = null ;
++            return ;
++        }
++
++        pageMgr.startRead();
++        currentPage = pageMgr.getRead(id) ;
++        
++        if ( currentPage.getCount() == 0 ) {
++            // Empty page.
++            close() ;
++            return ;
++        }
++
++        if ( fromRec != null ) {
++            currentIdx = currentPage.getRecordBuffer().find(fromRec) ;
++            if ( currentIdx < 0 )
++                currentIdx = decodeIndex(currentIdx) ;
++        }        
++    }
++
++    @Override
++    public boolean hasNext() {
++        if ( slot != null )
++            return true ;
++        if ( currentPage == null )
++            return false ;
++        // Set slot.
++        while (currentIdx >= currentPage.getCount()) {
++            // Move to next.
++            int link = currentPage.getLink() ;
++            if ( link < 0 ) {
++                close() ;
++                return false ;
++            }
++
++            if ( currentPage != null )
++                pageMgr.release(currentPage) ;
++
++            RecordBufferPage nextPage = pageMgr.getRead(link) ;
++            // Check currentPage -> nextPage is strictly increasing keys.
++            if ( false ) {
++                Record r1 = currentPage.getRecordBuffer().getHigh() ;
++                Record r2 = nextPage.getRecordBuffer().getLow() ;
++                if ( Record.keyGE(r1, r2) )
++                    throw new StorageException("RecordRangeIterator: records not strictly increasing: " + r1 + " // " + r2) ;
++            }
++            currentPage = nextPage ;
++            countBlocks++ ;
++            currentIdx = 0 ;
++        }
++
++        slot = currentPage.getRecordBuffer().access(currentIdx, keySlot, mapper) ;
++        currentIdx++ ;
++
++        if ( maxRec != null && Bytes.compare(keySlot, maxRec.getKey()) >= 0 ) {
++            close() ;
++            return false ;
++        }
++
++        if ( slot == null ) {
++            close() ;
++            return false ;
++        }
++        countRecords++ ;
++        return true ;
++    }
++
++    @Override
++    public void close() {
++        if ( currentPage == null )
++            return ;
++        pageMgr.release(currentPage) ;
++        pageMgr.finishRead();
++        currentPage = null ;
++        currentIdx = -99 ;
++        slot = null ;
++    }
++
++    @Override
++    public X next() {
++        if ( !hasNext() )
++            throw new NoSuchElementException() ;
++        X x = slot ;
++        slot = null ;
++        return x ;
++    }
++
++    final public long getCountRecords()     { return countRecords ; }
++
++    final public long getCountBlocks()      { return countBlocks ; }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/jenax/Txn.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/jenax/Txn.java
index 0000000,0000000..5f7ea6a
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/jenax/Txn.java
@@@ -1,0 -1,0 +1,41 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.jenax;
++
++import java.util.function.Supplier ;
++
++import org.apache.jena.sparql.core.Transactional ;
++
++/** Indirection to allow for modification for TDB2 capabilities */
++public class Txn {
++    public static <T extends Transactional> void executeRead(T txn, Runnable r) {
++        org.apache.jena.system.Txn.executeRead(txn, r);
++    }
++
++    public static <T extends Transactional, X> X calculateRead(T txn, Supplier<X> r) {
++        return org.apache.jena.system.Txn.calculateRead(txn, r);
++    }
++
++    public static <T extends Transactional> void executeWrite(T txn, Runnable r) {
++        org.apache.jena.system.Txn.executeWrite(txn, r);
++    }
++
++    public static <T extends Transactional, X> X calculateWrite(T txn, Supplier<X> r) {
++        return org.apache.jena.system.Txn.calculateWrite(txn, r);
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/migrate/L.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/migrate/L.java
index 0000000,0000000..e4e709f
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/migrate/L.java
@@@ -1,0 -1,0 +1,206 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.migrate;
++
++import java.io.IOException ;
++import java.io.OutputStream ;
++import java.io.OutputStreamWriter ;
++import java.io.Writer ;
++import java.nio.ByteBuffer ;
++import java.nio.charset.StandardCharsets ;
++import java.util.UUID ;
++import java.util.concurrent.* ;
++import java.util.concurrent.locks.Lock ;
++import java.util.function.Supplier ;
++
++import org.apache.jena.atlas.io.IO ;
++import org.apache.jena.atlas.lib.Bytes ;
++import org.apache.jena.atlas.lib.StrUtils ;
++import org.apache.jena.shared.uuid.JenaUUID ;
++
++/** Misc class */
++public class L {
++    
++    // Not to be confused with UUID.nameUUIDFromBytes (a helper for version 3 UUIDs)
++    /**
++     * Java UUID to bytes (most significant first)
++     */
++    public static byte[] uuidAsBytes(UUID uuid) {
++        return uuidAsBytes(uuid.getMostSignificantBits(), uuid.getLeastSignificantBits()) ;
++    }
++    
++    /**
++     * Jena UUID to bytes (most significant first)
++     */
++    public static byte[] uuidAsBytes(JenaUUID uuid) {
++        return uuidAsBytes(uuid.getMostSignificantBits(), uuid.getLeastSignificantBits()) ;
++    }
++    
++    /** UUID, as two longs, as bytes */ 
++    public static byte[] uuidAsBytes(long mostSignificantBits, long leastSignificantBits) {
++        byte[] bytes = new byte[16] ;
++        Bytes.setLong(mostSignificantBits, bytes, 0); 
++        Bytes.setLong(leastSignificantBits, bytes, 8);
++        return bytes ;
++    }
++    
++    /** A UUID string to bytes */ 
++    public static byte[] uuidAsBytes(String str) {
++        return uuidAsBytes(UUID.fromString(str)) ;
++    }
++    
++    public static String uuidToString(long mostSignificantBits, long leastSignificantBits) {
++        return new UUID(mostSignificantBits, leastSignificantBits).toString() ;
++        //JenaUUID.toString(mostSignificantBits, leastSignificantBits)
++    }
++    
++    private static ExecutorService executor = Executors.newCachedThreadPool() ; 
++    
++    /**
++     * Run asynchronously on another thread ; the thread has started
++     * when this function returns.
++     */
++    public static void async(Runnable r) {
++        Semaphore semaStart = new Semaphore(0, true) ;
++        Runnable r2 = () -> {
++            semaStart.release(1) ;
++            r.run();
++        } ;
++        executor.execute(r2);
++        semaStart.acquireUninterruptibly();
++    }
++    
++    /** Run synchronously but on another thread. */
++    public static void syncOtherThread(Runnable r) {
++        runCallable(()->{
++            r.run();
++            return null ;
++        }) ;
++    }
++
++    /** Run synchronously but on another thread. */
++    public static <T> T syncCallThread(Supplier<T> r) {
++        return runCallable(() -> {
++            T t = r.get() ;
++            return t ;
++        }) ;
++    }
++    
++    private static <T> T runCallable(Callable<T> action) {
++        try { return executor.submit(action).get() ; }
++        catch (Exception e) {
++            e.printStackTrace();
++            return null ;
++        }
++    }
++    
++
++    /** Execute. Preform the "before" action, then main action.
++     *  Always call the "after" runnable if the "bafore" succeeded.
++     *  Becareful about argument order. 
++     * @param action
++     * @param before
++     * @param after
++     */
++    public static void withBeforeAfter(Runnable action, Runnable before, Runnable after) {
++        before.run(); 
++        try { action.run(); }
++        finally { after.run();  }
++    }
++
++    /** Execute. Preform the "before" action, then main action.
++     *  Always call the "after" runnable if the "bafore" succeeded.
++     *  Becareful about argument order. 
++     * @param action
++     * @param before
++     * @param after
++     */
++    public static <V> V callWithBeforeAfter(Supplier<V> action, Runnable before, Runnable after) {
++        before.run(); 
++        try { return action.get() ; } 
++        finally { after.run();  }
++    }
++
++    /** Execute; always call the "after" runnable */
++    public static void withAfter(Runnable action, Runnable after) {
++        try { action.run(); } 
++        finally { after.run();  }
++    }
++
++    /** Execute and return a value; always call the "after" runnable */
++    public static <V> V callWithAfter(Supplier<V> action, Runnable after) {
++        try { return action.get() ; }
++        finally { after.run();  }
++    }
++
++    /** Run inside a Lock */
++    public static  <V> V callWithLock(Lock lock, Supplier<V> r) {
++        return callWithBeforeAfter(r, ()->lock.lock(), ()->lock.unlock()) ;  
++    }
++    
++    /** Run inside a Lock */
++    public static void withLock(Lock lock, Runnable r) {
++        withBeforeAfter(r, ()->lock.lock(), ()->lock.unlock()) ;
++    }
++    
++    // ==> IO.writeWholeFileAsUTF8
++    
++    /** Write a string to a file as UTF-8. The file is closed after the operation.
++     * @param filename
++     * @param content String to be writtem
++     * @throws IOException
++     */
++    
++    public static void writeStringAsUTF8(String filename, String content) throws IOException {
++        try ( OutputStream out = IO.openOutputFileEx(filename) ) {
++            writeStringAsUTF8(out, content) ;
++            out.flush() ;
++        }
++    }
++
++    /** Read a whole stream as UTF-8
++     * 
++     * @param out       OutputStream to be read
++     * @param content   String to be written
++     * @throws  IOException
++     */
++    public static void writeStringAsUTF8(OutputStream out, String content) throws IOException {
++        Writer w = new OutputStreamWriter(out, StandardCharsets.UTF_8) ;
++        w.write(content);
++        w.flush();
++        // Not close.
++    }
++
++    // ==> IO.writeWholeFileAsUTF8
++    
++    /** String to ByteBuffer */
++    public static ByteBuffer stringToByteBuffer(String str) {
++        byte[] b = StrUtils.asUTF8bytes(str) ;
++        return ByteBuffer.wrap(b) ;
++    }
++    
++    /** ByteBuffer to String */
++    public static String byteBufferToString(ByteBuffer bb) {
++        byte[] b = new byte[bb.remaining()] ;
++        bb.get(b) ;
++        return StrUtils.fromUTF8bytes(b) ;
++    }
++
++
++}
++


[37/50] [abbrv] jena git commit: JENA-1396: Merge //github/afs/mantis as subdirectory jena-db/

Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TestJournal.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TestJournal.java
index 0000000,0000000..6852d85
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TestJournal.java
@@@ -1,0 -1,0 +1,149 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction;
++
++import static org.junit.Assert.assertEquals ;
++import static org.junit.Assert.assertFalse ;
++import static org.junit.Assert.assertNotEquals ;
++import static org.junit.Assert.assertNotNull ;
++import static org.junit.Assert.assertTrue ;
++import static org.junit.Assert.fail ;
++
++import java.nio.ByteBuffer ;
++import java.util.Arrays ;
++import java.util.Iterator ;
++import java.util.List ;
++
++import org.apache.jena.atlas.lib.ByteBufferLib ;
++import org.junit.Test ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.dboe.transaction.txn.ComponentId ;
++import org.seaborne.dboe.transaction.txn.ComponentIds ;
++import org.seaborne.dboe.transaction.txn.journal.Journal ;
++import org.seaborne.dboe.transaction.txn.journal.JournalEntry ;
++import org.seaborne.dboe.transaction.txn.journal.JournalEntryType ;
++
++/** Journal tests spearate from the transaction coordinator */  
++public class TestJournal {
++    // For testing recovery, we need something to recover!
++    // See tests in TestRecovery in dboe-trans-data 
++    
++    @Test public void journal_01() {
++        Journal jrnl = Journal.create(Location.mem()) ;
++        assertNotNull(jrnl) ;
++        assertTrue(jrnl.isEmpty()) ;
++    }
++    
++    @Test public void journal_02() {
++        Journal jrnl = Journal.create(Location.mem()) ;
++        assertNotNull(jrnl) ;
++        JournalEntry e = JournalEntry.COMMIT ;
++        jrnl.writeJournal(e) ;
++        assertFalse(jrnl.isEmpty()) ;
++        assertNotEquals(0, jrnl.position()) ;
++    }
++
++    @Test public void journal_03() {
++        Journal jrnl = Journal.create(Location.mem()) ;
++        assertNotNull(jrnl) ;
++        jrnl.writeJournal(JournalEntry.COMMIT) ;
++        assertNotEquals(0, jrnl.position()) ;
++        JournalEntry e = jrnl.readJournal(0) ;
++        check(JournalEntry.COMMIT, e) ;
++        assertEquals(ComponentIds.idSystem, e.getComponentId());
++        assertEquals(JournalEntryType.COMMIT, e.getType());
++        check(JournalEntry.COMMIT, e) ;
++    }
++
++    @Test public void journal_04() {
++        Journal jrnl = Journal.create(Location.mem()) ;
++        jrnl.writeJournal(JournalEntry.COMMIT) ;
++        jrnl.writeJournal(JournalEntry.ABORT) ;
++        jrnl.writeJournal(JournalEntry.ABORT) ;
++        jrnl.writeJournal(JournalEntry.COMMIT) ;
++        assertFalse(jrnl.isEmpty()) ;
++        
++        Iterator<JournalEntry> iter = jrnl.entries(0) ;
++        
++        List<JournalEntry> expected = Arrays.asList(JournalEntry.COMMIT,
++                                                    JournalEntry.ABORT,
++                                                    JournalEntry.ABORT,
++                                                    JournalEntry.COMMIT) ;
++    }
++
++    @Test public void journal_05() {
++        Journal jrnl = Journal.create(Location.mem()) ;
++        jrnl.writeJournal(JournalEntry.COMMIT) ;
++        jrnl.writeJournal(JournalEntry.ABORT) ;
++        long x = jrnl.writeJournal(JournalEntry.COMMIT) ;
++        jrnl.writeJournal(JournalEntry.COMMIT) ;
++        assertFalse(jrnl.isEmpty()) ;
++        
++        Iterator<JournalEntry> iter = jrnl.entries(x) ;
++        
++        List<JournalEntry> expected = Arrays.asList(JournalEntry.COMMIT,
++                                                    JournalEntry.COMMIT) ;
++    }
++    
++    @Test public void journal_06() {
++        ByteBuffer bb = ByteBuffer.allocateDirect(100) ;
++        ByteBufferLib.fill(bb, (byte)0XA5);
++        Journal jrnl = Journal.create(Location.mem()) ;
++        JournalEntry e = new JournalEntry(JournalEntryType.REDO, ComponentId.allocLocal(), bb) ;
++        jrnl.writeJournal(e) ;
++        jrnl.sync() ;
++        JournalEntry e2 = jrnl.readJournal(0) ;
++        check(e, e2);
++    }
++
++    @Test public void journal_07() {
++        ByteBuffer bb = ByteBuffer.allocateDirect(100) ;
++        ByteBufferLib.fill(bb, (byte)0XA5);
++        Journal jrnl = Journal.create(Location.mem()) ;
++        JournalEntry e = new JournalEntry(JournalEntryType.REDO, ComponentId.allocLocal(), bb) ;
++        
++        jrnl.writeJournal(JournalEntry.COMMIT) ;
++        long locn = jrnl.position() ;
++        jrnl.writeJournal(e) ;
++        
++        assertNotEquals(0, locn);
++        jrnl.sync() ;
++        JournalEntry e2 = jrnl.readJournal(locn) ;
++        check(e, e2);
++    }
++    
++    private void check(List<JournalEntry> expected, Iterator<JournalEntry> iter) {
++        Iterator<JournalEntry> iter2 = expected.iterator() ;
++        for(;;) {
++            if ( ! iter.hasNext() || ! iter2.hasNext() )
++                break ;
++            check(iter2.next(), iter.next()) ;
++        }
++        if ( iter.hasNext() )
++            fail("Journal iterator longer") ;
++        if ( iter2.hasNext() )
++            fail("Expected iterator longer") ;
++    }
++
++    private void check(JournalEntry expected, JournalEntry actual) {
++        assertEquals(expected.getType(), actual.getType()) ;
++        assertEquals(expected.getComponentId(), actual.getComponentId()) ; 
++        assertEquals(expected.getByteBuffer(), actual.getByteBuffer()) ;  
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TestStateMgrData.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TestStateMgrData.java
index 0000000,0000000..eb6636d
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TestStateMgrData.java
@@@ -1,0 -1,0 +1,75 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction;
++import static org.junit.Assert.* ;
++
++import java.nio.ByteBuffer ;
++
++import org.junit.Test ;
++import org.seaborne.dboe.base.file.BufferChannel ;
++import org.seaborne.dboe.base.file.FileFactory ;
++import org.seaborne.dboe.transaction.txn.StateMgrDataIdx ;
++
++public class TestStateMgrData {
++    
++    @Test public void state_data_01() {
++        BufferChannel x = FileFactory.createBufferChannelMem() ;
++        long[] data = {2,3} ; 
++        StateMgrDataIdx sm = new StateMgrDataIdx(x, data) ;
++        assertEquals(data.length, sm.getData().length) ;
++        assertEquals(2L, sm.get(0)) ;
++        assertEquals(3L, sm.get(1)) ;
++        // Test initial state written
++        ByteBuffer bb = ByteBuffer.allocate(2*Long.BYTES) ;
++        x.read(bb, 0) ;
++        assertEquals(2L, bb.getLong(0)) ;
++        assertEquals(3L, bb.getLong(Long.BYTES)) ; 
++    }
++    
++    @Test public void state_data_02() {
++        BufferChannel x = FileFactory.createBufferChannelMem() ;
++        long[] data = {2,3} ; 
++        StateMgrDataIdx sm = new StateMgrDataIdx(x, data) ;
++        sm.writeState(); 
++        sm.set(1, 99L);
++        sm.writeState();
++        ByteBuffer bb = ByteBuffer.allocate(2*Long.BYTES) ;
++        x.read(bb, 0) ;
++        assertEquals(99L, bb.getLong(Long.BYTES)) ; 
++    }
++
++    @Test public void state_data_03() {
++        BufferChannel x = FileFactory.createBufferChannelMem() ;
++        {
++            ByteBuffer bb = ByteBuffer.allocate(Long.BYTES) ;
++            bb.putLong(0, -8888) ;
++            bb.rewind();
++            x.write(bb) ;
++            bb.putLong(0, -1234) ;
++            bb.rewind();
++            x.write(bb) ;
++            x.sync(); 
++        }
++        long[] data = {2,3} ; 
++        StateMgrDataIdx sm = new StateMgrDataIdx(x, data) ;
++        assertEquals(-8888L, sm.get(0)) ;
++        assertEquals(-1234L, sm.get(1)) ;
++    }
++    
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TestThreadingTransactions.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TestThreadingTransactions.java
index 0000000,0000000..4d300cf
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TestThreadingTransactions.java
@@@ -1,0 -1,0 +1,120 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction;
++
++import java.util.concurrent.Semaphore ;
++
++import org.apache.jena.query.ReadWrite ;
++import org.junit.After ;
++import org.junit.Assert ;
++import org.junit.Before ;
++import org.junit.Test ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.dboe.jenax.Txn ;
++import org.seaborne.dboe.transaction.txn.TransactionCoordinator ;
++
++public class TestThreadingTransactions {
++    static final long InitValue = 3 ;
++    private TransactionalInteger transInt ; 
++    
++    @Before public void init() {
++        TransactionCoordinator coord = new TransactionCoordinator(Location.mem()) ;
++        transInt = new TransactionalInteger(coord, InitValue) ;
++        coord.start();
++    }
++
++    @After public void after() {
++        transInt.getTxnMgr().shutdown();
++    }
++
++    // Read synchronously in a transaction.
++    void readTxn(String label, TransactionalInteger trans, long expected) {
++        Txn.executeRead(trans, () -> {
++            read(label, trans, expected) ;
++        }) ;
++    }
++
++    void read(String label, TransactionalInteger trans, long expected) {
++        long x = trans.get() ;
++        Assert.assertEquals(label, expected, x); 
++    }
++
++    ThreadTxn threadRead(String label, TransactionalInteger trans, long expectedValue) {
++        return ThreadTxn.threadTxnRead(trans, ()->{
++            read(label, trans, expectedValue) ;
++        }) ;
++    }
++    
++    @Test public void threadTrans_01() {
++        transInt.begin(ReadWrite.READ) ;
++        read("[01]", transInt, InitValue) ;
++        transInt.end();
++    }
++    
++    @Test public void threadTrans_02() {
++        transInt.begin(ReadWrite.READ) ;
++        threadRead("[02]", transInt, InitValue).run() ;
++        transInt.end();
++    }
++
++    @Test public void threadTrans_03() {
++        Semaphore semaBefore = new Semaphore(0, true) ;
++        Semaphore semaAfter  = new Semaphore(0, true) ;
++        ThreadTxn async1 = threadRead("[03/1]", transInt, InitValue);
++        ThreadTxn async2 = threadRead("[03/2]", transInt, InitValue);
++        
++        transInt.begin(ReadWrite.WRITE) ;
++        read("[03/3]", transInt, InitValue) ;
++        transInt.inc(); 
++        read("[03/4]", transInt, InitValue+1) ;
++        
++        async1.run() ;
++       
++        threadRead("[03/5]", transInt, InitValue) ;
++        
++        transInt.commit();
++        transInt.end();
++        async2.run();
++        readTxn("[03/6]", transInt, InitValue+1) ;
++    }
++    
++    @Test public void threadTrans_04() {
++        Semaphore semaBefore1 = new Semaphore(0, true) ;
++        Semaphore semaBefore2 = new Semaphore(0, true) ;
++        Semaphore semaAfter  = new Semaphore(0, true) ;
++        
++        ThreadTxn async1 = threadRead("[04/1]", transInt, InitValue);
++        ThreadTxn async2 = threadRead("[04/2]", transInt, InitValue);
++        ThreadTxn async3 = threadRead("[04/3]", transInt, InitValue);
++        
++        Txn.executeWrite(transInt, transInt::inc);
++
++        ThreadTxn async4 = threadRead("[04/3]", transInt, InitValue+1);
++        async1.run() ;
++
++        Txn.executeWrite(transInt, transInt::inc);  // ++
++        async2.run() ;
++        async4.run() ;
++
++        Txn.executeWrite(transInt, transInt::inc);  // ++
++        async3.run() ;
++        
++        readTxn("[04/4]", transInt, InitValue+3) ;
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TestTransactionCoordinator.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TestTransactionCoordinator.java
index 0000000,0000000..49d3f2d
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TestTransactionCoordinator.java
@@@ -1,0 -1,0 +1,177 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction;
++
++import static org.junit.Assert.assertEquals ;
++
++import org.apache.jena.query.ReadWrite ;
++import org.junit.Test ;
++
++public class TestTransactionCoordinator extends AbstractTestTxn {
++    @Test public void txn_coord_read_1() {
++        assertEquals(0, txnMgr.countActive()) ;
++        assertEquals(0, txnMgr.countFinished()) ;
++        assertEquals(0, txnMgr.countBegin()) ;
++        assertEquals(0, txnMgr.countBeginRead()) ;
++        assertEquals(0, txnMgr.countBeginWrite()) ;
++        assertEquals(0, txnMgr.countActiveReaders()) ;
++        assertEquals(0, txnMgr.countActiveWriter()) ;
++
++        unit.begin(ReadWrite.READ);
++
++        assertEquals(1, txnMgr.countActive()) ;
++        assertEquals(0, txnMgr.countFinished()) ;
++        assertEquals(1, txnMgr.countBegin()) ;
++        assertEquals(1, txnMgr.countBeginRead()) ;
++        assertEquals(0, txnMgr.countBeginWrite()) ;
++        
++        assertEquals(1, txnMgr.countActiveReaders()) ;
++        assertEquals(0, txnMgr.countActiveWriter()) ;
++
++        unit.end() ;
++
++        assertEquals(0, txnMgr.countActive()) ;
++        assertEquals(1, txnMgr.countBeginRead()) ;
++        assertEquals(0, txnMgr.countBeginWrite()) ;
++        assertEquals(1, txnMgr.countFinished()) ;
++        assertEquals(1, txnMgr.countBegin()) ;
++        assertEquals(0, txnMgr.countActiveReaders()) ;
++        assertEquals(0, txnMgr.countActiveWriter()) ;
++    }
++    
++    @Test public void txn_coord_read_2() {
++        assertEquals(0, txnMgr.countActive()) ;
++        assertEquals(0, txnMgr.countFinished()) ;
++        assertEquals(0, txnMgr.countBegin()) ;
++        assertEquals(0, txnMgr.countBeginRead()) ;
++        assertEquals(0, txnMgr.countBeginWrite()) ;
++
++        unit.begin(ReadWrite.READ);
++
++        assertEquals(1, txnMgr.countActive()) ;
++        assertEquals(0, txnMgr.countFinished()) ;
++        assertEquals(1, txnMgr.countBegin()) ;
++        assertEquals(1, txnMgr.countBeginRead()) ;
++        assertEquals(0, txnMgr.countBeginWrite()) ;
++
++        unit.commit() ;
++        
++        assertEquals(0, txnMgr.countActive()) ;
++        assertEquals(1, txnMgr.countBeginRead()) ;
++        assertEquals(0, txnMgr.countBeginWrite()) ;
++        assertEquals(1, txnMgr.countFinished()) ;
++        assertEquals(1, txnMgr.countBegin()) ;
++
++        unit.end() ;
++
++        assertEquals(0, txnMgr.countActive()) ;
++        assertEquals(1, txnMgr.countBeginRead()) ;
++        assertEquals(0, txnMgr.countBeginWrite()) ;
++        assertEquals(1, txnMgr.countFinished()) ;
++        assertEquals(1, txnMgr.countBegin()) ;
++    }
++
++    @Test public void txn_coord_write_1() {
++        assertEquals(0, txnMgr.countActive()) ;
++        assertEquals(0, txnMgr.countFinished()) ;
++        assertEquals(0, txnMgr.countBegin()) ;
++        assertEquals(0, txnMgr.countBeginRead()) ;
++        assertEquals(0, txnMgr.countBeginWrite()) ;
++
++        assertEquals(0, txnMgr.countActiveReaders()) ;
++        assertEquals(0, txnMgr.countActiveWriter()) ;
++
++        
++        unit.begin(ReadWrite.WRITE);
++        
++        assertEquals(1, txnMgr.countActive()) ;
++        assertEquals(0, txnMgr.countFinished()) ;
++        assertEquals(1, txnMgr.countBegin()) ;
++        assertEquals(0, txnMgr.countBeginRead()) ;
++        assertEquals(1, txnMgr.countBeginWrite()) ;
++        
++        assertEquals(0, txnMgr.countActiveReaders()) ;
++        assertEquals(1, txnMgr.countActiveWriter()) ;
++
++        unit.commit() ;
++
++        assertEquals(0, txnMgr.countActive()) ;
++        assertEquals(1, txnMgr.countFinished()) ;
++        assertEquals(1, txnMgr.countBegin()) ;
++        assertEquals(0, txnMgr.countBeginRead()) ;
++        assertEquals(1, txnMgr.countBeginWrite()) ;
++
++        assertEquals(0, txnMgr.countActiveReaders()) ;
++        assertEquals(0, txnMgr.countActiveWriter()) ;
++
++        unit.end() ;
++
++        assertEquals(0, txnMgr.countActive()) ;
++        assertEquals(1, txnMgr.countFinished()) ;
++        assertEquals(1, txnMgr.countBegin()) ;
++        assertEquals(0, txnMgr.countBeginRead()) ;
++        assertEquals(1, txnMgr.countBeginWrite()) ;
++
++        assertEquals(0, txnMgr.countActiveReaders()) ;
++        assertEquals(0, txnMgr.countActiveWriter()) ;
++    }
++
++    @Test public void txn_coord_write_2() {
++        assertEquals(0, txnMgr.countActive()) ;
++        assertEquals(0, txnMgr.countFinished()) ;
++        assertEquals(0, txnMgr.countBegin()) ;
++        assertEquals(0, txnMgr.countBeginRead()) ;
++        assertEquals(0, txnMgr.countBeginWrite()) ;
++        assertEquals(0, txnMgr.countActiveReaders()) ;
++        assertEquals(0, txnMgr.countActiveWriter()) ;
++        
++        unit.begin(ReadWrite.WRITE);
++        
++        assertEquals(1, txnMgr.countActive()) ;
++        assertEquals(0, txnMgr.countFinished()) ;
++        assertEquals(1, txnMgr.countBegin()) ;
++        assertEquals(0, txnMgr.countBeginRead()) ;
++        assertEquals(1, txnMgr.countBeginWrite()) ;
++        
++        assertEquals(0, txnMgr.countActiveReaders()) ;
++        assertEquals(1, txnMgr.countActiveWriter()) ;
++        
++        unit.abort() ;
++
++        assertEquals(0, txnMgr.countActive()) ;
++        assertEquals(1, txnMgr.countFinished()) ;
++        assertEquals(1, txnMgr.countBegin()) ;
++        assertEquals(0, txnMgr.countBeginRead()) ;
++        assertEquals(1, txnMgr.countBeginWrite()) ;
++        
++        assertEquals(0, txnMgr.countActiveReaders()) ;
++        assertEquals(0, txnMgr.countActiveWriter()) ;
++        
++        unit.end() ;
++
++        assertEquals(0, txnMgr.countActive()) ;
++        assertEquals(1, txnMgr.countFinished()) ;
++        assertEquals(1, txnMgr.countBegin()) ;
++        assertEquals(0, txnMgr.countBeginRead()) ;
++        assertEquals(1, txnMgr.countBeginWrite()) ;
++
++        assertEquals(0, txnMgr.countActiveReaders()) ;
++        assertEquals(0, txnMgr.countActiveWriter()) ;
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TestTransactionCoordinatorControl.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TestTransactionCoordinatorControl.java
index 0000000,0000000..56bf401
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TestTransactionCoordinatorControl.java
@@@ -1,0 -1,0 +1,141 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction;
++
++import static org.junit.Assert.assertEquals ;
++import static org.junit.Assert.assertFalse ;
++import static org.junit.Assert.assertNotNull ;
++import static org.junit.Assert.assertNull ;
++import static org.junit.Assert.assertTrue ;
++
++import java.util.concurrent.Semaphore ;
++import java.util.concurrent.atomic.AtomicInteger ;
++
++import org.apache.jena.query.ReadWrite ;
++import org.junit.After ;
++import org.junit.Before ;
++import org.junit.Test ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.dboe.jenax.Txn ;
++import org.seaborne.dboe.migrate.L ;
++import org.seaborne.dboe.transaction.txn.Transaction ;
++import org.seaborne.dboe.transaction.txn.TransactionCoordinator ;
++import org.seaborne.dboe.transaction.txn.TransactionException ;
++import org.seaborne.dboe.transaction.txn.TransactionalBase ;
++
++public class TestTransactionCoordinatorControl {
++    // The problem with these tests is the need for Lib.sleep as a way to ensure
++    // async threads run if they can.  And we are sometimes testing for "they can't".
++    
++    static final long InitValue = 3 ;
++    private TransactionCoordinator txnMgr ;
++    protected Transactional unit ;
++    
++    @Before public void init() {
++        txnMgr = new TransactionCoordinator(Location.mem()) ;
++        unit = new TransactionalBase(txnMgr) ;
++        txnMgr.start();
++    }
++
++    @After public void after() {
++        txnMgr.shutdown();
++    }
++    
++    
++    @Test public void txn_coord_disable_writers_1() {
++        AtomicInteger counter1 = new AtomicInteger(0) ;
++        AtomicInteger counter2 = new AtomicInteger(0) ;
++
++        txnMgr.blockWriters();
++        ThreadTxn threadTxn1 = ThreadTxn.threadTxnRead(unit, ()->counter1.incrementAndGet()) ;
++        threadTxn1.run() ;
++        assertEquals(1, counter1.get()) ;
++    }
++
++    @Test public void txn_coord_disable_writers_2() {
++        txnMgr.blockWriters();
++        Transaction txn = L.syncCallThread(()->txnMgr.begin(ReadWrite.WRITE, false)) ;
++        assertNull(txn) ;
++        txnMgr.enableWriters();
++        Transaction txn1 = L.syncCallThread(()->txnMgr.begin(ReadWrite.WRITE, false)) ;
++        assertNotNull(txn1) ;
++    }
++    
++    @Test public void txn_coord_disable_writers_3() {
++        txnMgr.blockWriters();
++        Transaction txn = L.syncCallThread(()->txnMgr.begin(ReadWrite.READ, false)) ;
++        assertNotNull(txn) ;
++        txnMgr.enableWriters();
++        Transaction txn1 = L.syncCallThread(()->txnMgr.begin(ReadWrite.WRITE, false)) ;
++        assertNotNull(txn1) ;
++        Transaction txn2 = L.syncCallThread(()->txnMgr.begin(ReadWrite.READ, false)) ;
++        assertNotNull(txn2) ;
++    }
++    
++    @Test(expected=TransactionException.class)
++    public void txn_coord_disable_writers_4() {
++        txnMgr.blockWriters();
++        txnMgr.enableWriters();
++        txnMgr.enableWriters();
++    }
++
++    @Test
++    public void txn_coord_disable_writers_() {
++        txnMgr.blockWriters();
++        boolean b = txnMgr.tryBlockWriters() ;
++        assertFalse(b) ;
++        txnMgr.enableWriters();
++    }
++    
++    @Test public void txn_coord_exclusive_1() {
++        txnMgr.startExclusiveMode();
++        L.syncOtherThread(()->{
++            Transaction txn1 = txnMgr.begin(ReadWrite.WRITE, false) ;
++            assertNull(txn1) ;
++            Transaction txn2 = txnMgr.begin(ReadWrite.READ, false) ;
++            assertNull(txn2) ;
++        }) ;
++        
++        txnMgr.finishExclusiveMode();
++        L.syncOtherThread(()->{
++            Transaction txn1 = txnMgr.begin(ReadWrite.WRITE, false) ;
++            assertNotNull(txn1) ;
++            Transaction txn2 = txnMgr.begin(ReadWrite.READ, false) ;
++            assertNotNull(txn2) ;
++        }) ;
++    }
++    
++    @Test public void txn_coord_exclusive_2() {
++        AtomicInteger counter1 = new AtomicInteger(0) ;
++        Semaphore finalSema = new Semaphore(0) ;
++        ThreadTxn ttxn = ThreadTxn.threadTxnWrite(unit, ()->{
++            counter1.incrementAndGet() ;
++        }) ;
++        boolean b = txnMgr.tryExclusiveMode(false);
++        assertFalse(b) ;
++        assertEquals(0, counter1.get()) ;
++        ttxn.run(); // Now run thread
++        assertEquals(1, counter1.get()) ;
++        Txn.executeWrite(unit, ()->{});
++        b = txnMgr.tryExclusiveMode(false);
++        assertTrue(b) ;
++    }
++    
++    
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TestTransactionLifecycle.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TestTransactionLifecycle.java
index 0000000,0000000..b7f0c43
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TestTransactionLifecycle.java
@@@ -1,0 -1,0 +1,261 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction;
++
++import static org.junit.Assert.fail ;
++import org.junit.Test ;
++import org.seaborne.dboe.transaction.txn.TransactionException ;
++
++import org.apache.jena.query.ReadWrite ;
++
++/**
++ * Tests of transaction lifecycle in one JVM.
++ * Journal independent.
++ * Not testing recovery or writing to the journal. 
++ */
++public class TestTransactionLifecycle extends AbstractTestTxn {
++    @Test public void txn_read_end() {
++        unit.begin(ReadWrite.READ);
++        unit.end() ;
++        checkClear() ;
++    }
++
++    @Test public void txn_read_end_end() {
++        unit.begin(ReadWrite.READ);
++        unit.end() ;
++        unit.end() ;
++        checkClear() ;
++    }
++
++    @Test public void txn_read_abort() {
++        unit.begin(ReadWrite.READ);
++        unit.abort() ;
++        checkClear() ;
++    }
++
++    @Test public void txn_read_commit() {
++        unit.begin(ReadWrite.READ);
++        unit.commit() ;
++        checkClear() ;
++    }
++
++    @Test public void txn_read_abort_end() {
++        unit.begin(ReadWrite.READ);
++        unit.abort() ;
++        unit.end() ;
++        checkClear() ;
++    }
++
++    @Test public void txn_read_commit_end() {
++        unit.begin(ReadWrite.READ);
++        unit.commit() ;
++        unit.end() ;
++        checkClear() ;
++    }
++    
++    @Test public void txn_read_commit_abort() {
++        unit.begin(ReadWrite.READ);
++        unit.commit() ;
++        try { unit.abort() ; fail() ; }
++        catch (TransactionException ex) { /* Expected : can continue */ }
++        unit.end() ;
++        checkClear() ;
++    }
++    
++    @Test public void txn_read_commit_commit() {
++        unit.begin(ReadWrite.READ);
++        unit.commit() ;
++        try { unit.commit() ; fail() ; }
++        catch (TransactionException ex) { /* Expected : can continue */ }
++        unit.end() ;
++        checkClear() ;
++    }
++
++    @Test public void txn_read_abort_commit() {
++        unit.begin(ReadWrite.READ);
++        unit.abort() ;
++        try { unit.commit() ; fail() ; }
++        catch (TransactionException ex) { /* Expected : can continue */ }
++        unit.end() ;
++        checkClear() ;
++    }
++
++    @Test public void txn_read_abort_abort() {
++        unit.begin(ReadWrite.READ);
++        unit.abort() ;
++        try { unit.abort() ; fail() ; }
++        catch (TransactionException ex) { /* Expected : can continue */ }
++        unit.end() ;
++        checkClear() ;
++    }
++
++    @Test(expected=TransactionException.class)
++    public void txn_begin_read_begin_read() {
++        unit.begin(ReadWrite.READ);
++        unit.begin(ReadWrite.READ);
++    }
++
++    @Test(expected=TransactionException.class)
++    public void txn_begin_read_begin_write() {
++        unit.begin(ReadWrite.READ);
++        unit.begin(ReadWrite.WRITE);
++    }
++
++    @Test(expected=TransactionException.class)
++    public void txn_begin_write_begin_read() {
++        unit.begin(ReadWrite.WRITE);
++        unit.begin(ReadWrite.READ);
++    }
++
++    @Test(expected=TransactionException.class)
++    public void txn_begin_write_begin_write() {
++        unit.begin(ReadWrite.WRITE);
++        unit.begin(ReadWrite.WRITE);
++    }
++
++    @Test(expected=TransactionException.class) 
++    public void txn_write_begin_end() {
++        unit.begin(ReadWrite.WRITE);
++        unit.end() ;
++        checkClear() ;
++    }
++    
++    @Test public void txn_write_abort() {
++        unit.begin(ReadWrite.WRITE);
++        unit.abort() ;
++        checkClear() ;
++    }
++
++    @Test public void txn_write_commit() {
++        unit.begin(ReadWrite.WRITE);
++        unit.commit() ;
++        checkClear() ;
++    }
++
++    @Test public void txn_write_abort_end() {
++        unit.begin(ReadWrite.WRITE);
++        unit.abort() ;
++        unit.end() ;
++        checkClear() ;
++    }
++
++    @Test public void txn_write_abort_end_end() {
++        unit.begin(ReadWrite.WRITE);
++        unit.abort() ;
++        unit.end() ;
++        unit.end() ;
++        checkClear() ;
++    }
++    
++    @Test public void txn_write_commit_end() {
++        unit.begin(ReadWrite.WRITE);
++        unit.commit() ;
++        unit.end() ;
++        checkClear() ;
++    }
++    
++    @Test public void txn_write_commit_end_end() {
++        unit.begin(ReadWrite.WRITE);
++        unit.commit() ;
++        unit.end() ;
++        unit.end() ;
++        checkClear() ;
++    }
++
++    @Test public void txn_write_commit_abort() {
++        // commit-abort
++        unit.begin(ReadWrite.WRITE);
++        unit.commit() ;
++        try { unit.abort() ; fail() ; }
++        catch (TransactionException ex) { /* Expected : can continue */ }
++        unit.end() ;
++        checkClear() ;
++    }
++    
++    @Test public void txn_write_commit_commit() {
++        // commit-commit
++        unit.begin(ReadWrite.WRITE);
++        unit.commit() ;
++        try { unit.commit() ; fail() ; }
++        catch (TransactionException ex) { /* Expected : can continue */ }
++        unit.end() ;
++        checkClear() ;
++    }
++
++    @Test public void txn_write_abort_commit() {
++        // abort-commit
++        unit.begin(ReadWrite.WRITE);
++        unit.abort() ;
++        try { unit.commit() ; fail() ; }
++        catch (TransactionException ex) { /* Expected : can continue */ }
++        unit.end() ;
++        checkClear() ;
++    }
++
++    @Test public void txn_write_abort_abort() {
++        // abort-abort
++        unit.begin(ReadWrite.WRITE);
++        unit.abort() ;
++        try { unit.abort() ; fail() ; }
++        catch (TransactionException ex) { /* Expected : can continue */ }
++        unit.end() ;
++        checkClear() ;
++    }
++    
++    private void read() {
++        unit.begin(ReadWrite.READ);
++        unit.end() ;
++        checkClear() ;
++    }
++    
++    private void write() {
++        unit.begin(ReadWrite.WRITE);
++        unit.commit() ;
++        unit.end() ;
++        checkClear() ;
++    }
++
++    @Test public void txn_read_read() {
++        read() ; 
++        read() ;
++    }
++
++    @Test public void txn_write_read() {
++        write() ; 
++        read() ;
++    }
++
++    @Test public void txn_read_write() {
++        read() ;
++        write() ; 
++    }
++
++    @Test public void txn_write_write() {
++        write() ; 
++        write() ; 
++    }
++
++    @Test public void txn_www() {
++        write() ; 
++        write() ; 
++        write() ;
++        checkClear();
++    }
++
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TestTransactionLifecycle2.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TestTransactionLifecycle2.java
index 0000000,0000000..6deca4e
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TestTransactionLifecycle2.java
@@@ -1,0 -1,0 +1,298 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction;
++
++import static org.junit.Assert.* ;
++
++import java.util.concurrent.atomic.AtomicReference ;
++
++import org.apache.jena.query.ReadWrite ;
++import org.junit.After ;
++import org.junit.Before ;
++import org.junit.Test ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.dboe.migrate.L ;
++import org.seaborne.dboe.transaction.txn.Transaction ;
++import org.seaborne.dboe.transaction.txn.TransactionCoordinator ;
++import org.seaborne.dboe.transaction.txn.TransactionException ;
++import org.seaborne.dboe.transaction.txn.journal.Journal ;
++
++/**
++ * Details tests of the transaction lifecycle in one JVM
++ * including tests beyond the TransactionalComponentLifecycle 
++ * 
++ * Journal independent.
++ */
++public class TestTransactionLifecycle2 {
++    // org.junit.rules.ExternalResource ?
++    protected TransactionCoordinator txnMgr ;
++//    protected TransInteger counter1 = new TransInteger(0) ; 
++//    protected TransInteger counter2 = new TransInteger(0) ;
++//    protected TransMonitor monitor  = new TransMonitor() ;
++    
++    @Before public void setup() {
++        Journal jrnl = Journal.create(Location.mem()) ;
++        txnMgr = new TransactionCoordinator(jrnl) ;
++        txnMgr.start() ;
++    }
++    
++    @After public void clearup() {
++        txnMgr.shutdown(); 
++    }
++    
++    protected void checkClear() {
++        assertEquals(0, txnMgr.countActive()) ;
++        assertEquals(0, txnMgr.countBegin()-txnMgr.countFinished()) ;
++    }
++    
++    @Test public void txn_direct_01() {
++        Transaction txn1 = txnMgr.begin(ReadWrite.READ) ;
++        txn1.end();
++        checkClear() ;
++    }
++    
++    @Test(expected=TransactionException.class)
++    public void txn_direct_02() {
++        Transaction txn1 = txnMgr.begin(ReadWrite.WRITE) ;
++        txn1.end(); 
++        checkClear() ;
++    }
++
++    @Test
++    public void txn_direct_03() {
++        Transaction txn1 = txnMgr.begin(ReadWrite.WRITE) ;
++        txn1.commit() ;
++        txn1.end() ; 
++        checkClear() ;
++    }
++
++    @Test
++    public void txn_direct_04() {
++        Transaction txn1 = txnMgr.begin(ReadWrite.WRITE) ;
++        // This tests the TransactionCoordinator
++        // but the TransactiolComponentLifecycle doesn't support multiple
++        // transactions per thread (use of ThreadLocals).
++        // To do that, the transaction object would be needed in all
++        // component API calls.  Doable but intrusive.
++        Transaction txn2 = txnMgr.begin(ReadWrite.READ) ;
++        txn1.commit() ;
++        txn2.end() ;
++        txn1.end() ; 
++        checkClear() ;
++    }
++    
++    @Test
++    public void txn_direct_05() {
++        Transaction txn1 = txnMgr.begin(ReadWrite.WRITE) ;
++        txn1.prepare() ;
++        txn1.commit() ;
++        txn1.end() ; 
++        checkClear() ;
++    }
++
++    @Test
++    public void txn_direct_06() {
++        Transaction txn1 = txnMgr.begin(ReadWrite.WRITE) ;
++        // txn1.prepare() ; Optional.
++        txn1.commit() ;
++        txn1.end() ; 
++        checkClear() ;
++    }
++    
++    @Test
++    public void txn_overlap_WW() {
++        Transaction txn1 = txnMgr.begin(ReadWrite.WRITE, false) ;
++        assertNotNull(txn1) ;
++        
++        Transaction txn2 = txnMgr.begin(ReadWrite.WRITE, false) ;
++        assertNull(txn2) ;  // Otherwise blocking.
++        
++        txn1.commit();
++        txn1.end() ;
++        checkClear() ;
++    }
++
++    @Test
++    public void txn_overlap_WR() {
++        Transaction txn1 = txnMgr.begin(ReadWrite.WRITE, false) ;
++        assertNotNull(txn1) ;
++        
++        Transaction txn2 = txnMgr.begin(ReadWrite.READ, false) ;
++        assertNotNull(txn2) ;
++        
++        txn1.commit();
++        txn1.end() ;
++        txn2.end();
++        checkClear() ;
++    }
++
++    @Test
++    public void txn_overlap_RW() {
++        Transaction txn1 = txnMgr.begin(ReadWrite.READ, false) ;
++        assertNotNull(txn1) ;
++
++        Transaction txn2 = txnMgr.begin(ReadWrite.WRITE, false) ;
++        assertNotNull(txn2) ;
++        txn1.commit();
++        txn1.end() ;
++        txn2.abort();
++        txn2.end();
++        checkClear() ;
++    }
++
++    @Test
++    public void txn_overlap_RR() {
++        Transaction txn1 = txnMgr.begin(ReadWrite.READ, false) ;
++        assertNotNull(txn1) ;
++
++        Transaction txn2 = txnMgr.begin(ReadWrite.READ, false) ;
++        assertNotNull(txn2) ;
++        
++        txn1.commit();
++        txn1.end() ;
++        txn2.end();
++        checkClear() ;
++    }
++    
++    @Test
++    public void txn_promote_1() {
++        Transaction txn1 = txnMgr.begin(ReadWrite.READ) ;
++        assertNotNull(txn1) ;
++        boolean b = txn1.promote() ;
++        assertTrue(b) ;
++        assertEquals(ReadWrite.WRITE, txn1.getMode()) ;
++        txn1.commit() ;
++        txn1.end() ;
++        checkClear() ;
++    }
++    
++    @Test
++    public void txn_promote_2() {
++        Transaction txn1 = txnMgr.begin(ReadWrite.WRITE) ;
++        boolean b = txn1.promote() ;
++        assertTrue(b) ;
++        b = txn1.promote() ;
++        assertTrue(b) ;
++        txn1.abort() ;
++        txn1.end() ;
++        checkClear() ;
++    }
++    
++    @Test(expected=TransactionException.class)
++    public void txn_promote_3() {
++        Transaction txn1 = txnMgr.begin(ReadWrite.READ) ;
++        boolean b = txn1.promote() ;
++        assertTrue(b) ;
++        b = txn1.promote() ;
++        assertTrue(b) ;
++        // Exception - now a writer
++        txn1.end() ;
++        checkClear() ;
++    }
++
++    //Not a @Test
++    public void txn_promote_deadlock() {
++        Transaction txn1 = txnMgr.begin(ReadWrite.READ) ;
++        Transaction txn2 = txnMgr.begin(ReadWrite.WRITE) ;
++        // Deadlock.
++        // Promotion waits for the writer to decide whether it is commiting or not.
++        // This can't be done on one thread.
++        boolean b = txn1.promote() ;
++        assertFalse(b) ;
++        txn1.end() ;
++        txn2.commit(); 
++        txn2.end() ;
++        checkClear() ;
++    }
++
++    @Test
++    public void txn_promote_thread_writer_1() {
++        Transaction txn1 = txnMgr.begin(ReadWrite.READ) ;
++        L.syncOtherThread(()->{
++            Transaction txn2 = txnMgr.begin(ReadWrite.WRITE) ;
++            txn2.commit(); 
++            txn2.end() ;
++        }) ;
++            
++        boolean b = txn1.promote() ;
++        assertFalse(b) ;
++        txn1.end() ;
++        checkClear() ;
++    }
++    
++    @Test
++    public void txn_promote_thread_writer_2() {
++        Transaction txn1 = txnMgr.begin(ReadWrite.READ) ;
++        L.syncOtherThread(()->{
++            Transaction txn2 = txnMgr.begin(ReadWrite.WRITE) ;
++            txn2.abort(); 
++            txn2.end() ;
++        }) ;
++            
++        boolean b = txn1.promote() ;
++        assertTrue(b) ;
++        // Now a writer.
++        txn1.commit();
++        txn1.end() ;
++        checkClear() ;
++    }
++
++    @Test
++    public void txn_promote_thread_writer_3() {
++        Transaction txn1 = txnMgr.begin(ReadWrite.READ) ;
++        boolean b = txn1.promote() ;
++        assertTrue(b) ;
++        AtomicReference<Transaction> ref = new AtomicReference<>(txn1) ;
++        L.syncOtherThread(()->{
++            // Should fail.
++            Transaction txn2 = txnMgr.begin(ReadWrite.WRITE, false) ;
++            ref.set(txn2);
++        }) ;
++        assertNull(ref.get()) ;
++        txn1.abort() ;
++        txn1.end() ;
++    }
++    
++    @Test
++    public void txn_promote_thread_writer_4() {
++        Transaction txn1 = txnMgr.begin(ReadWrite.READ) ;
++        boolean b = txn1.promote() ;
++        assertTrue(b) ;
++        AtomicReference<Transaction> ref = new AtomicReference<>(txn1) ;
++        L.syncOtherThread(()->{
++            // Should fail.
++            Transaction txn2 = txnMgr.begin(ReadWrite.WRITE, false) ;
++            ref.set(txn2);
++        }) ;
++        assertNull(ref.get()) ;
++        txn1.abort() ;
++        txn1.end() ;
++
++        L.syncOtherThread(()->{
++            // Should suceed
++            Transaction txn2 = txnMgr.begin(ReadWrite.WRITE, false) ;
++            ref.set(txn2);
++            txn2.abort() ;
++            txn2.end() ;
++        }) ;
++        assertNotNull(ref.get()) ;
++        checkClear() ;
++    }
++
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TestTxnId.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TestTxnId.java
index 0000000,0000000..ad7af38
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TestTxnId.java
@@@ -1,0 -1,0 +1,52 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction;
++
++import static org.junit.Assert.assertEquals ;
++import static org.junit.Assert.assertNotEquals ;
++import static org.junit.Assert.assertNotNull ;
++import static org.junit.Assert.assertNotSame ;
++
++import org.junit.Test ;
++import org.seaborne.dboe.transaction.txn.TxnId ;
++import org.seaborne.dboe.transaction.txn.TxnIdFactory ;
++import org.seaborne.dboe.transaction.txn.TxnIdSimple ;
++
++public class TestTxnId {
++    @Test public void txnId_1() {
++        TxnId id1 = TxnIdFactory.createSimple() ;
++        assertNotNull(id1) ;
++    }
++
++    @Test public void txnId_2() {
++        TxnId id1 = TxnIdFactory.createSimple() ;
++        TxnId id2 = TxnIdFactory.createSimple() ;
++        assertNotSame(id1, id2) ;
++        assertNotEquals(id1, id2) ;
++    }
++
++    @Test public void txnId_3() {
++        TxnId id1 = TxnIdFactory.createSimple() ;
++        TxnId id2 = TxnIdSimple.create(id1.bytes()) ;
++        assertNotSame(id1, id2) ;
++        assertEquals(id1, id2) ;
++        assertEquals(id1.name(), id2.name()) ;
++    }
++
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TestTxnLib.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TestTxnLib.java
index 0000000,0000000..0006573
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TestTxnLib.java
@@@ -1,0 -1,0 +1,197 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction;
++
++import static org.junit.Assert.assertEquals ;
++import static org.junit.Assert.assertNotEquals ;
++import static org.junit.Assert.fail ;
++
++import org.apache.jena.query.ReadWrite ;
++import org.junit.Test ;
++import org.seaborne.dboe.jenax.Txn ;
++
++public class TestTxnLib extends AbstractTestTxn {
++
++    @Test public void libTxn_1() {
++        long v1 = counter1.value() ;
++        long v2 = counter2.value() ;
++        assertEquals(0, v1) ;
++        assertEquals(0, v2) ;
++        
++        Txn.executeRead(unit, () -> {
++            assertEquals(0, counter1.get()) ;
++            assertEquals(0, counter2.get()) ;
++        }) ;
++    }
++
++    @Test public void libTxn_2() {
++        assertEquals(0, counter1.value()) ;
++        
++        Txn.executeWrite(unit, () -> {
++            counter1.inc() ;
++            assertEquals("In W, value()", 0, counter1.value()) ;
++            assertEquals("In W, get()",1, counter1.get()) ;
++        }) ;
++        
++        assertEquals("Direct value()", 1, counter1.value()) ;
++        assertEquals("Direct get()", 1, counter1.get()) ;
++
++        Txn.executeRead(unit, () -> {
++            assertEquals("In R, value()", 1, counter1.get()) ;
++            assertEquals("In R, get()", 1, counter1.value()) ;
++        }) ;
++    }
++    
++    @Test public void libTxn_3() {
++        Txn.executeRead(unit, () -> {
++            assertEquals("In R, value()", 0, counter2.get()) ;
++            assertEquals("In R, get()", 0, counter2.value()) ;
++        }) ;
++
++        Txn.executeWrite(unit, () -> {
++            counter2.inc() ;
++            assertEquals("In W, value()", 0, counter2.value()) ;
++            assertEquals("In W, get()",1, counter2.get()) ;
++        }) ;
++        
++        assertEquals("Direct value()", 1, counter2.value()) ;
++        assertEquals("Direct get()", 1, counter2.get()) ;
++
++        Txn.executeRead(unit, () -> {
++            assertEquals("In R, value()", 1, counter2.get()) ;
++            assertEquals("In R, get()", 1, counter2.value()) ;
++        }) ;
++    }
++
++    @Test public void libTxn_4() {
++        long v1 = counter1.value() ;
++        long v2 = counter2.value() ;
++        assertEquals(0, v1) ;
++        assertEquals(0, v2) ;
++        
++        //Txn.execWrite(unit, () -> {
++
++        unit.begin(ReadWrite.WRITE); 
++            counter1.inc() ;
++            counter2.inc() ;
++            assertEquals("Counter out of step", counter1.get(), counter2.get()); 
++            assertNotEquals("Counter 1 can see wrong state", counter1.get(), counter1.value() ) ;
++            assertNotEquals("Counter 2 can see wrong state", counter2.get(), counter2.value() ) ;
++            counter2.inc() ;
++            assertNotEquals("Counter 1 and 2 should differ", counter1.get(), counter2.get() ) ;
++        unit.commit() ;
++        unit.end() ;
++        //}) ;
++        assertEquals("Component 1 inconsistent", 1, counter1.value()) ;
++        assertEquals("Component 2 inconsistent", 2, counter2.value()) ;
++        
++        Txn.executeRead(unit, () -> {
++            assertEquals("Component 1 inconsistent (R)", 1, counter1.get()) ;
++            assertEquals("Component 2 inconsistent (R)", 2, counter2.get()) ;
++        }) ;
++    }
++    
++    @Test public void libTxn_5() {
++        long x = 
++            Txn.calculateRead(unit, () -> {
++                assertEquals("In R, value()", 0, counter2.get()) ;
++                assertEquals("In R, get()", 0, counter2.value()) ;
++                return counter2.get() ;
++            }) ;
++        assertEquals("Outside R", 0, x) ;
++    }
++    
++    @Test public void libTxn_6() {
++        long x = 
++            Txn.calculateWrite(unit, () -> {
++                counter2.inc() ;
++                assertEquals("In W, value()", 0, counter2.value()) ;
++                assertEquals("In W, get()",1, counter2.get()) ;
++                return counter2.get() ;
++            }) ;
++        assertEquals("Outside W",1, x) ;
++    }
++
++    @Test public void libTxn_7() {
++        long x1 = 
++            Txn.calculateWrite(unit, () -> {
++                counter2.inc() ;
++                counter2.inc() ;
++                return counter2.get() ;
++            }) ;
++        long x2 = Txn.calculateRead(unit, () -> {
++            return counter2.get() ;
++        }) ;
++        assertEquals("After W and R",x1 , x2) ;
++    }
++    
++    // Tests for thread transactions.
++    
++    @Test public void libTxnThread_1() {
++        ThreadTxn t = ThreadTxn.threadTxnRead(unit, ()->{}) ;
++        t.run();
++    }
++    
++    @Test public void libTxnThread_2() {
++        ThreadTxn t = ThreadTxn.threadTxnWrite(unit, ()-> fail("")) ;
++    }
++
++    @Test(expected=AssertionError.class)
++    public void libTxnThread_3() {
++        ThreadTxn t = ThreadTxn.threadTxnWrite(unit, ()-> fail("")) ;
++        t.run() ;
++    }
++
++    @Test public void libTxnThread_10() {
++        long x1 = counter1.get() ;  
++        ThreadTxn t = ThreadTxn.threadTxnWrite(unit, ()->{ counter1.inc() ;}) ;
++        long x2 = counter1.get() ;
++        assertEquals("x2", x1, x2) ;
++        t.run() ;
++        long x3 = counter1.get() ;
++        assertEquals("x3", x1+1, x3) ;
++    }
++    
++    @Test public void libTxnThread_11() {
++        long x1 = counter1.get() ;  
++        Txn.executeWrite(unit, ()->{
++            counter1.inc();
++            // Read the "before" state
++            ThreadTxn t = ThreadTxn.threadTxnRead(unit, ()->{ long z1 = counter1.get() ; assertEquals("Thread read", x1, z1) ; }) ;
++            counter1.inc();
++            t.run(); 
++        }) ;
++        long x2 = counter1.get() ;
++        assertEquals("after", x1+2, x2) ;
++    }
++
++    @Test public void libTxnThread_12() {
++        long x1 = counter1.get() ;  
++        ThreadTxn t = ThreadTxn.threadTxnRead(unit, () -> {
++            long z1 = counter1.get() ;
++            assertEquals("Thread", x1, z1) ;
++        }) ;
++        Txn.executeWrite(unit, ()->counter1.inc()) ;
++        t.run() ;
++        long x2 = counter1.get() ;
++        assertEquals("after", x1+1, x2) ;
++    }
++
++}
++
++ 

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TestTxnLib2.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TestTxnLib2.java
index 0000000,0000000..227e2a7
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TestTxnLib2.java
@@@ -1,0 -1,0 +1,65 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction;
++
++import org.apache.jena.atlas.lib.Pair ;
++import org.junit.After ;
++import org.junit.Assert ;
++import org.junit.Before ;
++import org.junit.Test ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.dboe.jenax.Txn ;
++import org.seaborne.dboe.transaction.txn.TransactionCoordinator ;
++
++/** Unusual ways to do things.
++ *  Rather than a TransactionalComponent,   
++ *  TransactionalInteger 
++ */
++public class TestTxnLib2 extends Assert {
++    // With setup/teardown / not from AbstractTestTxn
++    private final long InitValue = 7 ;  
++    TransactionalInteger integer ; 
++    
++    @Before public void setup() {
++        TransactionCoordinator coord = new TransactionCoordinator(Location.mem()) ;
++        integer = new TransactionalInteger(coord, InitValue) ;
++        coord.start() ;
++    }
++    
++    @After public void clearup() {
++        integer.shutdown(); 
++    }
++
++    @Test public void libTxn_10() {
++        Txn.executeWrite(integer, integer::inc) ;
++        long x = Txn.calculateRead(integer, integer::get) ;
++        assertEquals(InitValue+1, x) ;
++    }
++    
++    @Test public void libTxn_11() {
++        Pair<Long, Long> p = Txn.calculateWrite(integer, () -> {
++            integer.inc() ;
++            return Pair.create(integer.value(), integer.get()) ;
++        }) ;
++        assertEquals(InitValue,     p.getLeft().longValue()) ;
++        assertEquals(InitValue+1,   p.getRight().longValue()) ;
++        assertEquals(InitValue+1,   integer.get()) ;
++        assertEquals(InitValue+1,   integer.value()) ;
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TestTxnSwitching.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TestTxnSwitching.java
index 0000000,0000000..2d0b025
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TestTxnSwitching.java
@@@ -1,0 -1,0 +1,192 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction;
++
++import static org.junit.Assert.assertEquals ;
++import static org.junit.Assert.fail ;
++
++import org.apache.jena.query.ReadWrite ;
++import org.junit.After ;
++import org.junit.Before ;
++import org.junit.Test ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.dboe.jenax.Txn ;
++import org.seaborne.dboe.transaction.txn.* ;
++import org.seaborne.dboe.transaction.txn.journal.Journal ;
++
++/** Tests of changing the thread state ... carefully */ 
++public class TestTxnSwitching {
++    TransInteger integer = new TransInteger(100) ;
++    Journal jrnl = Journal.create(Location.mem()) ;
++    
++    //Transactional transactional = TransactionalFactory.create(jrnl, integer) ;
++    TransactionalBase transactional ;
++    TransactionCoordinator txnMgr = new  TransactionCoordinator(jrnl) ;
++    {
++        txnMgr.add(integer) ;
++        transactional = new TransactionalBase(txnMgr) ;
++        txnMgr.start() ;
++    }
++
++    
++    @Before public void setup() {
++    }
++    
++    @After public void clearup() {
++    }
++    
++    @Test public void txnSwitch_01() {
++        long z = integer.value() ;
++        transactional.begin(ReadWrite.WRITE);
++        integer.inc(); 
++        
++        assertEquals(integer.value()+1, integer.get()) ;
++        assertEquals(z+1, integer.get()) ;
++        
++        TransactionCoordinatorState txnState = transactional.detach() ;
++        
++        assertEquals(integer.value(), integer.get()) ;
++        assertEquals(z, integer.get()) ;
++        
++        transactional.attach(txnState);
++
++        assertEquals(integer.value()+1, integer.get()) ;
++        assertEquals(z+1, integer.get()) ;
++        
++        transactional.commit() ;
++        transactional.end() ;
++        assertEquals(z+1, integer.get()) ;
++        assertEquals(z+1, integer.value()) ;
++    }
++    
++    @Test public void txnSwitch_02() {
++        long z = integer.value() ;
++        Txn.executeWrite(transactional, ()->integer.inc());
++        assertEquals(z+1, integer.value()) ;
++        
++        
++        //Transaction txn = txnMgr.begin(ReadWrite.WRITE) ;
++        transactional.begin(ReadWrite.WRITE);
++        integer.inc(); 
++        assertEquals(z+2, integer.get()) ;
++        TransactionCoordinatorState txnState = transactional.detach() ;
++        // Can't transactional read.
++        try { integer.read() ; fail() ; } catch (TransactionException ex) {}
++        
++        long z1 = Txn.calculateRead(transactional, ()->integer.get()) ;
++        assertEquals(z+1, z1) ;
++        transactional.attach(txnState) ;
++        integer.inc();
++        assertEquals(z+3, integer.get()) ;
++        
++        ThreadTxn threadTxn = ThreadTxn.threadTxnRead(transactional, ()->assertEquals(z+1, integer.get())) ;
++        threadTxn.run() ;
++        
++        transactional.commit() ;
++        transactional.end() ;
++    }
++    
++    // As 02 but with Transaction txn = txnMgr.begin(ReadWrite.WRITE) ;
++    // and txn calls and integer calls.  Not transactional calls but txnMgr calls.
++
++    @Test public void txnSwitch_03() {
++        long z = integer.value() ;
++        Txn.executeWrite(transactional, ()->integer.inc());
++        assertEquals(z+1, integer.value()) ;
++        
++        Transaction txn = txnMgr.begin(ReadWrite.WRITE) ;
++        integer.inc(); 
++        assertEquals(z+2, integer.get()) ;
++        TransactionCoordinatorState txnState = txnMgr.detach(txn) ;
++        
++        Transaction txnRead = txnMgr.begin(ReadWrite.READ) ;
++        assertEquals(z+1, integer.get()) ;
++        txnRead.end() ;
++        
++        try { integer.read() ; fail() ; } catch (TransactionException ex) {}
++        
++        txnMgr.attach(txnState);
++        
++        integer.inc();
++        assertEquals(z+3, integer.get()) ;
++        
++        ThreadTxn.threadTxnRead(transactional, ()->assertEquals(z+1, integer.get())).run();
++        txn.commit(); 
++        txn.end() ;
++    }
++    
++    // Switch between read and write all on one thread. 
++    @Test public void txnSwitch_04() {
++        long z = integer.value() ;
++        
++        transactional.begin(ReadWrite.READ);
++        TransactionCoordinatorState txnStateR1 = transactional.detach() ;
++        
++        ThreadTxn t1 = ThreadTxn.threadTxnRead(transactional, ()->assertEquals(z, integer.get() )) ;
++        ThreadTxn t2 = ThreadTxn.threadTxnRead(transactional, ()->assertEquals(z, integer.get() )) ;
++
++        transactional.begin(ReadWrite.WRITE);
++        integer.inc();
++        
++        TransactionCoordinatorState txnStateW1 = transactional.detach() ;
++        
++        // Currently, thread has no transaction.
++        long z1 = Txn.calculateRead(transactional, ()->integer.get() );
++        assertEquals(z, z1) ;
++        
++        // Back to writer.
++        transactional.attach(txnStateW1) ;
++        integer.inc();
++        TransactionCoordinatorState txnStateW2 = transactional.detach() ;
++        
++        try { integer.read() ; fail() ; } catch (TransactionException ex) {}
++        // To reader.
++        transactional.attach(txnStateR1) ;
++        assertEquals(z1, integer.read()) ;
++        t1.run() ;
++
++        // And the writer again.
++        TransactionCoordinatorState txnStateR2 = transactional.detach() ;
++        transactional.attach(txnStateW2) ;
++        integer.inc();
++        transactional.commit(); 
++        transactional.end() ;
++        
++        t2.run() ;
++        transactional.attach(txnStateR2) ;
++        assertEquals(z1, integer.read()) ;
++        transactional.end() ;
++    }
++    
++    // Some error cases.
++    @Test(expected=TransactionException.class)
++    public void txnSwitch_10() {
++        transactional.begin(ReadWrite.READ);
++        TransactionCoordinatorState txnState = transactional.detach() ;
++        transactional.attach(txnState); 
++        transactional.attach(txnState);
++    }
++    @Test(expected=TransactionException.class)
++    public void txnSwitch_11() {
++        transactional.begin(ReadWrite.READ);
++        TransactionCoordinatorState txnState1 = transactional.detach() ;
++        TransactionCoordinatorState txnState2 = transactional.detach() ;
++    }
++
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TransactionalInteger.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TransactionalInteger.java
index 0000000,0000000..6118758
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TransactionalInteger.java
@@@ -1,0 -1,0 +1,59 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction;
++
++import org.seaborne.dboe.transaction.txn.TransactionCoordinator ;
++import org.seaborne.dboe.transaction.txn.TransactionalBase ;
++
++/** 
++ * A Transactional (unit of begin/commit) of a single integer component.
++ * Testing support.  Use {@link TransInteger} for application code.
++ * @see TransInteger 
++ */
++public class TransactionalInteger extends TransactionalBase {
++    final private TransInteger integer ;
++
++    public TransactionalInteger(TransactionCoordinator coord, long v) {
++        super(coord) ;
++        integer = new TransInteger(v) ;
++        coord.add(integer) ;
++    }
++
++    public void inc() {
++        integer.inc() ;
++    }
++
++    /** Return the current value.
++     * If inside a transaction, return the tarnsaction view of the value.
++     * If not in a transaction return the state value (effectively
++     * a fast read transaction).   
++     */
++    public long get() {
++        return integer.get() ;
++    }
++    
++    public void set(long v) {
++        integer.set(v) ;
++    }
++    
++    /** Return the currently commited value */ 
++    public long value() {
++        return integer.value() ;
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/test/resources/log4j.properties
index 0000000,0000000..d6b299a
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/test/resources/log4j.properties
@@@ -1,0 -1,0 +1,6 @@@
++log4j.rootLogger=INFO, stdlog
++
++log4j.appender.stdlog=org.apache.log4j.ConsoleAppender
++## log4j.appender.stdlog.target=System.err
++log4j.appender.stdlog.layout=org.apache.log4j.PatternLayout
++log4j.appender.stdlog.layout.ConversionPattern=%d{HH:mm:ss} %-5p %-20c{1} :: %m%n

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/fuseki-tdb2/pom.xml
----------------------------------------------------------------------
diff --cc jena-db/fuseki-tdb2/pom.xml
index 0000000,0000000..8205005
new file mode 100644
--- /dev/null
+++ b/jena-db/fuseki-tdb2/pom.xml
@@@ -1,0 -1,0 +1,129 @@@
++<?xml version="1.0" encoding="utf-8"?>
++<!--
++   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.
++-->
++
++<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
++  <modelVersion>4.0.0</modelVersion>
++
++  <name>Mantis (${project.artifactId})</name>
++  <artifactId>fuseki-tdb2-server</artifactId>
++
++  <parent>
++    <groupId>org.seaborne.mantis</groupId>
++    <artifactId>mantis</artifactId>
++    <version>0.3.1-SNAPSHOT</version>
++  </parent> 
++
++  <packaging>jar</packaging>
++  <description>Apache Jena Fuseki combined with Mantis/TDB2</description>
++
++  <dependencies>
++
++    <dependency>
++      <groupId>org.seaborne.mantis</groupId>
++      <artifactId>tdb2</artifactId>
++      <version>0.3.1-SNAPSHOT</version>
++    </dependency>
++
++    <dependency>
++      <groupId>org.apache.jena</groupId>
++      <artifactId>jena-fuseki-core</artifactId>
++      <version>${ver.fuseki}</version>
++    </dependency>
++
++    <dependency>
++      <groupId>org.apache.jena</groupId>
++      <artifactId>jena-cmds</artifactId>
++    </dependency>
++
++    <dependency>
++      <groupId>org.slf4j</groupId>
++      <artifactId>slf4j-log4j12</artifactId>
++      <scope>runtime</scope>
++    </dependency>
++    
++    <dependency>
++      <groupId>log4j</groupId>
++      <artifactId>log4j</artifactId>
++      <scope>runtime</scope>
++    </dependency> 
++
++  </dependencies>
++
++  <build>
++    <plugins>
++
++      <plugin>
++        <groupId>org.apache.maven.plugins</groupId>
++        <artifactId>maven-shade-plugin</artifactId>
++        <version>2.4.3</version>
++        <configuration>
++          <shadedArtifactAttached>false</shadedArtifactAttached>
++          <transformers>
++            <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
++              <!--<mainClass>org.apache.jena.fuseki.cmd.FusekiCmd</mainClass>-->
++              <mainClass>org.seaborne.fuseki.cmd.FusekiTDB2Cmd</mainClass>
++            </transformer>
++            <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
++            <transformer implementation="org.apache.maven.plugins.shade.resource.ApacheLicenseResourceTransformer" />
++            <transformer implementation="org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformer">
++              <addHeader>false</addHeader>
++            </transformer>
++          </transformers>
++          <filters>
++            <filter>
++              <artifact>*:*</artifact>
++              <excludes>
++                <!-- Some jars are signed but shading breaks that.
++                     Don't include signing files.
++                -->
++                <exclude>META-INF/*.SF</exclude>
++                <exclude>META-INF/*.DSA</exclude>
++                <exclude>META-INF/*.RSA</exclude>
++              </excludes>
++            </filter>
++          </filters>
++        </configuration>
++        <executions>
++          <execution>
++            <phase>package</phase>
++            <!--<phase /><!- - Switch off -->
++            <goals>
++              <goal>shade</goal>
++            </goals>
++          </execution>
++        </executions>
++      </plugin>
++
++      <plugin>
++        <groupId>org.apache.maven.plugins</groupId>
++        <artifactId>maven-source-plugin</artifactId>
++        <executions>
++          <execution>
++            <id>attach-sources</id>
++            <goals>
++              <goal>jar-no-fork</goal>
++            </goals>
++          </execution>
++        </executions>
++      </plugin>
++
++    </plugins>
++
++  </build>
++  
++</project>

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/fuseki-tdb2/src/main/java/org/seaborne/fuseki/cmd/FusekiTDB2Cmd.java
----------------------------------------------------------------------
diff --cc jena-db/fuseki-tdb2/src/main/java/org/seaborne/fuseki/cmd/FusekiTDB2Cmd.java
index 0000000,0000000..25e1314
new file mode 100644
--- /dev/null
+++ b/jena-db/fuseki-tdb2/src/main/java/org/seaborne/fuseki/cmd/FusekiTDB2Cmd.java
@@@ -1,0 -1,0 +1,33 @@@
++/*
++ * 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.seaborne.fuseki.cmd;
++
++import org.apache.jena.fuseki.Fuseki;
++import org.apache.jena.fuseki.FusekiLogging;
++import org.apache.jena.fuseki.cmd.FusekiCmd;
++
++public class FusekiTDB2Cmd {
++
++    public static void main(String... args) {
++        FusekiLogging.setLogging() ;
++        Fuseki.serverLog.info("Fuseki-TDB2 integration");
++        FusekiCmd.main(args);
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/pom.xml
----------------------------------------------------------------------
diff --cc jena-db/pom.xml
index 0000000,0000000..9be78e4
new file mode 100644
--- /dev/null
+++ b/jena-db/pom.xml
@@@ -1,0 -1,0 +1,421 @@@
++<?xml version="1.0" encoding="utf-8"?>
++<!--
++   Licensed 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.
++ 
++   See the NOTICE file distributed with this work for additional
++   information regarding copyright ownership.
++-->
++
++<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
++  <modelVersion>4.0.0</modelVersion>
++
++  <groupId>org.seaborne.mantis</groupId>
++  <artifactId>mantis</artifactId>
++  <packaging>pom</packaging>
++  <url>https://github.com/afs/mantis</url>
++  <version>0.3.1-SNAPSHOT</version>
++  <name>Mantis :: Database Operation Environment</name>
++  <description>Mantis :: Database Operation Environment</description>
++
++  <licenses>
++    <license>
++      <name>Apache License 2.0</name>
++      <url>http://www.apache.org/licenses/LICENSE-2.0</url>
++    </license>
++  </licenses>
++
++  <developers>
++     <developer>
++      <name>Andy Seaborne</name>
++    </developer>
++  </developers>
++  <!--
++      <contributors></contributors>
++      <mailingLists></mailingLists>
++  -->
++
++  <issueManagement>
++    <url>https://github.com/afs/mantis/issues</url>
++  </issueManagement>
++
++  <ciManagement>
++    <url>https://travis-ci.org/afs/mantis</url>
++  </ciManagement>
++
++  <scm>
++    <url>https://github.com/afs/mantis</url>
++    <connection>scm:git:https://github.com/afs/mantis</connection>
++    <developerConnection>scm:git:git@github.com:afs/mantis.git</developerConnection>
++    <tag>HEAD</tag>
++  </scm>
++  
++  <properties>
++    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
++    <maven.build.timestamp.format>yyyy-MM-dd'T'HH:mm:ssZ</maven.build.timestamp.format>
++    <build.time.xsd>${maven.build.timestamp}</build.time.xsd>
++    <ver.jena>3.4.0</ver.jena>
++    <ver.tdb>${ver.jena}</ver.tdb>
++    <ver.fuseki>${ver.jena}</ver.fuseki>
++    <ver.junit>4.12</ver.junit>
++    <ver.slf4j>1.7.25</ver.slf4j>
++    <ver.log4j>1.2.17</ver.log4j>
++  </properties>
++
++  <modules>
++    <module>dboe-base</module>
++    <module>dboe-transaction</module>
++    <module>dboe-index</module>
++    <module>dboe-index-test</module>
++    <module>dboe-trans-data</module>
++    <module>tdb2</module>
++    <module>tdb2-cmds</module>
++    <module>fuseki-tdb2</module>
++  </modules>
++
++  <dependencyManagement>
++
++    <dependencies>
++      <!-- For clarity, Jena dependencies do not incldue TDB -->
++      <!-- For now, use more precise control
++      <dependency>
++        <groupId>org.apache.jena</groupId>
++        <artifactId>apache-jena-libs</artifactId>
++        <version>${ver.jena}</version>
++        <exclusions> 
++          <exclusion>
++            <groupId>org.apache.jena</groupId>
++            <artifactId>jena-tdb</artifactId>
++          </exclusion>
++        </exclusions>
++      </dependency>
++      -->
++
++      <!-- ARQ is essential anyway.
++           <dependency>
++           <groupId>org.apache.jena</groupId>
++           <artifactId>jena-core</artifactId>
++           <version>${ver.jena}</version>
++           </dependency>
++      -->
++
++      <dependency>
++        <groupId>org.apache.jena</groupId>
++        <artifactId>jena-arq</artifactId>
++        <version>${ver.jena}</version>
++      </dependency>
++
++      <dependency>
++        <groupId>org.apache.jena</groupId>
++        <artifactId>jena-cmds</artifactId>
++        <version>${ver.jena}</version>
++      </dependency>
++
++      <!-- Tests -->
++      <dependency>
++        <groupId>org.apache.jena</groupId>
++        <artifactId>jena-base</artifactId>
++        <version>${ver.jena}</version>
++        <classifier>tests</classifier>
++        <scope>test</scope>
++      </dependency>
++
++      <dependency>
++        <groupId>org.apache.jena</groupId>
++        <artifactId>jena-core</artifactId>
++        <version>${ver.jena}</version>
++        <classifier>tests</classifier>
++        <scope>test</scope>
++      </dependency>
++
++      <dependency>
++        <groupId>org.apache.jena</groupId>
++        <artifactId>jena-arq</artifactId>
++        <version>${ver.jena}</version>
++        <classifier>tests</classifier>
++        <scope>test</scope>
++      </dependency>
++
++      <dependency>
++        <groupId>junit</groupId>
++        <artifactId>junit</artifactId>
++        <version>${ver.junit}</version>
++        <scope>test</scope>
++      </dependency>
++
++      <!-- Logging -->
++    
++      <dependency>
++        <groupId>org.slf4j</groupId>
++        <artifactId>slf4j-api</artifactId>
++        <version>${ver.slf4j}</version>
++      </dependency>
++
++      <dependency>
++        <groupId>org.slf4j</groupId>
++        <artifactId>slf4j-jdk14</artifactId>
++        <version>${ver.slf4j}</version>
++        <optional>true</optional>
++      </dependency>
++
++      <dependency>
++        <groupId>org.slf4j</groupId>
++        <artifactId>slf4j-log4j12</artifactId>
++        <version>${ver.slf4j}</version>
++        <optional>true</optional>
++      </dependency>
++
++      <!-- And intercept any uses of Jakarta Commons Logging 
++           e.g. Apache Common HTTP client. 
++      -->
++      <dependency>
++        <groupId>org.slf4j</groupId>
++        <artifactId>jcl-over-slf4j</artifactId>
++        <version>${ver.slf4j}</version>
++        <optional>true</optional>
++      </dependency>
++
++      <dependency>
++        <groupId>log4j</groupId>
++        <artifactId>log4j</artifactId>
++        <version>${ver.log4j}</version>
++        <optional>true</optional>
++      </dependency>
++
++    </dependencies>
++    
++  </dependencyManagement>
++
++  <dependencies>
++    <dependency>
++      <groupId>junit</groupId>
++      <artifactId>junit</artifactId>
++    </dependency>
++
++    <!-- Logging in tests -->
++    <dependency>
++      <groupId>org.slf4j</groupId>
++      <artifactId>slf4j-log4j12</artifactId>
++      <scope>test</scope>
++    </dependency>
++      
++    <dependency>
++      <groupId>log4j</groupId>
++      <artifactId>log4j</artifactId>
++      <scope>test</scope>
++    </dependency>
++
++  </dependencies>
++  
++  <build>
++    <pluginManagement>
++      <plugins>
++        
++        <plugin>
++          <groupId>org.apache.maven.plugins</groupId>
++          <artifactId>maven-compiler-plugin</artifactId>
++          <version>3.6.0</version>
++          <configuration>
++            <encoding>UTF-8</encoding>
++            <debug>true</debug>
++            <debuglevel>source,lines,vars</debuglevel>
++            <optimize>true</optimize>
++            <source>1.8</source>
++            <target>1.8</target>
++          </configuration>
++        </plugin>
++
++        <plugin>
++          <groupId>org.apache.maven.plugins</groupId>
++          <artifactId>maven-surefire-plugin</artifactId>
++          <version>2.19</version> 
++          <!--
++          <configuration>
++            <includes>
++              <include>**/TS_*.java</include>
++            </includes>
++          </configuration>
++          -->
++        </plugin>
++
++        <plugin>
++          <groupId>org.apache.maven.plugins</groupId>
++          <artifactId>maven-deploy-plugin</artifactId>
++          <version>2.8.2</version>
++        </plugin>
++
++        <plugin>
++          <groupId>org.apache.maven.plugins</groupId>
++          <artifactId>maven-release-plugin</artifactId>
++          <version>2.5.3</version>
++          <configuration>
++            <goals>deploy</goals>
++            <autoVersionSubmodules>true</autoVersionSubmodules>
++          </configuration> 
++        </plugin>
++        
++        <plugin>
++          <groupId>org.apache.maven.plugins</groupId>
++          <artifactId>maven-resources-plugin</artifactId>
++          <version>3.0.0</version>
++          <configuration>
++            <encoding>UTF-8</encoding>
++          </configuration>
++        </plugin>
++
++        <plugin>
++          <groupId>org.apache.maven.plugins</groupId>
++          <artifactId>maven-dependency-plugin</artifactId>
++          <version>3.0.0</version>
++          <configuration>
++            <overWriteReleases>false</overWriteReleases>
++            <overWriteIfNewer>true</overWriteIfNewer>
++          </configuration>
++        </plugin>
++
++        <plugin>
++          <groupId>org.apache.maven.plugins</groupId>
++          <artifactId>maven-jar-plugin</artifactId>
++          <version>3.0.2</version>
++          <executions>
++            <execution>
++              <goals>
++                <goal>test-jar</goal>
++              </goals>
++          </execution>
++          </executions>
++        </plugin>
++
++        <plugin>
++          <groupId>org.apache.maven.plugins</groupId>
++          <artifactId>maven-source-plugin</artifactId>
++          <version>3.0.1</version>
++          <executions> 
++            <execution>
++              <id>attach-sources</id>
++              <!-- <phase>package</phase> package is the default -->
++              <goals>
++                <goal>jar-no-fork</goal> 
++              </goals>
++            </execution>
++            <execution>
++              <id>attach-sources-test</id>
++              <goals>
++                <goal>test-jar-no-fork</goal>
++              </goals>
++            </execution>
++          </executions>
++        </plugin>
++
++      </plugins> 
++
++    </pluginManagement>
++
++  </build>
++
++  <profiles>
++    <profile>
++      <id>release</id>
++      <activation>
++        <property>
++          <name>performRelease</name>
++          <value>true</value>
++        </property>
++      </activation>
++      <build>
++        <plugins>
++          <plugin>
++            <groupId>org.apache.maven.plugins</groupId>
++            <artifactId>maven-javadoc-plugin</artifactId>
++            <version>2.9.1</version>
++            <executions>
++              <execution>
++                <id>attach-javadocs</id>
++                <goals>
++                  <goal>jar</goal>
++                </goals>
++              </execution>
++            </executions>
++          </plugin>
++          <plugin>
++            <groupId>org.apache.maven.plugins</groupId>
++            <artifactId>maven-gpg-plugin</artifactId>
++            <version>1.6</version>
++            <!-- Does this interfer with external -Darguments=-Dgpg.keyname=
++                  i.e. is it additive?
++                  <configuration>
++              <gpgArguments>
++                <arg>- -digest-algo=SHA512</arg> <comment fix </gpgArguments>
++            </configuration>
++            -->
++            <executions>
++              <execution>
++                <id>sign-artifacts</id>
++                <phase>verify</phase>
++                <goals>
++                  <goal>sign</goal>
++                </goals>
++              </execution>
++            </executions>
++          </plugin>
++        </plugins>
++      </build>
++    </profile>
++
++  </profiles>
++
++  <distributionManagement>
++    <snapshotRepository>
++      <id>ossrh.snapshots</id>
++      <name>Sonatype Snapshots Repository</name>
++      <url>https://oss.sonatype.org/content/repositories/snapshots</url>
++    </snapshotRepository>
++    <repository>
++      <id>ossrh.releases</id>
++      <name>Sonatype Gateway to Maven Central</name>
++      <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
++    </repository>
++  </distributionManagement>
++  
++  <repositories>
++    <!-- Apache Snapshot Repository -->
++    <repository>
++      <id>apache-repository-snapshots</id>
++      <url>https://repository.apache.org/snapshots</url>
++      <releases>
++        <enabled>false</enabled>
++      </releases>
++      <snapshots>
++        <enabled>true</enabled>
++        <updatePolicy>daily</updatePolicy>
++        <checksumPolicy>fail</checksumPolicy>
++      </snapshots>
++    </repository>
++
++    <!-- Sonatype snapshot repository -->
++    <repository>
++      <id>sonatype.public</id>
++      <name>Sonatype Snapshots Repository</name>
++      <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
++      <releases>
++        <enabled>false</enabled>
++      </releases>
++      <snapshots>
++        <enabled>true</enabled>
++        <updatePolicy>daily</updatePolicy>
++        <checksumPolicy>fail</checksumPolicy>
++      </snapshots>
++    </repository>
++
++  </repositories>
++
++</project>

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2-cmds/.gitignore
----------------------------------------------------------------------
diff --cc jena-db/tdb2-cmds/.gitignore
index 0000000,0000000..731afb6
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2-cmds/.gitignore
@@@ -1,0 -1,0 +1,2 @@@
++DB*
++D.*


[40/50] [abbrv] jena git commit: JENA-1396: Merge //github/afs/mantis as subdirectory jena-db/

Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/bplustree/soak/CmdTestBPlusTree.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/bplustree/soak/CmdTestBPlusTree.java
index 0000000,0000000..9422821
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/bplustree/soak/CmdTestBPlusTree.java
@@@ -1,0 -1,0 +1,104 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.bplustree.soak;
++
++import org.seaborne.dboe.base.file.BlockAccessMem ;
++import org.seaborne.dboe.index.test.BaseSoakTest ;
++import org.seaborne.dboe.index.test.IndexTestLib ;
++import org.seaborne.dboe.sys.SystemIndex ;
++import org.seaborne.dboe.sys.Sys ;
++import org.seaborne.dboe.trans.bplustree.BPT ;
++import org.seaborne.dboe.trans.bplustree.BPlusTree ;
++import org.seaborne.dboe.trans.bplustree.BPlusTreeFactory ;
++import org.seaborne.dboe.trans.bplustree.BlockTracker ;
++
++public class CmdTestBPlusTree extends BaseSoakTest
++{
++    static public void main(String... argv) {
++        //argv = new String[] {"50", "150", "1000000"} ;
++//        System.setProperty("bpt:checking", "false") ;
++//        System.setProperty("bpt:duplication", "true") ;
++        new CmdTestBPlusTree(argv).mainRun() ;
++    }
++
++    protected CmdTestBPlusTree(String[] argv) {
++        super(argv) ;
++    }
++
++    @Override
++    protected void before() {
++        SystemIndex.setNullOut(true) ;
++        BlockTracker.collectHistory = false ;
++        // Forced mode
++        if ( true ) {
++            BlockAccessMem.SafeMode = true ;
++            BPT.CheckingNode = trueOrFalse("bpt:checking", false) ;
++            boolean duplication = trueOrFalse("bpt:duplication", false) ;
++            BPT.forcePromoteModes = true ;
++            BPT.promoteDuplicateNodes = duplication ;
++            BPT.promoteDuplicateRecords = duplication ;
++        }
++        if ( false ) {
++            // Transactions.
++        }
++        
++        System.out.printf("    BPT.CheckingNode            = %s\n", BPT.CheckingNode) ;
++        System.out.printf("    BPT.forcePromoteModes       = %s\n", BPT.forcePromoteModes) ;
++        System.out.printf("    BPT.promoteDuplicateNodes   = %s\n", BPT.promoteDuplicateRecords) ;
++        System.out.printf("    BPT.promoteDuplicateRecords = %s\n", BPT.promoteDuplicateRecords) ;
++    }
++    
++    private boolean trueOrFalse(String property, boolean dftValue) {
++        String s = System.getProperty(property) ;
++        if ( s == null )
++            return dftValue ;
++        if ( s.equalsIgnoreCase("true") || s.equalsIgnoreCase("T") || s.equalsIgnoreCase("1") )
++            return true ;
++        if ( s.equalsIgnoreCase("false") || s.equalsIgnoreCase("F") || s.equalsIgnoreCase("0") )
++            return false ;
++        System.err.println("Not recognized: "+property+"="+s);
++        return dftValue ;
++    }
++
++    @Override
++    protected void after() {}
++    
++    @Override
++    protected void runOneTest(int testCount, int order, int size, boolean debug) {
++        runOneTest(testCount, order, size) ;
++    }
++
++    @Override
++    protected void runOneTest(int testCount, int order, int size) {
++//        //System.err.println("runOneTest("+order+","+size+")") ;
++        BPlusTree bpt = BPlusTreeFactory.makeMem(order, Sys.SizeOfInt, 0) ;
++        bpt = BPlusTreeFactory.addTracking(bpt) ;
++        bpt.nonTransactional() ;
++        IndexTestLib.randTest(bpt, 5*size, size, true);
++        bpt.close() ;
++        
++        // Transaction.
++//        BPlusTree bpt = BPlusTreeFactory.makeMem(order, SystemLz.SizeOfInt, 0) ;
++//        Journal journal = Journal.create(Location.mem()) ;
++//        Transactional holder = TransactionalFactory.create(journal, bpt) ;
++//        holder.begin(ReadWrite.WRITE);
++//        IndexTestLib.randTest(bpt, 5*size, size, true);
++//        holder.commit() ;
++//        holder.end() ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/bplustree/soak/CmdTestBPlusTreeRewriter.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/bplustree/soak/CmdTestBPlusTreeRewriter.java
index 0000000,0000000..1688bdc
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/bplustree/soak/CmdTestBPlusTreeRewriter.java
@@@ -1,0 -1,0 +1,67 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.bplustree.soak;
++
++import org.seaborne.dboe.base.record.RecordFactory ;
++import org.seaborne.dboe.index.test.BaseSoakTest ;
++import org.seaborne.dboe.sys.SystemIndex ;
++import org.seaborne.dboe.trans.bplustree.BPT ;
++import org.seaborne.dboe.trans.bplustree.rewriter.TestBPlusTreeRewriterNonTxn ;
++
++public class CmdTestBPlusTreeRewriter extends BaseSoakTest
++{
++    static public void main(String... argv) {
++        new CmdTestBPlusTreeRewriter(argv).mainRun() ;
++    }
++
++    protected CmdTestBPlusTreeRewriter(String[] argv) {
++        super(argv) ;
++    }
++    
++    static int KeySize     = 4 ;
++    static int ValueSize   = 8 ;
++    
++    @Override
++    protected void before() {
++        SystemIndex.setNullOut(true) ;
++        // Forced mode
++        if ( false ) {
++            BPT.forcePromoteModes = true ;
++            BPT.promoteDuplicateNodes = true ;
++            BPT.promoteDuplicateRecords  = true ;
++        }
++        if ( false ) {
++            // Transactions.
++        }
++    }
++    
++    @Override
++    protected void after() { }
++
++    @Override
++    protected void runOneTest(int testCount, int order, int size, boolean debug) {
++        runOneTest(testCount, order, size) ;
++    }
++
++    @Override
++    protected void runOneTest(int testCount, int order, int size) {
++        RecordFactory recordFactory = new RecordFactory(KeySize, ValueSize) ;
++        TestBPlusTreeRewriterNonTxn.runOneTest(order, size, recordFactory, false) ;
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/data/TS_TransData.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/data/TS_TransData.java
index 0000000,0000000..d310e6b
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/data/TS_TransData.java
@@@ -1,0 -1,0 +1,34 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.data;
++
++import org.junit.runner.RunWith ;
++import org.junit.runners.Suite ;
++import org.junit.runners.Suite.SuiteClasses ;
++
++@RunWith(Suite.class)
++@SuiteClasses( {
++    TestTransIndexMem.class
++    , TestTransBlob.class
++    , TestTransBlobPersistent.class
++    , TestTransBinaryDataFileGeneral.class
++    , TestTransBinaryDataFile.class
++})
++
++public class TS_TransData { }
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/data/TestTransBinaryDataFile.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/data/TestTransBinaryDataFile.java
index 0000000,0000000..0dfa56d
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/data/TestTransBinaryDataFile.java
@@@ -1,0 -1,0 +1,219 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.data;
++
++import static org.junit.Assert.assertArrayEquals;
++import static org.junit.Assert.assertEquals;
++import static org.junit.Assert.assertNotEquals;
++
++import org.apache.jena.atlas.lib.Bytes ;
++import org.apache.jena.atlas.lib.StrUtils ;
++import org.junit.After ;
++import org.junit.Before ;
++import org.junit.Test ;
++import org.seaborne.dboe.base.file.* ;
++import org.seaborne.dboe.jenax.Txn ;
++import org.seaborne.dboe.transaction.ThreadTxn ;
++import org.seaborne.dboe.transaction.Transactional ;
++import org.seaborne.dboe.transaction.TransactionalFactory ;
++import org.seaborne.dboe.transaction.txn.ComponentId ;
++import org.seaborne.dboe.transaction.txn.journal.Journal ;
++
++
++public class TestTransBinaryDataFile {
++    private Journal journal ;
++    private BinaryDataFile baseBinData ;
++    private TransBinaryDataFile transBinData ;
++    private Transactional transactional ;
++    
++    @Before public void before() {
++        // XXX Builder.
++        journal = Journal.create(Location.mem()) ;
++        baseBinData = new BinaryDataFileMem() ;
++        BufferChannel chan = FileFactory.createBufferChannelMem() ;
++        ComponentId cid = ComponentId.allocLocal() ;
++        transBinData = new TransBinaryDataFile(baseBinData, cid, chan) ;
++        transBinData.open();
++        transactional = TransactionalFactory.createTransactional(journal, transBinData) ;    
++    }
++
++    @After public void after() { }
++    
++//    private static ByteBuffer str2bb(String x) {
++//        byte[] d = StrUtils.asUTF8bytes(x) ;
++//        ByteBuffer bb = ByteBuffer.wrap(d) ;
++//        return bb ;
++//    }
++    
++    private static long writeOne(Transactional transactional, TransBinaryDataFile transBinaryFile, String data) {
++        return 
++            Txn.calculateWrite(transactional, ()->{
++                byte[] bytes = StrUtils.asUTF8bytes(data) ;
++                int len = bytes.length ;
++                byte[] lenBytes = new byte[4] ;
++                Bytes.setInt(len, lenBytes);
++                // To work with strings, we write length,bytes.
++                long x = transBinaryFile.write(lenBytes);
++                transBinaryFile.write(bytes);
++                return x ;
++        }) ;
++    }
++    
++    private static String readOne(Transactional transactional, TransBinaryDataFile transBinaryFile, long posn) {
++        return Txn.calculateRead(transactional, ()->{
++            byte[] lenBytes = new byte[4] ;
++            long x = posn ;
++            int got = transBinaryFile.read(x, lenBytes) ;
++            x += got ;
++            
++            int len = Bytes.getInt(lenBytes) ;
++            byte[] bytes = new byte[len] ;
++            got = transBinaryFile.read(x, bytes) ;
++            x += got ;
++            return StrUtils.fromUTF8bytes(bytes) ;
++        }) ;
++    }
++    
++    @Test public void transObjectFile_str1() {
++        String str = "Hello World" ; 
++        long x = writeOne(transactional, transBinData, str) ;
++        String str2 = readOne(transactional, transBinData, x) ;
++        assertEquals(str, str2) ;
++    }
++
++    @Test public void transObjectFile_str2() {
++        String str1 = "" ; 
++        String str2 = "$" ; 
++        long x1 = writeOne(transactional, transBinData, str1) ;
++        long x2 = writeOne(transactional, transBinData, str2) ;
++        assertNotEquals("("+x1+","+x2+")", x1, x2) ;
++        
++        String out2 = readOne(transactional, transBinData, x2) ;
++        String out1 = readOne(transactional, transBinData, x1) ;
++        assertEquals(str1, out1) ;
++        assertEquals(str2, out2) ;
++    }
++
++    static byte[] bytes0 = new byte[0] ;
++    static int len0 = 0 ;
++    
++    static byte[] bytes1 = new byte[] {(byte)1,(byte)2,(byte)3} ;
++    static int len1 = bytes1.length ;
++    
++    static byte[] bytes2 = StrUtils.asUTF8bytes("TheNext") ;
++    static int len2 = bytes2.length ;
++
++
++    @Test public void transObjectFile_1() {
++        Txn.executeWrite(transactional, ()->{
++            long x = transBinData.write(bytes1) ;
++            assertEquals(0L, x) ;
++            assertEquals(len1, transBinData.length()) ;
++        }) ;
++    }
++    
++    @Test public void transObjectFile_2() {
++        Txn.executeWrite(transactional, ()->{
++            long x = transBinData.write(bytes1) ;
++            assertEquals(0L, x) ;
++            assertEquals(len1, transBinData.length()) ;
++            byte[] bytes1a = new byte[len1] ;  
++            int len = transBinData.read(0, bytes1a) ;
++            assertEquals(len1, len) ;
++            assertArrayEquals(bytes1, bytes1a);
++        }) ;
++    }
++
++    @Test public void transObjectFile_3() {
++        Txn.executeWrite(transactional, ()->{
++            long x1 = transBinData.write(bytes1) ;
++            long x2 = transBinData.write(bytes2) ;
++            byte[] bytes1a = new byte[len1] ;  
++            byte[] bytes2a = new byte[len2] ;
++            int len1a = transBinData.read(0, bytes1a) ;
++            int len2a = transBinData.read(len1a, bytes2a) ;
++            assertEquals(len1, len1a) ;
++            assertArrayEquals(bytes1, bytes1a);
++            assertEquals(len2, len2a) ;
++            assertArrayEquals(bytes2, bytes2a);
++        }) ;
++    }
++
++    @Test public void transObjectFile_4() {
++        Txn.executeWrite(transactional, ()->{
++            long x1 = transBinData.write(bytes1) ;
++            long x2 = transBinData.write(bytes2) ;
++        }) ;
++        Txn.executeRead(transactional, ()->{
++            byte[] bytes1a = new byte[len1] ;  
++            byte[] bytes2a = new byte[len2] ;
++            int len1a = transBinData.read(0, bytes1a) ;
++            int len2a = transBinData.read(len1a, bytes2a) ;
++            assertEquals(len1, len1a) ;
++            assertArrayEquals(bytes1, bytes1a);
++            assertEquals(len2, len2a) ;
++            assertArrayEquals(bytes2, bytes2a);
++        }) ;
++    }
++    
++    // As above but reverse the read order
++    @Test public void transObjectFile_5() {
++        Txn.executeWrite(transactional, ()->{
++            long x1 = transBinData.write(bytes1) ;
++            long x2 = transBinData.write(bytes2) ;
++        }) ;
++        Txn.executeRead(transactional, ()->{
++            byte[] bytes1a = new byte[len1] ;  
++            byte[] bytes2a = new byte[len2] ;
++            
++            int len2a = transBinData.read(len1, bytes2a) ;
++            int len1a = transBinData.read(0, bytes1a) ;
++            
++            assertEquals(len1, len1a) ;
++            assertArrayEquals(bytes1, bytes1a);
++            assertEquals(len2, len2a) ;
++            assertArrayEquals(bytes2, bytes2a);
++        }) ;
++    }
++
++
++    @Test public void transObjectFile_6() {
++        Txn.executeWrite(transactional, ()->{
++            long x1 = transBinData.write(bytes1) ;
++            long x2 = transBinData.write(bytes2) ;
++        }) ;
++        Txn.executeRead(transactional, ()->{
++            byte[] bytes2a = new byte[len2] ;
++            int len2a = transBinData.read(len1, bytes2a) ;
++            assertEquals(len2, len2a) ;
++            assertArrayEquals(bytes2, bytes2a);
++        }) ;
++    }
++    
++    @Test public void transObjectFile_7() {
++        ThreadTxn.threadTxnWriteAbort(transactional, ()->{
++            long x1 = transBinData.write(bytes1) ;
++        }) ;
++        
++        Txn.executeRead(transactional, ()->{
++            assertEquals(0L, transBinData.length()) ;
++        }) ;
++    }
++
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/data/TestTransBinaryDataFileGeneral.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/data/TestTransBinaryDataFileGeneral.java
index 0000000,0000000..add5983
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/data/TestTransBinaryDataFileGeneral.java
@@@ -1,0 -1,0 +1,58 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.data;
++
++import org.apache.jena.query.ReadWrite ;
++import org.seaborne.dboe.base.file.* ;
++import org.seaborne.dboe.transaction.Transactional ;
++import org.seaborne.dboe.transaction.TransactionalFactory ;
++import org.seaborne.dboe.transaction.txn.ComponentId ;
++import org.seaborne.dboe.transaction.txn.journal.Journal ;
++
++public class TestTransBinaryDataFileGeneral extends AbstractTestBinaryDataFile {
++    private Journal journal ;
++    private BinaryDataFile baseBinData ;
++    private TransBinaryDataFile transBinData ;
++    private Transactional transactional ;
++
++    @Override
++    protected BinaryDataFile createBinaryDataFile() {
++        // XXX Builder.
++        journal = Journal.create(Location.mem()) ;
++        baseBinData = new BinaryDataFileMem() ;
++        BufferChannel chan = FileFactory.createBufferChannelMem() ;
++        ComponentId cid = ComponentId.allocLocal() ;
++        transBinData = new TransBinaryDataFile(baseBinData, cid, chan) ;
++        transBinData.open();
++        transactional = TransactionalFactory.createTransactional(journal, transBinData) ;
++        //Non-transactional usage of a disposed file. 
++        transactional.begin(ReadWrite.WRITE) ;
++        return transBinData ; 
++        
++    }
++    
++    @Override
++    protected void releaseBinaryDataFile(BinaryDataFile file) {
++        if ( file.isOpen() ) {
++            transactional.commit() ;
++            transactional.end() ;
++            baseBinData.close() ;
++        }
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/data/TestTransBlob.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/data/TestTransBlob.java
index 0000000,0000000..114dcf1
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/data/TestTransBlob.java
@@@ -1,0 -1,0 +1,129 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.data;
++
++import java.util.concurrent.atomic.AtomicReference ;
++
++import org.apache.jena.query.ReadWrite ;
++import org.junit.After ;
++import org.junit.Assert ;
++import org.junit.Before ;
++import org.junit.Test ;
++import org.seaborne.dboe.base.file.BufferChannel ;
++import org.seaborne.dboe.base.file.BufferChannelMem ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.dboe.jenax.Txn ;
++import org.seaborne.dboe.transaction.ThreadTxn ;
++import org.seaborne.dboe.transaction.Transactional ;
++import org.seaborne.dboe.transaction.TransactionalFactory ;
++import org.seaborne.dboe.transaction.txn.ComponentId ;
++import org.seaborne.dboe.transaction.txn.journal.Journal ;
++
++public class TestTransBlob extends Assert {
++    private Journal         journal ;
++    private TransBlob       transBlob ;
++    private Transactional   transactional ;
++    
++    @Before public void before() {
++        journal = Journal.create(Location.mem()) ;
++        
++        BufferChannel chan = BufferChannelMem.create("TestTransBlob") ;
++        ComponentId cid = ComponentId.allocLocal() ;
++        transBlob = new TransBlob(cid, chan) ;
++        transactional = TransactionalFactory.createTransactional(journal, transBlob) ;    
++    }
++
++    @After public void after() { }
++    
++    public static void write(Transactional transactional, TransBlob transBlob, String data) {
++        Txn.executeWrite(transactional, ()->{
++            transBlob.setString(data);
++        }) ;
++    }
++    
++    public static String read(Transactional transactional, TransBlob transBlob) {
++        return Txn.calculateRead(transactional, ()->{
++            return transBlob.getString() ;
++        }) ;
++    }
++    
++    void threadRead(String expected) {
++        AtomicReference<String> result = new AtomicReference<>() ;
++        ThreadTxn.threadTxnRead(transactional, ()-> {
++            String s = transBlob.getString() ;
++            result.set(s);
++        }).run(); 
++        Assert.assertEquals(expected, result.get());
++    }
++    
++    // testing with real files in TestTransBlobPersistent
++    
++    @Test public void transBlob_1() {
++        String str = "Hello World" ; 
++        write(transactional, transBlob, str) ;
++        String str2 = read(transactional, transBlob) ;
++        assertEquals(str, str2) ;
++        String str3 = transBlob.getString() ;
++        assertEquals(str, str3) ;
++
++    }
++
++    // Verify visibility and transactions.
++    @Test public void transBlob_2() {
++        String str1 = "one" ; 
++        String str2 = "two" ;
++        write(transactional, transBlob, str1) ;
++        transactional.begin(ReadWrite.WRITE);
++        transBlob.setString(str2); 
++        
++        // Difefrent therad and transaction.
++        threadRead(str1) ;
++        
++        transactional.commit() ;
++        transactional.end() ;
++        threadRead(str2) ;
++    }
++    
++    // Verify visibility and transactions.
++    @Test public void transBlob_3() {
++        String str1 = "one" ; 
++        String str2 = "two" ;
++        write(transactional, transBlob, str1) ;
++        String s1 = transBlob.getString() ;
++        assertEquals(str1, s1) ;
++        String s2 = read(transactional, transBlob) ;
++        assertEquals(str1, s2) ;
++        
++        // Start now.
++        ThreadTxn tt = ThreadTxn.threadTxnRead(transactional, ()-> {
++            String sr = transBlob.getString() ;
++            Assert.assertEquals(str1, sr) ;
++        }) ;
++        
++        write(transactional, transBlob, str2) ;
++        
++        Txn.executeWrite(transactional, ()->{
++            transBlob.setString(str2) ; 
++            String s = transBlob.getString() ;
++            assertEquals(str2, s) ;
++        }) ;
++        // Run later, after W transaction.
++        tt.run();
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/data/TestTransBlobPersistent.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/data/TestTransBlobPersistent.java
index 0000000,0000000..053d7dc
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/data/TestTransBlobPersistent.java
@@@ -1,0 -1,0 +1,116 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.data;
++
++import org.apache.jena.util.FileUtils ;
++
++import org.apache.jena.atlas.lib.FileOps ;
++import org.junit.* ;
++import org.seaborne.dboe.base.file.BufferChannel ;
++import org.seaborne.dboe.base.file.BufferChannelFile ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.dboe.jenax.Txn;
++import org.seaborne.dboe.transaction.Transactional ;
++import org.seaborne.dboe.transaction.TransactionalFactory ;
++import org.seaborne.dboe.transaction.txn.ComponentId ;
++import org.seaborne.dboe.transaction.txn.journal.Journal ;
++
++public class TestTransBlobPersistent extends Assert {
++    private final static String DIR  = "target/blobtest" ;
++    private final static String JRNL = DIR+"/journal.jrnl" ;
++    private final static String DATA = DIR+"/blob.data" ;
++    
++    @BeforeClass public static void beforeClass() {
++        FileOps.ensureDir(DIR);
++    }
++    
++    @Before public void before() {
++        FileOps.deleteSilent(JRNL) ;
++        FileOps.deleteSilent(DATA) ;
++    }
++    
++    @AfterClass public static void afterClass() {
++        FileOps.deleteSilent(JRNL) ;
++        FileOps.deleteSilent(DATA) ;
++    }
++    
++    @Test public void transBlobFile_1() throws Exception {
++        Journal journal = Journal.create(Location.create(DIR)) ;
++        BufferChannel chan = BufferChannelFile.create(DATA) ;
++        ComponentId cid = ComponentId.allocLocal() ;
++        TransBlob transBlob = new TransBlob(cid, chan) ;
++        Transactional transactional = TransactionalFactory.createTransactional(journal, transBlob) ;   
++        String str = "Hello" ; 
++
++        TestTransBlob.write(transactional, transBlob, str) ;
++        
++        chan.close() ;
++        journal.close() ;
++        String s = FileUtils.readWholeFileAsUTF8(DATA) ;
++        assertEquals(str, s); 
++    }
++
++    @Test public void transBlobFile_2() throws Exception {
++        Journal journal = Journal.create(Location.create(DIR)) ;
++        BufferChannel chan = BufferChannelFile.create(DATA) ;
++        ComponentId cid = ComponentId.allocLocal() ;
++        TransBlob transBlob = new TransBlob(cid, chan) ;
++        Transactional transactional = TransactionalFactory.createTransactional(journal, transBlob) ;   
++        String str = "Hello1" ; 
++
++        Txn.executeWrite(transactional, ()->{
++            transBlob.setString("one");
++        }) ;
++        
++        Txn.executeWrite(transactional, ()->{
++            transBlob.setString("two");
++        }) ;
++        
++        chan.close() ;
++        journal.close() ;
++        String s = FileUtils.readWholeFileAsUTF8(DATA) ;
++        assertEquals("two", s); 
++    }
++    
++    // restart.
++    @Test public void transBlobFile_3() throws Exception {
++        String str = "Hello World" ; 
++        // Write out.
++        {
++            Journal journal = Journal.create(Location.create(DIR)) ;
++            BufferChannel chan = BufferChannelFile.create(DATA) ;
++            ComponentId cid = ComponentId.allocLocal() ;
++            TransBlob transBlob = new TransBlob(cid, chan) ;
++            Transactional transactional = TransactionalFactory.createTransactional(journal, transBlob) ;   
++            TestTransBlob.write(transactional, transBlob, str) ;
++            chan.close();
++            journal.close(); 
++        }
++        // Restart
++        {
++            Journal journal = Journal.create(Location.create(DIR)) ;
++            BufferChannel chan = BufferChannelFile.create(DATA) ;
++            ComponentId cid = ComponentId.allocLocal() ;
++            TransBlob transBlob = new TransBlob(cid, chan) ;
++            Transactional transactional = TransactionalFactory.createTransactional(journal, transBlob) ;   
++            String s = TestTransBlob.read(transactional, transBlob) ;
++            assertEquals(str, s); 
++        }
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/data/TestTransIndexMem.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/data/TestTransIndexMem.java
index 0000000,0000000..85cc9ff
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/data/TestTransIndexMem.java
@@@ -1,0 -1,0 +1,34 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.data;
++
++import org.seaborne.dboe.base.record.RecordFactory ;
++import org.seaborne.dboe.index.Index ;
++import org.seaborne.dboe.index.IndexMap ;
++import org.seaborne.dboe.index.test.AbstractTestIndex ;
++
++public class TestTransIndexMem extends AbstractTestIndex
++{
++    @Override
++    protected Index makeIndex(int kLen, int vLen)
++    {
++        RecordFactory rf = new RecordFactory(kLen, vLen) ;
++        return new IndexMap(rf) ;
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/recovery/TestRecovery.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/recovery/TestRecovery.java
index 0000000,0000000..adaf2c0
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/recovery/TestRecovery.java
@@@ -1,0 -1,0 +1,126 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.recovery;
++
++import java.nio.ByteBuffer ;
++import java.util.Arrays ;
++
++import org.apache.jena.atlas.lib.FileOps ;
++import org.junit.* ;
++import org.seaborne.dboe.base.file.BufferChannel ;
++import org.seaborne.dboe.base.file.BufferChannelFile ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.dboe.migrate.L ;
++import org.seaborne.dboe.trans.data.TransBlob ;
++import org.seaborne.dboe.transaction.txn.ComponentId ;
++import org.seaborne.dboe.transaction.txn.TransactionCoordinator ;
++import org.seaborne.dboe.transaction.txn.journal.Journal ;
++import org.seaborne.dboe.transaction.txn.journal.JournalEntry ;
++import org.seaborne.dboe.transaction.txn.journal.JournalEntryType ;
++
++// We need something to recover io order to test recovery.
++
++public class TestRecovery extends Assert {
++    private final static String DIR   = "target/recovery" ;
++    private final static String JRNL  = DIR + "/journal.jrnl" ;
++    private final static String DATA  = DIR + "/blob.data" ;
++    private final static String DATA1 = DIR + "/blob.data-1" ;
++    private final static String DATA2 = DIR + "/blob.data-2" ;
++
++    @BeforeClass public static void beforeClass() {
++        FileOps.ensureDir(DIR);
++    }
++    
++    @Before public void before() {
++        FileOps.deleteSilent(JRNL) ;
++        FileOps.deleteSilent(DATA) ;
++        FileOps.deleteSilent(DATA1) ;
++        FileOps.deleteSilent(DATA2) ;
++    }
++    
++    @AfterClass public static void afterClass() {
++        FileOps.deleteSilent(JRNL) ;
++        FileOps.deleteSilent(DATA) ;
++    }
++    
++    // Fake journal recovery.
++    @Test public void recoverBlobFile_1() throws Exception {
++        String str = "Hello Journal" ; 
++        ComponentId cid = ComponentId.allocLocal() ;
++//        ComponentIdRegistry registry = new ComponentIdRegistry() ;
++//        registry.register(cid, "Blob", 1) ;
++        
++        // Write out a journal.
++        {
++            Journal journal = Journal.create(Location.create(DIR)) ;
++            journal.write(JournalEntryType.REDO, cid, L.stringToByteBuffer(str)) ;
++            journal.writeJournal(JournalEntry.COMMIT) ;
++            journal.close(); 
++        }
++        
++        TransactionCoordinator coord = new TransactionCoordinator(Location.create(DIR)) ;
++        BufferChannel chan = BufferChannelFile.create(DATA) ;
++        TransBlob tBlob = new TransBlob(cid, chan) ;
++        coord.add(tBlob) ;
++        coord.start();
++        
++        ByteBuffer blob = tBlob.getBlob() ;
++        assertNotNull(blob); 
++        String s = L.byteBufferToString(blob) ;
++        assertEquals(str,s) ;
++        coord.shutdown();
++    }
++
++    @Test public void recoverBlobFile_2() throws Exception {
++        String str1 = "Recovery One" ; 
++        String str2 = "Recovery Two" ; 
++        ComponentId cid1 = ComponentId.allocLocal() ;
++        ComponentId cid2 = ComponentId.allocLocal() ;
++        
++        // Write out a journal for two components.
++        {
++            Journal journal = Journal.create(Location.create(DIR)) ;
++            journal.write(JournalEntryType.REDO, cid1, L.stringToByteBuffer(str1)) ;
++            journal.write(JournalEntryType.REDO, cid2, L.stringToByteBuffer(str2)) ;
++            journal.writeJournal(JournalEntry.COMMIT) ;
++            journal.close(); 
++        }
++        
++        Journal journal = Journal.create(Location.create(DIR)) ;
++        BufferChannel chan = BufferChannelFile.create(DATA) ;
++        TransBlob tBlob1 = new TransBlob(cid1, chan) ;
++        TransBlob tBlob2 = new TransBlob(cid2, chan) ;
++
++        TransactionCoordinator coord = new TransactionCoordinator(journal, Arrays.asList(tBlob1, tBlob2)) ;
++        coord.start();
++        
++        ByteBuffer blob1 = tBlob1.getBlob() ;
++        assertNotNull(blob1); 
++        String s1 = L.byteBufferToString(blob1) ;
++        assertEquals(str1,s1) ;
++        
++        ByteBuffer blob2 = tBlob2.getBlob() ;
++        assertNotNull(blob2); 
++        String s2 = L.byteBufferToString(blob2) ;
++        assertEquals(str2,s2) ;
++
++        assertNotEquals(str1,str2) ;
++        coord.shutdown();
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/test/resources/log4j.properties
index 0000000,0000000..d6b299a
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/test/resources/log4j.properties
@@@ -1,0 -1,0 +1,6 @@@
++log4j.rootLogger=INFO, stdlog
++
++log4j.appender.stdlog=org.apache.log4j.ConsoleAppender
++## log4j.appender.stdlog.target=System.err
++log4j.appender.stdlog.layout=org.apache.log4j.PatternLayout
++log4j.appender.stdlog.layout.ConversionPattern=%d{HH:mm:ss} %-5p %-20c{1} :: %m%n

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/pom.xml
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/pom.xml
index 0000000,0000000..8c4b21f
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/pom.xml
@@@ -1,0 -1,0 +1,41 @@@
++<?xml version="1.0" encoding="utf-8"?>
++<!--
++   Licensed 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.
++ 
++   See the NOTICE file distributed with this work for additional
++   information regarding copyright ownership.
++-->
++
++<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
++  <modelVersion>4.0.0</modelVersion>
++  <artifactId>dboe-transaction</artifactId>
++  <packaging>jar</packaging>
++  <name>Mantis (${project.artifactId})</name>
++
++  <parent>
++    <groupId>org.seaborne.mantis</groupId>
++    <artifactId>mantis</artifactId>
++    <version>0.3.1-SNAPSHOT</version>
++  </parent> 
++
++  <dependencies>
++
++    <dependency>
++      <groupId>org.seaborne.mantis</groupId>
++      <artifactId>dboe-base</artifactId>
++      <version>0.3.1-SNAPSHOT</version>
++    </dependency>
++
++  </dependencies>
++
++</project>

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/ComponentIdRegistry.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/ComponentIdRegistry.java
index 0000000,0000000..1fe339e
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/ComponentIdRegistry.java
@@@ -1,0 -1,0 +1,92 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction;
++
++
++
++public class ComponentIdRegistry {
++//    // Not stable across JVMs hence "local" 
++//    private ComponentId localCid = ComponentId.create("Local", L.uuidAsBytes(UUID.randomUUID())) ;
++//    
++//    // No! byte[].equals is Object.equals
++//    
++//    // ComponentId equality is by bytes (not display label)
++//    // so this maps bytes to a better form. 
++//    private Map<Holder, ComponentId> registry = new ConcurrentHashMap<>() ;
++//    
++//    public ComponentIdRegistry() { }
++//    
++//    public ComponentId registerLocal(String label, int index) {
++//        return register(localCid, label, index) ;
++//    }
++//    
++//    public ComponentId register(ComponentId base, String label, int index) {
++//        byte[] bytes = base.bytes() ;
++//        bytes = Arrays.copyOf(bytes, bytes.length) ;
++//        int x = Bytes.getInt(bytes, bytes.length - SystemBase.SizeOfInt) ;
++//        x = x ^ index ;
++//        Bytes.setInt(x, bytes, bytes.length-SystemBase.SizeOfInt) ;
++//        ComponentId cid = new ComponentId(label+"-"+index, bytes) ;
++//        Holder h = new Holder(bytes) ;
++//        registry.put(h, cid) ;
++//        return cid ;
++//    }
++//    
++//    public ComponentId lookup(byte[] bytes) {
++//        Holder h = new Holder(bytes) ;
++//        return registry.get(h) ;
++//    }
++//    
++//    public void reset() {
++//        registry.clear() ;
++//    }
++//    
++//    // Makes equality the value of the bytes. 
++//    static class Holder {
++//        private final byte[] bytes ;
++//
++//        Holder(byte[] bytes) { this.bytes = bytes ; }
++//
++//        @Override
++//        public int hashCode() {
++//            final int prime = 31 ;
++//            int result = 1 ;
++//            result = prime * result + Arrays.hashCode(bytes) ;
++//            return result ;
++//        }
++//
++//        @Override
++//        public boolean equals(Object obj) {
++//            if ( this == obj )
++//                return true ;
++//            if ( obj == null )
++//                return false ;
++//            if ( getClass() != obj.getClass() )
++//                return false ;
++//            Holder other = (Holder)obj ;
++//            if ( !Arrays.equals(bytes, other.bytes) )
++//                return false ;
++//            return true ;
++//        }
++//        
++//        @Override
++//        public String toString() {
++//            return Bytes.asHex(bytes) ;
++//        }
++//    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/ThreadTxn.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/ThreadTxn.java
index 0000000,0000000..bd29e8f
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/ThreadTxn.java
@@@ -1,0 -1,0 +1,144 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction;
++
++import java.util.Objects ;
++import java.util.concurrent.Executor ;
++import java.util.concurrent.Semaphore ;
++import java.util.concurrent.atomic.AtomicReference ;
++
++import org.apache.jena.query.ReadWrite ;
++
++import org.seaborne.dboe.sys.Sys ;
++import org.apache.jena.sparql.core.Transactional ;
++
++/**
++ * An action that will happen on a different thread later when {@link #run} is
++ * called. A thread is created and the transaction started during a call to the
++ * creation operations {@link #threadTxnRead} or {@link #threadTxnWrite}.
++ * The associated Runnable is called and the transaction completed when
++ * {@link #run} is called. Being on a thread, the state of the world the
++ * forked transaction sees is outside the creating thread which may itself be in a
++ * transaction. Warning: creating a write transaction inside a write transaction
++ * will cause deadlock.
++ */ 
++public class ThreadTxn {
++    
++    /** Create a thread-backed delayed READ transaction action. */
++    public static ThreadTxn threadTxnRead(Transactional trans, Runnable action) {
++        return ThreadTxn.create(trans, ReadWrite.READ, action, false) ;
++    }
++    
++    /** Create a thread-backed delayed WRITE  action.
++     * If called from inside a write transaction on the {@code trans},
++     * this will deadlock.
++     */
++    public static ThreadTxn threadTxnWrite(Transactional trans, Runnable action) {
++        return ThreadTxn.create(trans, ReadWrite.WRITE, action, true) ;
++    }
++    
++    /** Create a thread-backed delayed WRITE-abort action (testing). */
++    public static ThreadTxn threadTxnWriteAbort(Transactional trans, Runnable action) {
++        return ThreadTxn.create(trans, ReadWrite.WRITE, action, false) ;
++    }
++    
++    private final Semaphore semaStart ;
++    private final Semaphore semaFinish ;
++    private final AtomicReference<RuntimeException> thrownRuntimeException = new AtomicReference<>(null) ; 
++    private final AtomicReference<Error> thrownError = new AtomicReference<>(null) ;
++    private final Runnable action ;
++    
++    private ThreadTxn(Runnable action) {
++        this.action = action ;
++        this.semaStart = new Semaphore(0, true) ;
++        this.semaFinish = new Semaphore(0, true) ;
++    }
++    
++    /**
++     * Perform the Runnable, reporting any 
++     * {@link java.lang.RuntimeException} or {@link java.lang.Error}
++     */
++    public void run() { 
++        semaStart.release();
++        semaFinish.acquireUninterruptibly() ;
++        if ( thrownError.get() != null )
++            throw thrownError.get() ;
++        if ( thrownRuntimeException.get() != null )
++            throw thrownRuntimeException.get() ;
++    }
++    
++    // Called on the async thread.
++    private void trigger() {
++        try { action.run(); }
++        catch (Error error) { thrownError.set(error) ; throw error  ;}
++        catch (RuntimeException ex) { thrownRuntimeException.set(ex) ; throw ex ; }
++    }
++    
++    // System-shared executor.
++    private static Executor executor = Sys.executor ;
++    
++    /*package*/ static ThreadTxn create(Transactional trans, ReadWrite mode, Runnable action, boolean isCommit) {
++        Objects.requireNonNull(trans) ;
++        Objects.requireNonNull(mode) ;
++        Objects.requireNonNull(action) ;
++        
++        ThreadTxn threadAction = new ThreadTxn(action) ;
++        // Startup semaphore so that the thread has started by the
++        // time we exit this setup function. 
++        Semaphore semaStartup = new Semaphore(0, true) ;
++        executor.execute( ()-> {
++            // NB. trans.begin then semaStartup.release() ;
++            // This ensures that the transaction has really started.
++            trans.begin(mode) ;
++            
++            // Signal the creator (see below) that the transaction has started.
++            semaStartup.release() ;
++            
++            // Wait for the signal to run the action.
++            threadAction.semaStart.acquireUninterruptibly();
++            
++            try {
++                // Performane the action, catch and record any RuntimeException or Error. 
++                threadAction.trigger() ;
++                
++                // Finish transaction (if no throwable)
++                if ( mode == ReadWrite.WRITE ) {
++                    if ( isCommit )
++                        trans.commit();
++                    else
++                        trans.abort() ;
++                    trans.end() ;
++                } else {
++                    // Read
++                    if ( isCommit )
++                        trans.commit();
++                    trans.end() ;
++                }
++            } 
++            catch (Throwable ex) { 
++                       // Suppress now it has trigger transaction mechanism in
++                       // the presence of an unchecked exception.
++                       // Passed to the main thread via ThreadTxn
++            }
++            finally { threadAction.semaFinish.release() ; }
++        }) ;
++        // Don't return until the transaction has started.
++        semaStartup.acquireUninterruptibly();
++        return threadAction ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/TransInteger.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/TransInteger.java
index 0000000,0000000..5f4996c
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/TransInteger.java
@@@ -1,0 -1,0 +1,242 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction;
++
++import java.io.IOException ;
++import java.nio.ByteBuffer ;
++import java.util.concurrent.atomic.AtomicLong ;
++
++import org.apache.jena.atlas.io.IO ;
++import org.apache.jena.atlas.lib.FileOps ;
++import org.apache.jena.atlas.lib.InternalErrorException ;
++import org.apache.jena.atlas.logging.Log ;
++import org.apache.jena.query.ReadWrite ;
++import org.seaborne.dboe.migrate.L ;
++import org.seaborne.dboe.transaction.txn.ComponentId ;
++import org.seaborne.dboe.transaction.txn.TransactionalComponentLifecycle ;
++import org.seaborne.dboe.transaction.txn.TxnId ;
++
++/** A transaction component with an integer supporting MR+SW (=one writer AND many readers).
++ * @see TransactionalInteger
++ */
++public class TransInteger extends TransactionalComponentLifecycle<TransInteger.IntegerState> {
++
++    private final AtomicLong value = new AtomicLong(-1712) ;
++    private final String filename ;
++    
++    /** Per transaction state - and per thread safe because we subclass 
++     * TransactionalComponentLifecycle
++     */ 
++    static class IntegerState {
++        long txnValue ;
++        public IntegerState(long v) { this.txnValue = v ; }
++    }
++    
++    static int counter = 0 ;
++    
++    /** In-memory, non persistent, transactional integer */
++    public TransInteger() { this(0L) ; }
++    
++    /** In-memory, non persistent, transactional integer */
++    public TransInteger(long v) {
++        this(null, ComponentId.allocLocal()) ;
++        value.set(v) ;
++    }
++
++    /** Persistent, transactional integer. The persistent state is held in
++     *  filename.  When first initialized, the value is 0L. 
++     * @param filename  Persistent state
++     * @param cid        Component id
++     */
++    public TransInteger(String filename, ComponentId cid) {
++        super(cid) ;
++        this.filename = filename ;
++        // Set the value now for "fast read" transactions.
++        readLocation() ;
++    }
++    
++    private void readLocation() {
++        if ( filename != null ) {
++            if ( ! FileOps.exists(filename) ) {
++                value.set(0L) ;
++                writeLocation() ;
++                return ;
++            }
++            long x = read(filename) ;
++            value.set(x);
++        }
++    }
++
++    private void writeLocation() {
++        writeLocation(value.get()) ;
++    }
++    
++    private void writeLocation(long value) {
++        if ( filename != null ) {
++            write(filename, value) ;
++        }
++    }
++
++    //-- Read/write the value
++    // This should really be checksum'ed or other internal check to make sure IO worked.  
++    private static long read(String filename) {
++        try {
++            String str = IO.readWholeFileAsUTF8(filename) ;
++            if ( str.endsWith("\n") ) {
++                str = str.substring(0, str.length()-1) ;
++            }
++            str = str.trim() ;
++            return Long.parseLong(str) ;
++        } 
++        catch (IOException ex) {
++            Log.error(TransInteger.class, "IOException: " + ex.getMessage(), ex) ;
++            IO.exception(ex) ;
++        }
++        catch (NumberFormatException ex) {
++            Log.error(TransInteger.class, "NumberformatException: " + ex.getMessage()) ;
++            throw new InternalErrorException(ex) ;
++        }
++        // Not reached.
++        return Long.MIN_VALUE ;
++    }
++
++    
++    private static void write(String filename, long value) {
++        try { L.writeStringAsUTF8(filename, Long.toString(value)) ; } 
++        catch (IOException ex) {}
++        catch (NumberFormatException ex) {}
++    }
++    
++    private boolean recoveryAction = false ; 
++    
++    @Override
++    public void startRecovery() {
++        recoveryAction = false ;
++    }
++
++    @Override
++    public void recover(ByteBuffer ref) {
++        long x = ref.getLong() ;
++        value.set(x) ;
++        recoveryAction = true ;
++    }
++    
++
++    @Override
++    public void finishRecovery() {
++        if ( recoveryAction )
++            writeLocation();
++        // Leave true as a record.
++    }
++
++    @Override
++    public void cleanStart() {
++        recoveryAction = false ;
++    }
++    
++    /** Set the value, return the old value*/ 
++    public void inc() {
++        checkWriteTxn() ;
++        IntegerState ts = getDataState() ;
++        ts.txnValue++ ;
++    }
++    
++    /** Set the value, return the old value*/ 
++    public long set(long x) {
++        checkWriteTxn() ;
++        IntegerState ts = getDataState() ;
++        long v = ts.txnValue ;
++        ts.txnValue = x ;
++        return v ;
++    }
++    
++
++    /** Return the current value in a transaction. s*/
++    public long read() {
++        checkTxn();
++        return getDataState().txnValue ;
++    }
++
++    /** Return the current value.
++     * If inside a transaction, return the transaction view of the value.
++     * If not in a transaction return the state value (effectively
++     * a read transaction, optimized by the fact that reading the
++     * {@code TransInteger} state is atomic).
++     */
++    public long get() {
++        if ( super.isActiveTxn() )
++            return getDataState().txnValue ;
++        else
++            return value.get() ;
++    }
++
++    /** Read the current global state (that is, the last committed value) outside a transaction. */
++    public long value() {
++        return value.get() ;
++    }
++
++    @Override
++    protected IntegerState _begin(ReadWrite readWrite, TxnId txnId) {
++        return createState();
++    }
++
++    private IntegerState createState() {
++        return new IntegerState(value.get()) ;
++    }
++    
++    @Override
++    protected IntegerState _promote(TxnId txnId, IntegerState state) {
++        return createState();
++    }
++
++    @Override
++    protected ByteBuffer _commitPrepare(TxnId txnId, IntegerState state) {
++        ByteBuffer x = ByteBuffer.allocate(Long.BYTES) ;
++        x.putLong(state.txnValue) ;
++        return x ;
++    }
++
++    @Override
++    protected void _commit(TxnId txnId, IntegerState state) {
++        writeLocation(state.txnValue) ;
++    }
++
++    @Override
++    protected void _commitEnd(TxnId txnId, IntegerState state) {
++        value.set(state.txnValue) ;
++    }
++    @Override
++    protected void _abort(TxnId txnId, IntegerState state) {
++        // Nothing
++    }
++
++    @Override
++    protected void _complete(TxnId txnId, IntegerState state) {
++        // Nothing
++    }
++
++    @Override 
++    protected void _shutdown() {
++    }
++    
++    @Override
++    public String toString() {
++        return String.valueOf(super.getComponentId()) ; 
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/TransLogger.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/TransLogger.java
index 0000000,0000000..21bbd47
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/TransLogger.java
@@@ -1,0 -1,0 +1,151 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction;
++
++import java.nio.ByteBuffer ;
++
++import org.apache.jena.atlas.logging.FmtLog ;
++import org.seaborne.dboe.transaction.txn.ComponentId ;
++import org.seaborne.dboe.transaction.txn.SysTransState ;
++import org.seaborne.dboe.transaction.txn.Transaction ;
++import org.seaborne.dboe.transaction.txn.TransactionalComponent ;
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++/**
++ * Logging for transaction steps. This class is stateless in the transaction.
++ * When operations are called in the {@link TransactionalComponent} lifecycle,
++ * it logs the event.
++ */
++
++public class TransLogger implements TransactionalComponent {
++    private final Logger log ;
++    private final boolean everyEvent ; 
++    
++    /** Create a logger for begin-commit/abort-end */ 
++    public TransLogger() {
++        this(null, false) ;
++    }
++    
++    /** Create a logger, either just  begin-commit/abort-end or all steps */
++    public TransLogger(Logger logger) {
++        this(logger, false) ;
++    }
++
++    /** Create a logger, either just  begin-commit/abort-end or all steps */
++    public TransLogger(Logger logger, boolean all) {
++        if ( logger == null )
++            logger = LoggerFactory.getLogger(TransLogger.class) ;
++        this.log = logger ;
++        this.everyEvent = all ;
++    }
++
++    @Override
++    public ComponentId getComponentId() {
++//        if ( everyEvent )
++//            log.info("getComponentId") ; 
++        return null ;
++    }
++
++    @Override
++    public void startRecovery() {
++        if ( everyEvent )
++            log.info("startRecovery") ;
++    }
++
++    @Override
++    public void recover(ByteBuffer ref) {
++        // Is not called because this compoent vener writes a redo/undo action to the log.
++        if ( everyEvent )
++            log.info("recover") ;
++    }
++
++    @Override
++    public void finishRecovery() {
++        if ( everyEvent )
++            log.info("finishRecovery") ;
++    }
++
++    @Override
++    public void cleanStart() {
++        if ( everyEvent )
++            log.info("cleanStart") ;
++    }
++
++    @Override
++    public void begin(Transaction transaction) {
++        txnStep("begin", transaction) ;
++    }
++
++    @Override
++    public boolean promote(Transaction transaction) {
++        txnStep("promote", transaction) ;
++        return true ;
++    }
++
++    @Override
++    public ByteBuffer commitPrepare(Transaction transaction) {
++        if ( everyEvent )
++            txnStep("commitPrepare", transaction) ;
++        return null ;
++    }
++
++    @Override
++    public void commit(Transaction transaction) {
++        txnStep("commit", transaction) ;
++    }
++
++    @Override
++    public void commitEnd(Transaction transaction) {
++        if ( everyEvent )
++            txnStep("commitEnd", transaction) ;
++    }
++
++    @Override
++    public void abort(Transaction transaction) {
++        txnStep("abort", transaction) ;
++    }
++
++    @Override
++    public void complete(Transaction transaction) {
++        txnStep("complete", transaction) ;
++    }
++
++    @Override
++    public SysTransState detach() {
++        log.info("detach") ;
++        return null ;
++    }
++
++    @Override
++    public void attach(SysTransState systemState) {
++        log.info("attach") ;
++    }
++
++    @Override
++    public void shutdown() {
++        if ( everyEvent )
++            log.info("shutdown") ;
++    }
++    
++    private void txnStep(String opName, Transaction transaction) {
++        FmtLog.info(log, "%-8s %s", transaction.getTxnId(), opName) ;
++    }
++
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/TransMonitor.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/TransMonitor.java
index 0000000,0000000..668f97b
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/TransMonitor.java
@@@ -1,0 -1,0 +1,191 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction;
++
++import java.io.PrintStream ;
++import java.nio.ByteBuffer ;
++import java.util.LinkedHashMap ;
++import java.util.Map ;
++import java.util.concurrent.atomic.LongAdder ;
++import java.util.stream.Collectors ;
++
++import org.seaborne.dboe.transaction.txn.ComponentId ;
++import org.seaborne.dboe.transaction.txn.SysTransState ;
++import org.seaborne.dboe.transaction.txn.Transaction ;
++import org.seaborne.dboe.transaction.txn.TransactionalComponent ;
++
++/** This class is stateless in the transaction but it records counts of
++ * every {@link TransactionalComponent} operations.
++ * For each operation called "ABC" there is a member field "counterABC".  
++ */
++public class TransMonitor implements TransactionalComponent {
++    
++    // LongAdder for stats (better from mostly write, not consistent read)
++    
++    /** Counters, in initialization order */
++    private Map<String, LongAdder> counters = new LinkedHashMap<>() ;
++    private final ComponentId componentId ;
++    
++    private LongAdder allocCounter(String string) {
++        LongAdder counter = new LongAdder() ;
++        counters.put(string, counter) ;
++        return counter ;
++    }
++    public TransMonitor(ComponentId cid) { 
++        this.componentId = cid ;
++    }
++    
++    /** Reset all counters to zero */
++    public void reset() {
++        counters.forEach( (s,c) -> c.reset()) ; 
++    }
++
++    /** Get a copy of the counters with current values.
++     * The values are as of the point of this being called and are not changed
++     * by any later calls to monitored operations.  
++     */
++    public Map<String, Long> getAll() {
++        return counters.entrySet().stream()
++            .collect(Collectors.toMap(
++                                      e -> e.getKey(),
++                                      e -> e.getValue().sum()
++                ));
++    }
++    
++    /** Print the counters state. */
++    public void print() {
++        print(System.out) ;
++    }
++
++    /** Print the counters state. */
++    public void print(PrintStream ps) {
++        ps.println("Transaction Counters:") ;
++        counters.forEach( (s,c) -> {
++            ps.printf("   %-15s %4d\n", s, c.longValue()) ;
++        }) ;
++    }
++
++    public LongAdder counterGetComponentId = allocCounter("getComponentId") ;
++
++    @Override
++    public ComponentId getComponentId() {
++        counterGetComponentId.increment() ;
++        return componentId ;
++    }
++
++    public LongAdder counterStartRecovery = allocCounter("startRecovery") ;
++
++    @Override
++    public void startRecovery() {
++        counterStartRecovery.increment() ;
++    }
++
++    public LongAdder counterRecover = allocCounter("recover") ;
++
++    @Override
++    public void recover(ByteBuffer ref) {
++        counterRecover.increment() ;
++    }
++
++    public LongAdder counterFinishRecovery = allocCounter("finishRecovery") ;
++
++    @Override
++    public void finishRecovery() {
++        counterFinishRecovery.increment() ;
++    }
++    
++    public LongAdder counterCleanStart = allocCounter("finishRecovery") ;
++    
++    @Override
++    public void cleanStart() {
++        counterCleanStart.increment() ;
++    }
++    
++    public LongAdder counterBegin = allocCounter("begin") ;
++
++    @Override
++    public void begin(Transaction transaction) {
++        counterBegin.increment() ;
++    }
++    
++    public LongAdder counterPromote = allocCounter("promote") ;
++    
++    @Override
++    public boolean promote(Transaction transaction) {
++        counterPromote.increment() ;
++        return true ;
++    }
++
++    public LongAdder counterCommitPrepare = allocCounter("commitPrepare") ;
++
++    @Override
++    public ByteBuffer commitPrepare(Transaction transaction) {
++        counterCommitPrepare.increment() ;
++        return null ;
++    }
++
++    public LongAdder counterCommit = allocCounter("commit") ;
++
++    @Override
++    public void commit(Transaction transaction) {
++        counterCommit.increment() ;
++    }
++
++    public LongAdder counterCommitEnd = allocCounter("commitEnd") ;
++
++    @Override
++    public void commitEnd(Transaction transaction) {
++        counterCommitEnd.increment() ;
++    }
++
++    public LongAdder counterAbort = allocCounter("abort") ;
++
++    @Override
++    public void abort(Transaction transaction) {
++        counterAbort.increment() ;
++    }
++
++    public LongAdder counterComplete = allocCounter("complete") ;
++
++    @Override
++    public void complete(Transaction transaction) {
++        counterComplete.increment() ;
++    }
++
++    public LongAdder counterDetach = allocCounter("detach") ;
++    
++    @Override
++    public SysTransState detach() {
++        counterDetach.increment() ;
++        return null ;
++    }
++
++    public LongAdder counterAttach = allocCounter("attach") ;
++
++    @Override
++    public void attach(SysTransState systemState) {
++        counterAttach.increment() ;
++    }
++    
++    public LongAdder counterShutdown = allocCounter("shutdown") ;
++
++    @Override
++    public void shutdown() {
++        counterShutdown.increment() ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/Transactional.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/Transactional.java
index 0000000,0000000..fdef2b3
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/Transactional.java
@@@ -1,0 -1,0 +1,98 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction;
++
++import org.apache.jena.query.ReadWrite ;
++import org.seaborne.dboe.jenax.Txn ;
++
++/** Interface that encapsulates the  begin/abort|commit/end operations.
++ * <p>The read lifecycle is:
++ * <pre> begin(READ) ... end()</pre>
++ * <p>{@code commit} and {@code abort} are allowed. 
++ * <p>The write lifecycle is:
++ * <pre> begin(WRITE) ... abort() or commit()</pre>
++ * <p>{@code end()} is optional but preferred.
++ * <p>
++ * Helper code is available {@link Txn} so, for example:
++ * <pre>Txn.execRead(dataset, ()-> { ... sparql query ... });</pre> 
++ * <pre>Txn.execWrite(dataset, ()-> { ... sparql update ... });</pre>
++ * <p>
++ * Directly called, code might look like:
++ * <pre>
++ *     Transactional object = ...
++ *     object.begin(ReadWrite.READ) ;
++ *     try {
++ *       ... actions inside a read transaction ...
++ *     } finally { object.end() ; }
++ * </pre>
++ * or
++ * <pre>
++ *     Transactional object = ...
++ *     object.begin(ReadWrite.WRITE) ;
++ *     try {
++ *        ... actions inside a write transaction ...
++ *        object.commit() ;
++ *     } finally {
++ *        // This causes an abort if {@code commit} has not been called.
++ *        object.end() ;
++ *     }
++ * </pre>
++ * Exceptions will not be thrown.
++ * If any do occur, this indicates serious internal problems with the transaction system.
++ */
++public interface Transactional extends org.apache.jena.sparql.core.Transactional
++{
++   /** Start either a READ or WRITE transaction */
++   @Override
++   public void begin(ReadWrite readWrite) ;
++   
++   /** Attempt to promote a read transaction to a write transaction.
++    * This is not guaranteed to succeed - any changes by another write transaction
++    * may restrict promotion.  
++    * <p>
++    * In the MR+SW implementation, any intervening write transaction will block promotion.
++    * <p>
++    * Promoting a transaction which is already a write transaction will return true. 
++    * <p>
++    * Consider also:
++    *  <pre>
++    *    .end() ;
++    *    .begin(WRITE) ;
++    *  </pre>
++    *  to see any intermediate commits from another writer.
++    * 
++    * @return boolean indicating whether the transaction is now a write transaction or not.
++    */
++   public boolean promote() ;
++
++   /** Commit a transaction - finish the transaction and make any changes permanent (if a "write" transaction) */
++   @Override
++   public void commit() ;
++   
++   /** Abort a transaction - finish the transaction and undo any changes (if a "write" transaction) */  
++   @Override
++   public void abort() ;
++   
++   /** Finish the transaction - if a write transaction and commit() has not been called, then abort. */
++   @Override
++   public void end() ;
++   
++   /** Say whether inside a transaction. */ 
++   @Override
++   public boolean isInTransaction() ;
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/TransactionalFactory.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/TransactionalFactory.java
index 0000000,0000000..9daa686
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/TransactionalFactory.java
@@@ -1,0 -1,0 +1,59 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction;
++
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.dboe.transaction.txn.TransactionCoordinator ;
++import org.seaborne.dboe.transaction.txn.TransactionalBase ;
++import org.seaborne.dboe.transaction.txn.TransactionalComponent ;
++import org.seaborne.dboe.transaction.txn.TransactionalSystem ;
++import org.seaborne.dboe.transaction.txn.journal.Journal ;
++
++/** Helper operations for creating a {@link Transactional}.
++ * The operations capture some common patterns.
++ */
++public class TransactionalFactory {
++
++    /** Create, and start, management of a number of {@link TransactionalComponent}s */ 
++    public static Transactional createTransactional(Location location, TransactionalComponent ... elements) {
++        TransactionCoordinator coord = new TransactionCoordinator(location) ;
++        return createTransactional(coord, elements) ;
++    }
++
++    /** Create, and start, management of a number of {@link TransactionalComponent}s */ 
++    public static Transactional createTransactional(Journal journal, TransactionalComponent ... elements) {
++        TransactionCoordinator coord = new TransactionCoordinator(journal) ;
++        return createTransactional(coord, elements) ;
++    }
++
++    private static Transactional createTransactional(TransactionCoordinator coord, TransactionalComponent[] elements) {
++        for ( TransactionalComponent tc : elements ) {
++            coord.add(tc) ;
++        }
++        TransactionalBase base = new TransactionalBase(coord) ;
++        coord.start() ;
++        return base ;
++    }
++    
++    /** Create, but do not start, a {@link TransactionalSystem} from a {@link TransactionCoordinator} */
++    public static TransactionalSystem createTransactionalSystem(TransactionCoordinator coord) {
++        return new TransactionalBase(coord) ;
++    }
++
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/TransactionalMonitor.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/TransactionalMonitor.java
index 0000000,0000000..a9e1d8f
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/TransactionalMonitor.java
@@@ -1,0 -1,0 +1,38 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction;
++
++import org.apache.jena.query.ReadWrite ;
++
++/** Interface for the Transactional interface */
++public interface TransactionalMonitor {
++    default void startBegin(ReadWrite mode)     {}
++    default void finishBegin(ReadWrite mode)    {}
++
++    default void startPromote()     {}
++    default void finishPromote()    {}
++
++    default void startCommit()      {}
++    default void finishCommit()     {}
++
++    default void startAbort()       {}
++    default void finishAbort()      {}
++
++    default void startEnd()         {}
++    default void finishEnd()        {}
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/ComponentGroup.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/ComponentGroup.java
index 0000000,0000000..f38760e
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/ComponentGroup.java
@@@ -1,0 -1,0 +1,77 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction.txn;
++
++import java.util.Arrays ;
++import java.util.HashMap ;
++import java.util.Map ;
++import java.util.Objects ;
++import java.util.function.BiConsumer ;
++import java.util.function.Consumer ;
++
++import org.apache.jena.atlas.logging.Log ;
++
++final
++public class ComponentGroup {
++    private Map<ComponentId, TransactionalComponent> group = new HashMap<>() ;
++    
++    public ComponentGroup(TransactionalComponent...components) {
++        Arrays.asList(components).forEach(this::add);
++    }
++
++    private ComponentGroup(Map<ComponentId, TransactionalComponent> group) {
++        this.group.putAll(group); 
++    }
++    
++    public void add(TransactionalComponent component) {
++        Objects.requireNonNull(component) ;
++        //Log.info(this, "add("+component.getComponentId()+")") ;
++        if ( component.getComponentId() == null )
++            Log.warn(this,  "Null component id - likely to be overwritten: "+component) ;
++            
++        if ( group.containsKey(component.getComponentId()) ) {
++            Log.warn(this,  component.getComponentId().toString()) ;
++            Log.warn(this,  "Add component already in the group: "+component) ;
++        }
++        
++        group.put(component.getComponentId(), component) ;
++    }
++    
++    /*package*/ void remove(ComponentId componentId) {
++        group.remove(componentId) ;
++    }
++
++    public TransactionalComponent findComponent(ComponentId componentId) {
++        return group.get(componentId) ;
++    }
++    
++    public void forEachComponent(Consumer<? super TransactionalComponent> action) {
++        group.values().forEach(action) ;
++    }
++    
++    public void forEach(BiConsumer<ComponentId, TransactionalComponent> action) {
++        group.forEach(action);
++    }
++
++    public void addAll(ComponentGroup components) {
++        this.group.putAll(components.group); 
++    }
++    
++    public int size() { return group.size() ; }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/ComponentId.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/ComponentId.java
index 0000000,0000000..31081cd
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/ComponentId.java
@@@ -1,0 -1,0 +1,123 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction.txn;
++
++import java.util.Arrays ;
++import java.util.UUID ;
++
++import org.apache.jena.atlas.lib.Bytes ;
++
++/** A {@code ComponentId} consists of two parts: a globally unique context 
++ * (roughly - the domain where the {@code ComponentId} is valid)
++ * and an id within the context.
++ * Context is often a single coordinator but can be, for example,
++ * a distributed transaction coordinator.    
++ */
++
++public class ComponentId {
++    // Fixed size. 
++    public static final int SIZE = 4 ;
++    private final UUID coordinatorId ;
++    private final byte[] bytes ;
++    // Just a helper for development.  Not persisted in the journal.
++    private final String displayName ;
++    
++    /** Create a new ComponentId from the given bytes.
++     * The bytes are <em>not</em> copied. 
++     * The caller must not modify them after this call.
++     * The static method {@link #create(String, byte[])}
++     * does the copy.
++     */
++    private ComponentId(String label, UUID coordinatorId, byte[] bytes) {
++        this.coordinatorId = coordinatorId ;
++        if ( label == null )
++            label = "" ;
++        if ( bytes.length > SIZE )
++            throw new IllegalArgumentException("Bytes for ComponentId too long "+bytes.length+" > "+SIZE) ;
++        if ( bytes.length < SIZE )
++            // Make safe.
++            bytes = Arrays.copyOf(bytes, SIZE) ;
++        this.bytes = bytes ;
++        this.displayName = label ;
++    }
++    
++    public byte[] getBytes() { return bytes ; }
++    
++    public UUID getBaseId() { return coordinatorId ; }
++    
++    public String label() { return displayName ; }
++    
++    @Override
++    public String toString() { return displayName+"["+Bytes.asHex(bytes)+"]" ; }
++    
++    @Override
++    public int hashCode() {
++        final int prime = 31 ;
++        int result = 1 ;
++        result = prime * result + Arrays.hashCode(bytes) ;
++        return result ;
++    }
++
++    @Override
++    public boolean equals(Object obj) {
++        if ( this == obj )
++            return true ;
++        if ( obj == null )
++            return false ;
++        if ( getClass() != obj.getClass() )
++            return false ;
++        ComponentId other = (ComponentId)obj ;
++        if ( !Arrays.equals(bytes, other.bytes) )
++            return false ;
++        return true ;
++    }
++
++    /** Create a ComponentId from the given bytes */
++    public static ComponentId create(UUID coordinatorBase, byte[] bytes) {
++        bytes = Arrays.copyOf(bytes, bytes.length) ;
++        return new ComponentId(null, coordinatorBase, bytes) ;
++    }
++    
++    /** Given a base componentId, create a derived (different) one.
++     * This is deterministically done based on  baseComponentId and index.
++     * The label is just for display purposes.
++     */ 
++    public static ComponentId alloc(String label, UUID coordinatorBase, int index) {
++        byte[] bytes = Bytes.intToBytes(index) ;
++        return new ComponentId(label, coordinatorBase, bytes) ;
++    }
++    
++//    private static ComponentId create(byte[] bytes, String label, int index) {
++//        bytes = Arrays.copyOf(bytes, bytes.length) ;
++//        int x = Bytes.getInt(bytes, bytes.length-SystemBase.SizeOfInt) ;
++//        x = x ^ index ;
++//        Bytes.setInt(x, bytes, bytes.length - SystemBase.SizeOfInt) ;
++//        ComponentId cid = new ComponentId(label+"-"+index, bytes) ;
++//        return cid ;
++//    }
++    
++    static int counter = 0 ;
++    /** Return a fresh ComponentId (not preserved across JVM runs) */ 
++    public static ComponentId allocLocal() {
++        counter++ ;
++        UUID uuid = UUID.randomUUID() ;
++        return alloc("Local-"+counter, uuid, counter) ;
++    }
++
++}
++


[18/50] [abbrv] jena git commit: Update cmds

Posted by an...@apache.org.
Update cmds


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

Branch: refs/heads/jena-tdb2
Commit: cf4b7a8202f6b20d33c6ba1b7e396e85e6befeab
Parents: df59082
Author: Andy Seaborne <an...@seaborne.org>
Authored: Mon Sep 4 17:13:08 2017 +0100
Committer: Andy Seaborne <an...@seaborne.org>
Committed: Mon Sep 4 17:13:08 2017 +0100

----------------------------------------------------------------------
 .../src/main/java/tdb2/cmdline/CmdSub.java      | 49 +++++++++-----------
 .../src/main/java/tdb2/cmdline/CmdTDB.java      | 10 ++--
 tdb2-cmds/src/main/java/tdb2/tdbbackup.java     | 39 +++++++++++++---
 tdb2-cmds/src/main/java/tdb2/tdbcompact.java    | 45 ++++++++++++++++++
 tdb2-cmds/src/main/java/tdb2/tdbdump.java       |  3 +-
 tdb2-cmds/src/main/java/tdb2/tdbstats.java      |  3 +-
 tdb2-cmds/tdb2                                  |  2 +-
 .../java/org/seaborne/tdb2/sys/TDBInternal.java | 12 ++++-
 8 files changed, 119 insertions(+), 44 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/cf4b7a82/tdb2-cmds/src/main/java/tdb2/cmdline/CmdSub.java
----------------------------------------------------------------------
diff --git a/tdb2-cmds/src/main/java/tdb2/cmdline/CmdSub.java b/tdb2-cmds/src/main/java/tdb2/cmdline/CmdSub.java
index 03b12af..bda20c5 100644
--- a/tdb2-cmds/src/main/java/tdb2/cmdline/CmdSub.java
+++ b/tdb2-cmds/src/main/java/tdb2/cmdline/CmdSub.java
@@ -24,37 +24,34 @@ import java.util.Map;
 
 import jena.cmd.CmdException;
 
-public class CmdSub
-{
-    public interface Exec { public void exec(String[] argv) ; }
-    Map<String, Exec> dispatch = new HashMap<>() ;
-    
-    String subCmd ;
-    String args[] ;
-    
-    public CmdSub(String ...argv)
-    {
+public class CmdSub {
+    public interface Exec {
+        public void exec(String[] argv) ;
+    }
+    private Map<String, Exec> dispatch = new HashMap<>() ;
+
+    private String    subCmd ;
+    private String    args[] ;
+
+    public CmdSub(String... argv) {
         subCmd = subCommand(argv) ;
         args = cmdline(argv) ;
     }
-    
-    protected void exec()
-    {
+
+    protected void exec() {
         Exec exec = dispatch.get(subCmd) ;
         if ( exec == null )
-            throw new CmdException("No subcommand: "+subCmd) ;
+            throw new CmdException("No subcommand: " + subCmd) ;
         exec.exec(args) ;
     }
 
-    protected static String[] cmdline(String ... argv)
-    {
-        String [] a = new String[argv.length-1] ;
-        System.arraycopy(argv, 1, a, 0, argv.length-1) ;
-        return a ; 
+    protected static String[] cmdline(String... argv) {
+        String[] a = new String[argv.length - 1] ;
+        System.arraycopy(argv, 1, a, 0, argv.length - 1) ;
+        return a ;
     }
 
-    protected static String subCommand(String ... argv)
-    {
+    protected static String subCommand(String... argv) {
         if ( argv.length == 0 )
             throw new CmdException("Missing subcommand") ;
 
@@ -63,14 +60,12 @@ public class CmdSub
             throw new CmdException("Argument found where subcommand expected") ;
         return subCmd ;
     }
-    
-    protected void addSubCommand(String subCmdName, Exec exec)
-    {
+
+    protected void addSubCommand(String subCmdName, Exec exec) {
         dispatch.put(subCmdName, exec) ;
     }
-    
-    protected Collection<String> subCommandNames()
-    {
+
+    protected Collection<String> subCommandNames() {
         return dispatch.keySet() ;
     }
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/cf4b7a82/tdb2-cmds/src/main/java/tdb2/cmdline/CmdTDB.java
----------------------------------------------------------------------
diff --git a/tdb2-cmds/src/main/java/tdb2/cmdline/CmdTDB.java b/tdb2-cmds/src/main/java/tdb2/cmdline/CmdTDB.java
index 556cf67..da799fd 100644
--- a/tdb2-cmds/src/main/java/tdb2/cmdline/CmdTDB.java
+++ b/tdb2-cmds/src/main/java/tdb2/cmdline/CmdTDB.java
@@ -28,9 +28,8 @@ import org.apache.jena.sparql.core.DatasetGraph ;
 import org.apache.jena.system.JenaSystem ;
 import org.seaborne.dboe.base.file.Location;
 import org.seaborne.tdb2.TDB2;
-import org.seaborne.tdb2.store.DatasetGraphTDB;
-import org.seaborne.tdb2.sys.TDBInternal;
-import tdb2.cmdline.ModTDBDataset;
+import org.seaborne.tdb2.store.DatasetGraphSwitchable ;
+import org.seaborne.tdb2.sys.TDBInternal ;
 
 public abstract class CmdTDB extends CmdARQ
 {
@@ -70,9 +69,8 @@ public abstract class CmdTDB extends CmdARQ
         return getDataset().asDatasetGraph() ;
     }
 
-    protected DatasetGraphTDB getDatasetGraphTDB() {
-        DatasetGraph dsg = getDatasetGraph() ;
-        return TDBInternal.getDatasetGraphTDB(dsg) ;
+    protected DatasetGraphSwitchable getDatabaseContainer() {
+        return TDBInternal.getDatabaseContainer(getDatasetGraph());
     }
 
     protected Dataset getDataset() {

http://git-wip-us.apache.org/repos/asf/jena/blob/cf4b7a82/tdb2-cmds/src/main/java/tdb2/tdbbackup.java
----------------------------------------------------------------------
diff --git a/tdb2-cmds/src/main/java/tdb2/tdbbackup.java b/tdb2-cmds/src/main/java/tdb2/tdbbackup.java
index 3f75741..8291514 100644
--- a/tdb2-cmds/src/main/java/tdb2/tdbbackup.java
+++ b/tdb2-cmds/src/main/java/tdb2/tdbbackup.java
@@ -1,4 +1,4 @@
-/**
+/*
  * 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
@@ -18,11 +18,38 @@
 
 package tdb2;
 
-public class tdbbackup
+import arq.cmdline.ModLangOutput ;
+import org.seaborne.tdb2.store.DatasetGraphSwitchable ;
+import org.seaborne.tdb2.sys.DatabaseOps ;
+import tdb2.cmdline.CmdTDB;
+
+public class tdbbackup extends CmdTDB
 {
-    // For now, implement as "dump".
-    static public void main(String... argv) {
-        tdbdump.main(argv);
+    static ModLangOutput modLangOutput = new ModLangOutput() ;
+    
+    static public void main(String... argv)
+    { 
+        CmdTDB.init() ;
+        new tdbbackup(argv).mainRun() ;
     }
-}
 
+    protected tdbbackup(String[] argv)
+    {
+        super(argv) ;
+        addModule(modLangOutput) ;
+    }
+    
+    @Override
+    protected String getSummary()
+    {
+        return getCommandName()+" : Backup a TDB dataset" ;
+    }
+
+    @Override
+    protected void exec()
+    {
+        DatasetGraphSwitchable dsg = getDatabaseContainer();
+        String fn = DatabaseOps.backup(dsg);
+        System.out.println("Backup written to "+fn);
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/cf4b7a82/tdb2-cmds/src/main/java/tdb2/tdbcompact.java
----------------------------------------------------------------------
diff --git a/tdb2-cmds/src/main/java/tdb2/tdbcompact.java b/tdb2-cmds/src/main/java/tdb2/tdbcompact.java
new file mode 100644
index 0000000..373e822
--- /dev/null
+++ b/tdb2-cmds/src/main/java/tdb2/tdbcompact.java
@@ -0,0 +1,45 @@
+/*
+ * 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 tdb2;
+
+import org.seaborne.tdb2.store.DatasetGraphSwitchable ;
+import org.seaborne.tdb2.sys.DatabaseOps ;
+import tdb2.cmdline.CmdTDB;
+
+public class tdbcompact extends CmdTDB {
+    static public void main(String... argv) {
+        CmdTDB.init() ;
+        new tdbcompact(argv).mainRun() ;
+    }
+
+    protected tdbcompact(String[] argv) {
+        super(argv) ;
+    }
+
+    @Override
+    protected String getSummary() {
+        return getCommandName() + " : Compact a TDB2 dataset" ;
+    }
+
+    @Override
+    protected void exec() {
+        DatasetGraphSwitchable dsg = getDatabaseContainer() ;
+        DatabaseOps.compact(dsg) ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/cf4b7a82/tdb2-cmds/src/main/java/tdb2/tdbdump.java
----------------------------------------------------------------------
diff --git a/tdb2-cmds/src/main/java/tdb2/tdbdump.java b/tdb2-cmds/src/main/java/tdb2/tdbdump.java
index e562196..f54220c 100644
--- a/tdb2-cmds/src/main/java/tdb2/tdbdump.java
+++ b/tdb2-cmds/src/main/java/tdb2/tdbdump.java
@@ -52,8 +52,7 @@ public class tdbdump extends CmdTDB
     @Override
     protected void exec()
     {
-        DatasetGraph dsg = super.getDatasetGraphTDB() ;
-        
+        DatasetGraph dsg = getDatasetGraph() ;
         // Prefer stream over fully pretty output formats.
         RDFFormat fmt = modLangOutput.getOutputStreamFormat() ;
         // Stream writing happens naturally - no need to call StreamRDFWriter.

http://git-wip-us.apache.org/repos/asf/jena/blob/cf4b7a82/tdb2-cmds/src/main/java/tdb2/tdbstats.java
----------------------------------------------------------------------
diff --git a/tdb2-cmds/src/main/java/tdb2/tdbstats.java b/tdb2-cmds/src/main/java/tdb2/tdbstats.java
index 162bff2..7f53199 100644
--- a/tdb2-cmds/src/main/java/tdb2/tdbstats.java
+++ b/tdb2-cmds/src/main/java/tdb2/tdbstats.java
@@ -33,6 +33,7 @@ import org.seaborne.tdb2.store.DatasetGraphTDB;
 import org.seaborne.tdb2.store.NodeId;
 import org.seaborne.tdb2.store.nodetable.NodeTable;
 import org.seaborne.tdb2.store.nodetupletable.NodeTupleTable;
+import org.seaborne.tdb2.sys.TDBInternal ;
 import tdb2.cmdline.CmdTDB;
 import tdb2.cmdline.CmdTDBGraph;
 
@@ -90,7 +91,7 @@ public class tdbstats extends CmdTDBGraph {
 
     @Override
     protected void exec() {
-        DatasetGraphTDB dsg = getDatasetGraphTDB();
+        DatasetGraphTDB dsg = TDBInternal.getDatasetGraphTDB(getDatasetGraph());
         Node gn = getGraphName();
         StatsResults results = stats(dsg, gn);
         Stats.write(System.out, results);

http://git-wip-us.apache.org/repos/asf/jena/blob/cf4b7a82/tdb2-cmds/tdb2
----------------------------------------------------------------------
diff --git a/tdb2-cmds/tdb2 b/tdb2-cmds/tdb2
index 583ca7e..d2772db 100755
--- a/tdb2-cmds/tdb2
+++ b/tdb2-cmds/tdb2
@@ -79,7 +79,7 @@ TDB2_PKG="tdb2"
 case $CMD in
     "")
     ;;
-    tdbbackup | tdbdump | tdbloader | tdbquery | tdbstats | tdbupdate)
+    tdbbackup | tdbdump | tdbloader | tdbquery | tdbstats | tdbupdate | tdbcompact)
 	CMD_CLS="$TDB2_PKG"."$CMD"
 	;;
 ##     tools.*)

http://git-wip-us.apache.org/repos/asf/jena/blob/cf4b7a82/tdb2/src/main/java/org/seaborne/tdb2/sys/TDBInternal.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/sys/TDBInternal.java b/tdb2/src/main/java/org/seaborne/tdb2/sys/TDBInternal.java
index 3bd7427..0b57de8 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/sys/TDBInternal.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/sys/TDBInternal.java
@@ -106,10 +106,20 @@ public class TDBInternal {
      * Return the DatasetGraphTDB for a DatasetGraph, or null.
      * Use the {@link DatasetGraphTDB} with care.
      */
+    public static DatasetGraphSwitchable getDatabaseContainer(DatasetGraph dsg) {
+        if ( dsg instanceof DatasetGraphSwitchable )
+            return (DatasetGraphSwitchable)dsg;
+        throw new TDBException("Not a TDB database container");
+    }
+    
+    /**
+     * Return the DatasetGraphTDB for a DatasetGraph, or null.
+     * Use the {@link DatasetGraphTDB} with care.
+     */
     public static DatasetGraphTDB getDatasetGraphTDB(DatasetGraph dsg) {
         return unwrap(dsg);
     }
-    
+
     /**
      * Return the DatasetGraphTDB for a DatasetGraph, or throw an exception.
      */


[46/50] [abbrv] jena git commit: JENA-1396: Merge //github/afs/mantis as subdirectory jena-db/

Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/sys/FileLib.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/sys/FileLib.java
index 0000000,0000000..e1a827b
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/sys/FileLib.java
@@@ -1,0 -1,0 +1,85 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.sys;
++
++import java.io.FileNotFoundException ;
++import java.io.IOException ;
++import java.io.RandomAccessFile ;
++import java.nio.channels.FileChannel ;
++
++import org.apache.jena.atlas.io.IO ;
++import org.seaborne.dboe.base.file.ChannelManager ;
++import org.seaborne.dboe.base.file.FileException ;
++
++// => IO
++public class FileLib
++{
++    // Check whether still used.
++    
++    public static FileChannel openUnmanaged(String filename) {
++        return openUnmanaged(filename, "rw") ;
++    }
++
++    // And operation from BufferChannelFile
++    
++    public static FileChannel openUnmanaged(String filename, String mode) {
++        try {
++            @SuppressWarnings("resource")
++            RandomAccessFile out = new RandomAccessFile(filename, mode) ;
++            FileChannel channel = out.getChannel() ;
++            return channel ;
++        }
++        catch (FileNotFoundException e) {
++            IO.exception(e) ;
++            return null ;
++        }
++    }
++
++    // TODO remove and call ChannelManager directly 
++    public static FileChannel openManaged(String filename) {
++        return openManaged(filename, "rw") ;
++    }
++
++    // TODO remove and call ChannelManager directly 
++    public static FileChannel openManaged(String filename, String mode) {
++        return ChannelManager.acquire(filename, mode) ;
++    }
++
++    public static long size(FileChannel channel) {
++        try {
++            return channel.size() ;
++        } catch (IOException ex)
++        { IO.exception(ex) ; return -1L ; }
++    }
++
++    public static void truncate(FileChannel channel, long posn) {
++        try { channel.truncate(posn) ; }
++        catch (IOException ex) { IO.exception(ex) ; }
++    }
++
++    public static void close(FileChannel channel) {
++        ChannelManager.release(channel) ;
++    }
++
++    public static void sync(FileChannel channel) {
++        try {
++            channel.force(true) ;
++        } catch (IOException ex)
++        { throw new FileException("FileBase.sync", ex) ; }
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/sys/Names.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/sys/Names.java
index 0000000,0000000..0729e67
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/sys/Names.java
@@@ -1,0 -1,0 +1,150 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.sys;
++
++/** Names of things in TDB */
++public class Names
++{
++    /** Filename of the TDB configuration file */
++    public static final String TDB_CONFIG_FILE = "tdb.cfg" ;
++    
++    /** Filename of the TDB lock file */
++    public static final String TDB_LOCK_FILE = "tdb.lock" ; 
++    
++    /* System files */
++    public static final String extJournal               = "jrnl" ;
++    /* Component Transactional State */    
++    public static final String extState                 = "cts" ;           
++    
++    public static final String journalFileBase          = "journal" ;
++    public static final String journalFile              = journalFileBase+"."+extJournal ;
++
++    // ++ Elsewhere
++    /* RDF indexes */
++    public static final String primaryIndexTriples      = "SPO" ; 
++    public static final String[] tripleIndexes          = { primaryIndexTriples, "POS", "OSP" } ;
++
++    public static final String primaryIndexQuads        = "GSPO" ; 
++    public static final String[] quadIndexes            = { primaryIndexQuads, "GPOS", "GOSP", "POSG", "OSPG", "SPOG"} ;
++    
++    public static final String primaryIndexPrefix       = "GPU" ;
++    public static final String[] prefixIndexes          = { primaryIndexPrefix } ;
++    // ++ Elsewhere
++    
++    /* B+Trees - nodes file and records file */
++    public static final String extBptState              = "bpt" ;
++    public static final String extBptTree               = "idn" ;
++    public static final String extBptRecords            = "dat" ;
++
++//    /* B+Trees - single file per tree */
++//    public static final String btExt                    = "idx" ;
++    
++    /* ExtHash - dictionary file*/
++    public static final String extHashExt               = "exh" ;
++    public static final String extHashBucketExt         = "dat" ;
++
++    public static final String datasetConfig            = "config-tdb" ;        // name of the TDB configuration file.
++
++    /* Node files */
++    public static final String extBdfState              = "bdf" ;           // Node state
++    public static final String extObjNodeData           = "obj" ;
++    public static final String nodeTableBaseName        = "nodes" ;         // Node table
++
++    /** Prefixes file */
++    public static final String prefixTableBaseName      = "prefixes" ;      // Prefix node table 
++    
++    /** Optimizer / stats */
++    public static final String optStats                 = "stats.opt" ;
++    public static final String optFixed                 = "fixed.opt" ;
++    public static final String optNone                  = "none.opt" ; 
++    public static final String optDefault               = optFixed ;
++    
++    public static final String extMeta                  = "info" ;
++    public static final String directoryMetafile        = "this" ;          // Root name of the directory for a metafile.  
++
++    /** Name to indicate in-memory */ 
++    public static final String memName                  = "--mem--" ;
++
++    public static boolean isMem(String name)            { return memName.equals(name) ; }
++    
++    // ---- Names for Java properties in metadata files
++    
++    public static String makeKey(String root, String...elements)
++    { return makeName(root, elements) ; }
++    
++    public static String makeName(String root, String...elements)
++    {
++        StringBuilder sb = new StringBuilder() ;
++        sb.append(root) ;
++        for ( String s : elements )
++        {
++            if ( ! s.startsWith(".") )
++                sb.append(".") ;
++            sb.append(s) ;
++        }
++        return sb.toString() ;
++    }
++    
++    // Component elements.
++    public static final String elNode                 = "node" ;
++    public static final String elBPlusTree            = "bptree" ;
++    public static final String elIndex                = "index" ;
++    public static final String elObject               = "object" ;
++    
++    public static final String elType                 = "type" ;
++    public static final String elLayout               = "layout" ;
++    public static final String elVersion              = "version" ;
++    public static final String elTimestamp            = "timestamp" ;
++    public static final String elCreated              = "created" ;
++    
++    // Root names.
++    public static final String keyNS                    = "tdb" ;
++    public static final String keyNSNode                = makeName(keyNS, elNode) ;
++    public static final String keyNSBPlusTree           = makeName(keyNS, elBPlusTree) ;
++    
++    // Location metadata - in the directory wide metadata file.
++    public static final String kVersion               = makeName(keyNS, elVersion) ;
++    public static final String kCreatedDate           = makeName(keyNS, elTimestamp) ;    
++
++    // Node table metadata
++    public static final String kNodeTableType         = makeName(keyNS, elNode, elType) ;
++    public static final String kNodeTableLayout       = makeName(keyNS, elNode, elLayout) ;
++
++//    // Object file
++//    public static final String kObjectTableType         = makeName(keyNS, elObject, elType) ;
++//    
++//    // Default index metadata - in the index metadata file.
++//    
++//    public static final String kIndexType             = makeName(keyNS, elIndex, elType) ;
++//    public static final String kIndexFileLayout       = makeName(keyNS, elIndex, elLayout) ;
++    
++    // See also BPlusTreeParams for keyNSBPlusTree derived names.
++    
++//    // Current values
++//    public static final String currentIndexType         = IndexType.BPlusTree.getName() ;
++//    public static final String currentIndexFileVersion  = "v1" ;
++
++    // Property strings - Setup
++    
++    public static final String pNode2NodeIdCacheSize       = "tdb.cache.node2nodeid.size" ;
++    public static final String pNodeId2NodeCacheSize       = "tdb.cache.nodeid2node.size" ;
++    public static final String pNodeMissesCacheSize        = "tdb.cache.nodeMiss.size" ;
++    public static final String pBlockWriteCacheSize        = "tdb.cache.blockwrite.size" ;
++    public static final String pBlockReadCacheSize         = "tdb.cache.blockread.size" ;
++    public static final String pSyncTick                   = "tdb.synctick" ;
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/sys/ProcessUtils.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/sys/ProcessUtils.java
index 0000000,0000000..46cb252
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/sys/ProcessUtils.java
@@@ -1,0 -1,0 +1,156 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.sys;
++
++import java.io.BufferedReader;
++import java.io.IOException;
++import java.io.InputStreamReader;
++import java.lang.management.ManagementFactory;
++import java.util.ArrayList;
++import java.util.List;
++
++public class ProcessUtils {
++
++    private static int myPid = -1;
++
++    /**
++     * Tries to get the PID of the current process caching it for future calls
++     * since it won't change throughout the life of the process
++     * 
++     * @param fallback
++     *            Fallback PID to return if unable to determine the PID i.e. an
++     *            error code to return
++     * 
++     * @return PID of current process or provided {@code fallback} if unable to
++     *         determine PID
++     */
++    public static int getPid(int fallback) {
++        if (myPid != -1)
++            return myPid;
++
++        String runtimeBeanName = ManagementFactory.getRuntimeMXBean().getName();
++        if (runtimeBeanName == null) {
++            return useFallbackPid(fallback);
++        }
++
++        // Bean name will have format PID@hostname so we try to parse the PID
++        // portion
++        int index = runtimeBeanName.indexOf("@");
++        if (index < 0)
++            return useFallbackPid(fallback);
++        try {
++            // Parse and cache for future reuse
++            String pidData = runtimeBeanName.substring(0, index);
++            myPid = Integer.parseInt(pidData);
++            return myPid;
++        } catch (NumberFormatException e) {
++            // Invalid PID
++            return useFallbackPid(fallback);
++        }
++    }
++
++    private static int useFallbackPid(int fallback) {
++        // In the case where we can't determine our PID then treat ourselves as
++        // no owner and cache for future use
++        myPid = fallback;
++        return myPid;
++    }
++
++    /**
++     * Determines whether a given PID is alive
++     * 
++     * @param pid
++     *            PID
++     * @return True if the given PID is alive or unknown, false if it is dead
++     */
++    public static boolean isAlive(int pid) {
++        String pidStr = Integer.toString(pid);
++        try {
++            List<String> data = getProcessInfo(pidStr);
++
++            // Expect a line to contain the PID to indicate the process is
++            // alive
++            for (String lineData : data) {
++                if (lineData.contains(pidStr))
++                    return true;
++            }
++
++            // Did not find any lines mentioning the PID so we can safely
++            // assume that process is dead
++            return false;
++        } catch (IOException e) {
++            // If any error running the process to check for the live process
++            // then our check failed and for safety we assume the process is
++            // alive
++
++            Sys.errlog
++                    .warn("Your platform does not support checking process liveness so TDB disk locations cannot be reliably locked to prevent possible corruption due to unsafe multi-JVM usage");
++            return true;
++        }
++    }
++
++    /**
++     * Gets whether the platform we are running on will cause us to treat
++     * negative (i.e. invalid) PIDs as alive because of the format in which the
++     * command line process monitor application for the system returns errors
++     * for invalid PIDs
++     * 
++     * @return True if a negative PID is treated as alive on this platform or we
++     *         cannot determine liveness for PIDs on this platform, false
++     *         otherwise
++     */
++    public static boolean negativePidsTreatedAsAlive() {
++        return isAlive(-1);
++    }
++
++    /**
++     * Gets process information based on the given PID string
++     * 
++     * @param pidStr
++     *            PID string
++     * @return Output of running the OSes appropriate command line task info
++     *         application
++     * @throws IOException
++     *             Thrown if there is a problem running the command line
++     *             application or reading the data returned
++     */
++    private static List<String> getProcessInfo(String pidStr) throws IOException {
++        Process p;
++        if (Sys.isWindows) {
++            // Use the Windows tasklist utility
++            ProcessBuilder builder = new ProcessBuilder("tasklist", "/FI", "PID eq " + pidStr);
++            builder.redirectErrorStream(true);
++            p = builder.start();
++        } else {
++            // Use the ps utility
++            ProcessBuilder builder = new ProcessBuilder("ps", "-p", pidStr);
++            builder.redirectErrorStream(true);
++            p = builder.start();
++        }
++
++        // Run and read data from the process
++        try(BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
++            List<String> data = new ArrayList<>();
++            String line = null;
++            while ((line = reader.readLine()) != null) {
++                data.add(line);
++            }
++            return data;
++        }
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/sys/Sys.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/sys/Sys.java
index 0000000,0000000..356cb76
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/sys/Sys.java
@@@ -1,0 -1,0 +1,96 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.sys;
++
++import java.nio.ByteOrder;
++import java.util.concurrent.Executor ;
++import java.util.concurrent.Executors ;
++
++import org.apache.jena.atlas.logging.Log ;
++import org.seaborne.dboe.DBOpEnvException ;
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++/** Low level environment */ 
++public class Sys
++{
++    static final Logger log = LoggerFactory.getLogger("Sys") ;
++    
++    /** System log - use for general messages (a few) and warnings.
++     *  Generally, do not log events unless you want every user to see them every time.
++     *  Libraries and embedded systems should be seen and not heard.
++     *  @see #errlog 
++     */
++    
++    /** General system log */
++    public static final Logger syslog = LoggerFactory.getLogger("System") ;
++    /** Send warnings and error */
++    public static final Logger errlog = LoggerFactory.getLogger("System") ;
++    
++    /** Size, in bytes, of a Java long */
++    public static final int SizeOfLong              = Long.BYTES ; // Long.SIZE/Byte.SIZE ;
++    
++    /** Size, in bytes, of a Java int */
++    public static final int SizeOfInt               = Integer.BYTES ; //Integer.SIZE/Byte.SIZE ;
++    
++    public static final boolean is64bitSystem = determineIf64Bit() ;
++    
++    public static final ByteOrder NetworkOrder      = ByteOrder.BIG_ENDIAN ;
++
++    // To make the class initialize
++    static public void init() {}
++
++    public static void panic(Class<? > clazz, String string) {
++        Log.error(clazz, string) ;
++        throw new DBOpEnvException(string) ;
++    }
++    public static final boolean isWindows = determineIfWindows() ;	// Memory mapped files behave differently.
++
++    //Or look in File.listRoots.
++    //Alternative method:
++    //  http://stackoverflow.com/questions/1293533/name-of-the-operating-system-in-java-not-os-name
++    
++    private static boolean determineIfWindows() {
++    	String s = System.getProperty("os.name") ;
++    	if ( s == null )
++    		return false ;
++    	return s.startsWith("Windows ") ;
++	}
++    
++    /** A general thread pool */
++    public static Executor executor = Executors.newCachedThreadPool() ;
++
++    private static boolean determineIf64Bit() {
++        String s = System.getProperty("sun.arch.data.model") ;
++        if ( s != null ) {
++            boolean b = s.equals("64") ;
++            syslog.debug("System architecture: " + (b ? "64 bit" : "32 bit")) ;
++            return b ;
++        }
++        // Not a SUN VM
++        s = System.getProperty("java.vm.info") ;
++        if ( s == null ) {
++            log.warn("Can't determine the data model") ;
++            return false ;
++        }
++        log.debug("Can't determine the data model from 'sun.arch.data.model' - using java.vm.info") ;
++        boolean b = s.contains("64") ;
++        syslog.debug("System architecture: (from java.vm.info) " + (b ? "64 bit" : "32 bit")) ;
++        return b ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/sys/SystemFile.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/sys/SystemFile.java
index 0000000,0000000..5971904
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/sys/SystemFile.java
@@@ -1,0 -1,0 +1,23 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.sys;
++
++public class SystemFile {
++    public static final int ObjectFileWriteBufferSize = 8*1024 ;
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/sys/SystemIndex.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/sys/SystemIndex.java
index 0000000,0000000..0304b9c
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/sys/SystemIndex.java
@@@ -1,0 -1,0 +1,149 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.sys;
++
++import org.seaborne.dboe.DBOpEnvException ;
++import org.seaborne.dboe.base.block.FileMode ;
++
++public class SystemIndex
++{
++    /** Size, in bytes, of a pointer between blocks */
++    public static final int SizeOfPointer           = Sys.SizeOfInt ;
++    
++    public static final boolean is64bitSystem       = Sys.is64bitSystem ;
++
++    // To make the class initialize
++    static public void init() {}
++    
++    /** Size, in bytes, of a block */
++    public static final int BlockSize               = 8*1024 ; // intValue("BlockSize", 8*1024) ;
++
++    /** Size, in bytes, of a block for testing */
++    public static final int BlockSizeTest           = 1024 ; // intValue("BlockSizeTest", 1024) ;
++
++    /** Size, in bytes, of a block for testing */
++    public static final int BlockSizeTestMem         = 500 ;
++
++//    /** Size, in bytes, of a memory block */
++//    public static final int BlockSizeMem            = 32*8 ; //intValue("BlockSizeMem", 32*8 ) ;
++
++    /** Order of an in-memory BTree or B+Tree */
++    public static final int OrderMem                = 5 ; // intValue("OrderMem", 5) ;
++    
++    /** Size, in bytes, of a segment (used for memory mapped files) */
++    public static final int SegmentSize             = 8*1024*1024 ; // intValue("SegmentSize", 8*1024*1024) ;
++    
++    // ---- Cache sizes (within the JVM)
++
++    /** Size of Node to NodeId cache.
++     *  Used to map from Node to NodeId spaces.
++     *  Used for loading and for query preparation.
++     */
++    public static final int Node2NodeIdCacheSize    = intValue("Node2NodeIdCacheSize", ( is64bitSystem ? 100*1000 : 50*1000 )) ;
++
++    /** Size of NodeId to Node cache.
++     *  Used to map from NodeId to Node spaces.
++     *  Used for retriveing results.
++     */
++    public static final int NodeId2NodeCacheSize    = intValue("NodeId2NodeCacheSize", ( is64bitSystem ? 500*1000 : 50*1000 ) ) ;
++    
++    /** Size of Node lookup miss cache. */
++    public static final int NodeMissCacheSize       = 100 ;
++    
++    /** Size of the delayed-write block cache (32 bit systems only) (per file) */
++    public static final int BlockWriteCacheSize     = intValue("BlockWriteCacheSize", 2*1000) ;
++
++    /** Size of read block cache (32 bit systems only).  Increase JVM size as necessary. Per file. */
++    public static final int BlockReadCacheSize      = intValue("BlockReadCacheSize", 10*1000) ;
++    
++    private static int intValue(String name, int dft) { return dft ; }
++    
++    
++    public static void setNullOut(boolean nullOut)
++    { NullOut = nullOut ; }
++
++    /** Are we nulling out unused space in bytebuffers (records, points etc) */ 
++    public static boolean getNullOut()
++    { return NullOut ; }
++
++    /** null out (with the FillByte) freed up space in buffers */
++    public static boolean NullOut = false ;
++    
++    /** FillByte value for NullOut */
++    public static final byte FillByte = (byte)0xFF ;
++
++    public static boolean Checking = false ;       // This isn't used enough!
++    
++    // ---- File mode
++    
++    private static FileMode fileMode = null ;
++    public static FileMode fileMode()
++    { 
++        if ( fileMode == null )
++            fileMode = determineFileMode() ;
++        return fileMode ;
++    }
++
++    public static void setFileMode(FileMode newFileMode)
++    {
++        if ( fileMode != null )
++        {
++            Sys.log.warn("System file mode already determined - setting it has no effect") ;
++            return ;
++        }
++        fileMode = newFileMode ;
++    }
++    
++    // So the test suite can setup thing up ... very carefully.
++    /*package*/ static void internalSetFileMode(FileMode newFileMode)
++    {
++        fileMode = newFileMode ;
++    }
++    
++    private static FileMode determineFileMode()
++    {
++        // Be careful that this is not called very, very early, before --set might be seen.
++        // Hence delayed access above in fileMode().
++        
++        //String x = ARQ.getContext().getAsString(SystemTDB.symFileMode, "default") ;
++        String x = "default" ;
++        
++        if ( x.equalsIgnoreCase("direct") )
++        {
++            Sys.syslog.info("File mode: direct (forced)") ;
++            return FileMode.direct ;
++        }
++        if ( x.equalsIgnoreCase("mapped") )
++        {
++            Sys.syslog.info("File mode: mapped (forced)") ;
++            return FileMode.mapped ;
++        }
++        
++        if ( x.equalsIgnoreCase("default") )
++        {
++            if ( is64bitSystem )
++            {
++                Sys.syslog.debug("File mode: Mapped") ;
++                return FileMode.mapped ;
++            }
++            Sys.syslog.debug("File mode: Direct") ;
++            return FileMode.direct ;
++        }
++        throw new DBOpEnvException("Unrecognized file mode (not one of 'default', 'direct' or 'mapped': "+x) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/test/BufferTestLib.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/test/BufferTestLib.java
index 0000000,0000000..2da791d
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/test/BufferTestLib.java
@@@ -1,0 -1,0 +1,62 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.test;
++
++import java.nio.ByteBuffer ;
++
++import org.seaborne.dboe.base.block.Block ;
++
++public class BufferTestLib
++{
++    public static boolean sameValue(Block block1, Block block2)
++    {
++        if ( block1.getId() != block2.getId()) return false ;
++        ByteBuffer bb1 = block1.getByteBuffer() ; 
++        ByteBuffer bb2 = block2.getByteBuffer() ;
++        
++        if ( bb1.capacity() != bb2.capacity() ) return false ;
++        
++        for ( int i = 0 ; i < bb1.capacity() ; i++ )
++            if ( bb1.get(i) != bb2.get(i) ) return false ;
++        return true ;
++    }
++    
++    public static boolean sameValue(ByteBuffer bb1, ByteBuffer bb2)
++    {
++        if ( bb1.capacity() != bb2.capacity() ) return false ;
++        
++        int posn1 = bb1.position();
++        int limit1 = bb1.limit();
++        int posn2 = bb2.position();
++        int limit2 = bb2.limit();
++        
++        bb1.clear() ;
++        bb2.clear() ;
++        
++        try {
++            for ( int i = 0 ; i < bb1.capacity() ; i++ )
++                if ( bb1.get(i) != bb2.get(i) ) return false ;
++            return true ;
++        } finally {
++            bb1.position(posn1) ;
++            bb1.limit(limit1) ;
++            bb2.position(posn2) ;
++            bb2.limit(limit2) ;
++        }
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/main/java/org/seaborne/dboe/test/RecordLib.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/main/java/org/seaborne/dboe/test/RecordLib.java
index 0000000,0000000..569b21a
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/main/java/org/seaborne/dboe/test/RecordLib.java
@@@ -1,0 -1,0 +1,109 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.test;
++
++
++import java.util.ArrayList ;
++import java.util.Iterator ;
++import java.util.List ;
++
++import org.apache.jena.atlas.iterator.Iter ;
++import org.apache.jena.atlas.lib.Bytes ;
++import org.seaborne.dboe.base.record.Record ;
++import org.seaborne.dboe.base.record.RecordFactory ;
++
++/** Record support operations (mainly for testing using ints) */
++
++public class RecordLib
++{
++    // Size of a record when testing (one integer)
++    public final static int TestRecordLength = 4 ;
++    
++    public final static RecordFactory recordFactory    = new RecordFactory(TestRecordLength, 0) ; 
++    
++    public static Record intToRecord(int v) { return intToRecord(v, recordFactory) ; }
++    public static Record intToRecord(int v, int recLen) { return intToRecord(v, new RecordFactory(recLen, 0)) ; }
++    
++    public static Record intToRecord(int v, RecordFactory factory)
++    {
++        byte[] vb = Bytes.packInt(v) ;
++
++        int recLen = factory.recordLength() ;
++        byte[] bb = new byte[recLen] ;
++        int x = 0 ; // Start point in bb.
++        if ( recLen > 4 )
++            x = recLen-4 ;
++        
++        int len = Math.min(4, recLen) ;
++        int z = 4-len ; // Start point in vb
++    
++        // Furthest right bytes.
++        for ( int i = len-1 ; i >= 0 ; i-- ) 
++           bb[x+i] = vb[z+i] ; 
++        
++        return factory.create(bb) ;
++    }
++
++    public static List<Record> intToRecord(int[] v) { return intToRecord(v, recordFactory) ; }
++
++    public static List<Record> intToRecord(int[] v, int recLen)
++    { return intToRecord(v, new RecordFactory(recLen, 0)) ; }
++    
++    static List<Record> intToRecord(int[] v, RecordFactory factory)
++    {
++        List<Record> x = new ArrayList<>() ;
++        for ( int i : v )
++            x.add(intToRecord(i, factory)) ;
++        return x ;
++    }
++
++    public static int recordToInt(Record key)
++    {
++        return Bytes.getInt(key.getKey()) ;
++    }
++
++    public static List<Integer> toIntList(Iterator<Record> iter)
++    {
++        return Iter.toList(Iter.map(iter, item->recordToInt(item))) ;
++    }
++    
++    public static Record r(int v)
++    {
++        return RecordLib.intToRecord(v, RecordLib.TestRecordLength) ; 
++    }
++
++    public static int r(Record rec)
++    {
++        return RecordLib.recordToInt(rec) ; 
++    }
++
++    public static List<Integer> toIntList(int... vals)
++    {
++        List<Integer> x = new ArrayList<>() ;
++        for ( int i : vals )
++            x.add(i) ;
++        return x ;
++    }
++
++    public static List<Integer> r(Iterator<Record> iter)
++    {
++        return RecordLib.toIntList(iter) ;
++    }
++
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/ConfigTestDBOE.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/ConfigTestDBOE.java
index 0000000,0000000..174ebdc
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/ConfigTestDBOE.java
@@@ -1,0 -1,0 +1,76 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe;
++
++import org.apache.jena.atlas.lib.FileOps ;
++import org.seaborne.dboe.sys.Sys ;
++
++public class ConfigTestDBOE
++{
++    private static String testingDataRoot = "testing" ;
++    // Place under target
++    private static final String testingDir = "target/dboe-testing" ;
++    static boolean nonDeleteableMMapFiles = Sys.isWindows ;
++    
++    static boolean initialized = false ; 
++    
++    private static void init()
++    {
++        FileOps.ensureDir("target") ;
++        FileOps.ensureDir(testingDir) ;
++        initialized = true ;
++    }
++    
++    private static int count = 0 ;
++
++    public static void setTestingDataRoot(String dir) { testingDataRoot = dir ; }
++    public static String getTestingDataRoot() { return testingDataRoot ; }
++    
++    /** return a directory */ 
++    public static final String getCleanDir() {
++        init() ;
++        String dir = nonDeleteableMMapFiles ? getTestingDirUnique() : getTestingDir() ;
++        FileOps.ensureDir(dir); 
++        FileOps.clearDirectory(dir) ;
++        return dir ;
++    }
++    /** Get a empty directory name that has not been used before in this JVM */
++    
++    private static final String getTestingDirUnique()
++    {
++        init() ;
++    	String dn = testingDir+"/D-"+(++count) ;
++    	FileOps.ensureDir(dn) ;
++    	FileOps.clearDirectory(dn) ;
++    	return dn ; 
++    }
++    
++    public static final String getTestingDir()
++    {
++        init() ;
++        return testingDir ;
++    }
++    
++    public static final void deleteTestingDir()
++    {
++        if ( ! FileOps.exists(testingDir) )
++            return ;
++        FileOps.clearDirectory(testingDir) ;
++        FileOps.deleteSilent(testingDir) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/TC_DBOE_IO.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/TC_DBOE_IO.java
index 0000000,0000000..35a697d
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/TC_DBOE_IO.java
@@@ -1,0 -1,0 +1,60 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe;
++
++import org.apache.log4j.Level ;
++import org.apache.log4j.Logger ;
++import org.junit.AfterClass ;
++import org.junit.BeforeClass ;
++import org.junit.runner.RunWith ;
++import org.junit.runners.Suite ;
++import org.seaborne.dboe.base.block.FileMode ;
++import org.seaborne.dboe.base.block.TS_Block ;
++import org.seaborne.dboe.base.buffer.TS_Buffer ;
++import org.seaborne.dboe.base.file.TS_File ;
++import org.seaborne.dboe.base.record.TS_Record ;
++import org.seaborne.dboe.base.recordfile.TS_RecordFile ;
++import org.seaborne.dboe.sys.SystemIndex ;
++
++@RunWith(Suite.class)
++@Suite.SuiteClasses( {
++    TS_Block.class
++    , TS_File.class
++    , TS_Buffer.class
++    , TS_Record.class
++    , TS_RecordFile.class
++} )
++
++public class TC_DBOE_IO {
++    static {
++        if ( false )
++            SystemIndex.setFileMode(FileMode.direct) ;
++    }
++    
++    @BeforeClass static public void beforeClass()   
++    {
++        //org.apache.log4j.LogManager.resetConfiguration() ;
++        //org.apache.log4j.PropertyConfigurator.configure("log4j.properties") ;
++        Logger.getLogger("org.apache.jena.tdb.info").setLevel(Level.WARN) ;
++        //Logger.getLogger("com.hp.hpl.jena.tdb.exec").setLevel(Level.WARN) ;
++    }
++
++    @AfterClass static public void afterClass() {
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/block/AbstractTestBlockMgr.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/block/AbstractTestBlockMgr.java
index 0000000,0000000..1b8c695
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/block/AbstractTestBlockMgr.java
@@@ -1,0 -1,0 +1,133 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.block ;
++
++import java.nio.ByteBuffer ;
++
++import static org.apache.jena.atlas.lib.ByteBufferLib.fill ;
++import org.junit.Assert ;
++import org.junit.After ;
++import org.junit.Before ;
++import org.junit.Test ;
++import org.seaborne.dboe.base.block.Block ;
++import org.seaborne.dboe.base.block.BlockMgr ;
++
++public abstract class AbstractTestBlockMgr extends Assert {
++    static final public int BlkSize  = 256 ;
++
++    protected BlockMgr      blockMgr = null ;
++
++    @Before
++    public void before() {
++        blockMgr = make() ;
++        blockMgr.beginUpdate() ;
++    }
++
++    @After
++    public void after() {
++        if ( blockMgr != null ) {
++            blockMgr.endUpdate() ;
++            blockMgr.close() ;
++        }
++    }
++
++    @Test
++    public void file01() {
++        long x = blockMgr.allocLimit() ;
++        assertTrue("First allocLimit : " + x, x >= 0) ;
++        // Assume no recycling.
++        Block block = blockMgr.allocate(BlkSize) ;
++        assertTrue("Block inside allocate boundary", block.getId() >= x) ;
++        ByteBuffer bb = block.getByteBuffer() ;
++        fill(bb, (byte)1) ;
++        blockMgr.write(block) ;
++        blockMgr.release(block) ;
++    }
++
++    @Test
++    public void file02() {
++        Block block = blockMgr.allocate(BlkSize) ;
++        ByteBuffer bb = block.getByteBuffer() ;
++        fill(bb, (byte)1) ;
++        long id = block.getId() ;
++        blockMgr.write(block) ;
++        blockMgr.release(block) ;
++
++        Block block2 = blockMgr.getRead(id) ;
++        ByteBuffer bb2 = block2.getByteBuffer() ;
++        assertEquals(bb2.capacity(), BlkSize) ;
++        assertEquals(bb2.get(0), (byte)1) ;
++        assertEquals(bb2.get(BlkSize - 1), (byte)1) ;
++        blockMgr.release(block2) ;
++    }
++
++    @Test
++    public void file03() {
++        Block block = blockMgr.allocate(BlkSize) ;
++        ByteBuffer bb = block.getByteBuffer() ;
++        fill(bb, (byte)2) ;
++        long id = block.getId() ;
++        blockMgr.write(block) ;
++        blockMgr.release(block) ;
++
++        Block block2 = blockMgr.getRead(id) ;
++        ByteBuffer bb2 = block2.getByteBuffer() ;
++        assertEquals(bb2.capacity(), BlkSize) ;
++        assertEquals(bb2.get(0), (byte)2) ;
++        assertEquals(bb2.get(BlkSize - 1), (byte)2) ;
++        blockMgr.release(block2) ;
++    }
++
++    @Test
++    public void multiAccess01() {
++        Block block1 = blockMgr.allocate(BlkSize) ;
++        Block block2 = blockMgr.allocate(BlkSize) ;
++        long id1 = block1.getId() ;
++        long id2 = block2.getId() ;
++
++        ByteBuffer bb1 = block1.getByteBuffer() ;
++        ByteBuffer bb2 = block2.getByteBuffer() ;
++
++        fill(bb1, (byte)1) ;
++        fill(bb2, (byte)2) ;
++
++        blockMgr.write(block1) ;
++        blockMgr.write(block2) ;
++        blockMgr.release(block1) ;
++        blockMgr.release(block2) ;
++
++        Block block3 = blockMgr.getRead(id1) ;
++        Block block4 = blockMgr.getRead(id2) ;
++
++        ByteBuffer bb_1 = block3.getByteBuffer() ;
++        ByteBuffer bb_2 = block4.getByteBuffer() ;
++
++        contains(bb_1, (byte)1) ;
++        contains(bb_2, (byte)2) ;
++
++        blockMgr.release(block3) ;
++        blockMgr.release(block4) ;
++    }
++
++    protected abstract BlockMgr make() ;
++
++    protected static void contains(ByteBuffer bb, byte fillValue) {
++        for ( int i = 0 ; i < bb.limit() ; i++ )
++            assertEquals("Index: " + i, bb.get(i), fillValue) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/block/TS_Block.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/block/TS_Block.java
index 0000000,0000000..5a91b2c
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/block/TS_Block.java
@@@ -1,0 -1,0 +1,33 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.block;
++
++import org.junit.runner.RunWith;
++import org.junit.runners.Suite;
++
++@RunWith(Suite.class)
++@Suite.SuiteClasses( {
++    TestBlockMgrMem.class
++    , TestBlockMgrDirect.class
++    , TestBlockMgrMapped.class
++    , TestBlockMgrTracked.class
++})
++
++
++public class TS_Block
++{}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/block/TestBlockMgrDirect.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/block/TestBlockMgrDirect.java
index 0000000,0000000..79c4d68
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/block/TestBlockMgrDirect.java
@@@ -1,0 -1,0 +1,48 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.block;
++
++import org.apache.jena.atlas.lib.FileOps ;
++import org.junit.AfterClass;
++import org.junit.BeforeClass;
++import org.seaborne.dboe.ConfigTestDBOE ;
++import org.seaborne.dboe.base.block.BlockMgr ;
++import org.seaborne.dboe.base.block.BlockMgrFactory ;
++import org.seaborne.dboe.base.block.BlockMgrFileAccess ;
++import org.seaborne.dboe.base.file.BlockAccess ;
++import org.seaborne.dboe.base.file.BlockAccessDirect ;
++
++public class TestBlockMgrDirect extends AbstractTestBlockMgr
++{
++    static final String filename = ConfigTestDBOE.getTestingDir()+"/block-mgr" ;
++    
++    @BeforeClass static public void remove1() { FileOps.delete(filename) ; } 
++    @AfterClass  static public void remove2() { FileOps.delete(filename) ; }
++    
++    @Override
++    protected BlockMgr make()
++    { 
++        // Make directly - no wrapper, no cache, no free block mgt.
++        FileOps.delete(filename) ;
++        BlockAccess file = new BlockAccessDirect(filename, BlkSize) ;
++        BlockMgr mgr = new BlockMgrFileAccess(file, BlkSize) ;
++        if ( BlockMgrFactory.AddTracker )
++            mgr = BlockMgrFactory.tracker(mgr) ;
++        return mgr ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/block/TestBlockMgrMapped.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/block/TestBlockMgrMapped.java
index 0000000,0000000..851e715
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/block/TestBlockMgrMapped.java
@@@ -1,0 -1,0 +1,63 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.block;
++
++import org.apache.jena.atlas.lib.FileOps ;
++import org.apache.jena.atlas.logging.LogCtl ;
++import org.junit.After ;
++import org.junit.AfterClass ;
++import org.junit.BeforeClass ;
++import org.seaborne.dboe.ConfigTestDBOE ;
++import org.seaborne.dboe.base.block.BlockMgr ;
++import org.seaborne.dboe.base.block.BlockMgrFileAccess ;
++import org.seaborne.dboe.base.file.BlockAccess ;
++import org.seaborne.dboe.base.file.BlockAccessMapped ;
++
++public class TestBlockMgrMapped extends AbstractTestBlockMgr
++{
++    static boolean logging = false ;
++    
++    static { if ( logging ) LogCtl.setLog4j() ; }
++    
++    static final String filename = ConfigTestDBOE.getTestingDir()+"/block-mgr" ;
++    
++    // Windows is iffy about deleting memory mapped files.
++    
++    @After public void after1()     { clearBlockMgr() ; }
++    
++    private void clearBlockMgr()
++    {
++        if ( blockMgr != null )
++        {
++            blockMgr.close() ;
++            FileOps.deleteSilent(filename) ;
++            blockMgr = null ;
++        }
++    }
++    
++    @BeforeClass static public void remove1() { FileOps.deleteSilent(filename) ; }
++    @AfterClass  static public void remove2() { FileOps.deleteSilent(filename) ; }
++    
++    @Override
++    protected BlockMgr make()
++    { 
++        clearBlockMgr() ;
++        BlockAccess file = new BlockAccessMapped(filename, BlkSize) ;
++        return new BlockMgrFileAccess(file, BlkSize) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/block/TestBlockMgrMem.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/block/TestBlockMgrMem.java
index 0000000,0000000..be1a953
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/block/TestBlockMgrMem.java
@@@ -1,0 -1,0 +1,30 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.block;
++
++import org.seaborne.dboe.base.block.BlockMgr ;
++import org.seaborne.dboe.base.block.BlockMgrFactory ;
++
++
++public class TestBlockMgrMem extends AbstractTestBlockMgr
++{
++    @Override
++    protected BlockMgr make()
++    { return BlockMgrFactory.createMem("mem", BlkSize) ; } 
++    
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/block/TestBlockMgrTracked.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/block/TestBlockMgrTracked.java
index 0000000,0000000..ba7e056
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/block/TestBlockMgrTracked.java
@@@ -1,0 -1,0 +1,141 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.block;
++
++import java.nio.ByteBuffer ;
++
++import org.junit.AfterClass ;
++import org.junit.Assert ;
++import org.junit.BeforeClass ;
++import org.junit.Test ;
++
++public class TestBlockMgrTracked extends Assert
++{
++    // Mainly testing the tracking
++
++    static boolean b ;
++
++    @BeforeClass
++    static public void beforeClass()
++    {
++        b = BlockMgrTracker.verbose ;
++        BlockMgrTracker.verbose = false ;
++    }
++
++    @AfterClass
++    static public void afterClass()
++    {
++        BlockMgrTracker.verbose = b ;
++    }    
++    
++    @Test public void track_01()
++    {
++        BlockMgr mgr = BlockMgrFactory.createMem("BPTRecord", 4) ;
++        mgr = BlockMgrFactory.tracker(mgr) ;
++        mgr.beginUpdate() ;
++        Block block = mgr.allocate(4) ;
++        ByteBuffer bb = block.getByteBuffer() ;
++        bb.putInt(0,1234) ;
++        mgr.write(block) ;
++        mgr.release(block) ;
++        // -----
++        Block block2 = mgr.getRead(block.getId()) ;
++        ByteBuffer bb2 = block2.getByteBuffer() ;
++        assertArrayEquals(bb.array(), bb2.array()) ;
++        mgr.release(block2) ;
++        mgr.endUpdate() ;
++    }
++
++    // Multiple overlapping read operations.
++    static BlockMgr setup()
++    {
++        BlockMgr mgr = BlockMgrFactory.createMem("BPTRecord", 4) ;
++        mgr = BlockMgrFactory.tracker(mgr) ;
++        return mgr ;
++    }
++    
++    static void write(BlockMgr mgr, int value)
++    {
++        mgr.beginUpdate() ;
++        Block block = mgr.allocate(4) ;
++        ByteBuffer bb = block.getByteBuffer() ;
++        bb.putInt(0,value) ;
++        mgr.write(block) ;
++        mgr.release(block) ;
++        mgr.endUpdate() ;
++    }
++    
++    @Test public void track_02()
++    {
++        BlockMgr mgr = setup() ;
++        write(mgr, 1234) ;
++        write(mgr, 5678) ;
++
++        mgr.beginRead() ;
++        mgr.beginRead() ;
++
++        Block b0 = mgr.getRead(0) ;
++        Block b1 = mgr.getRead(1) ;
++        
++        mgr.release(b1) ;
++        mgr.release(b0) ;
++        
++        mgr.endRead() ;
++        mgr.endRead() ;
++    }
++    
++    @Test(expected=BlockException.class)
++    public void track_03()
++    {
++        BlockMgr mgr = setup() ;
++        write(mgr, 1234) ;
++        write(mgr, 5678) ;
++
++        mgr.beginRead() ;
++        Block b0 = mgr.getWrite(0) ;
++        mgr.endRead() ;
++    }
++
++    @Test(expected=BlockException.class)
++    public void track_04()
++    {
++        BlockMgr mgr = setup() ;
++        write(mgr, 1234) ;
++        mgr.beginRead() ;
++        Block b0 = mgr.getRead(0) ;
++        mgr.promote(b0) ;
++        mgr.endRead() ;
++    }
++
++    @Test(expected=BlockException.class)
++    public void track_05()
++    {
++        BlockMgr mgr = setup() ;
++        mgr.beginRead() ;
++        mgr.endUpdate() ;
++    }
++
++    @Test(expected=BlockException.class)
++    public void track_06()
++    {
++        BlockMgr mgr = setup() ;
++        mgr.beginUpdate() ;
++        mgr.endRead() ;
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/buffer/TS_Buffer.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/buffer/TS_Buffer.java
index 0000000,0000000..a294196
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/buffer/TS_Buffer.java
@@@ -1,0 -1,0 +1,31 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.buffer;
++
++import org.junit.runner.RunWith;
++import org.junit.runners.Suite;
++
++@RunWith(Suite.class)
++@Suite.SuiteClasses( {
++    TestRecordBuffer.class
++    , TestPtrBuffer.class
++})
++
++
++public class TS_Buffer
++{}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/buffer/TestPtrBuffer.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/buffer/TestPtrBuffer.java
index 0000000,0000000..82b410f
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/buffer/TestPtrBuffer.java
@@@ -1,0 -1,0 +1,315 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.buffer;
++
++import java.nio.ByteBuffer ;
++
++import org.junit.AfterClass ;
++import org.junit.Assert ;
++import org.junit.BeforeClass ;
++import org.junit.Test ;
++import org.seaborne.dboe.sys.SystemIndex ;
++
++public class TestPtrBuffer extends Assert
++{
++    static boolean originalNullOut ; 
++    @BeforeClass static public void beforeClass()
++    {
++        originalNullOut = SystemIndex.getNullOut() ;
++        SystemIndex.setNullOut(true) ;    
++    }
++    
++    @AfterClass static public void afterClass()
++    {
++        SystemIndex.setNullOut(originalNullOut) ;    
++    }
++    
++    // Testing the test framework!
++    @Test public void ptrbuffer01()
++    {
++        PtrBuffer pb = make(4) ;
++        contains(pb, 2, 4, 6, 8) ;
++    }
++    
++    // No BinarySearch test
++    
++    // Shift at LHS
++    @Test public void ptrbuffer03()
++    {
++        PtrBuffer pb = make(4,5) ;
++        contains(pb, 2, 4, 6, 8) ;
++        
++        pb.shiftUp(0) ;
++        pb.set(0, 99) ;
++        contains(pb, 99, 2, 4, 6, 8) ;
++        
++        pb.shiftDown(0) ;
++        contains(pb, 2, 4, 6, 8) ;
++    }    
++    
++    @Test public void ptrbuffer04()
++    {
++        PtrBuffer pb = make(4,5) ;
++        contains(pb, 2, 4, 6, 8) ;
++        pb.shiftDown(0) ;
++        
++        contains(pb, 4, 6, 8) ;
++        pb.shiftUp(0) ;
++
++        pb.set(0,1) ;
++        contains(pb, 1, 4, 6, 8) ;
++    }    
++    
++
++    // Shift at middle
++    @Test public void ptrbuffer05()
++    {
++        PtrBuffer pb = make(4,5) ;
++        contains(pb, 2, 4, 6, 8) ;
++        pb.shiftUp(2) ;
++        pb.set(2, 0) ;
++        contains(pb, 2, 4, 0, 6, 8) ;
++        pb.shiftDown(2) ;
++        contains(pb, 2, 4, 6, 8) ;
++    }    
++
++    @Test public void ptrbuffer06()
++    {
++        PtrBuffer pb = make(4,5) ;
++        contains(pb, 2, 4, 6, 8) ;
++        pb.shiftDown(2) ;
++        contains(pb, 2, 4, 8) ;
++        pb.shiftUp(2) ;
++        assertTrue(pb.isClear(2)) ;
++        contains(pb, 2, 4, -1, 8) ;
++    }    
++
++    // Shift RHS - out of bounds
++    @Test public void ptrbuffer07()
++    {
++        PtrBuffer pb = make(4,5) ;
++        contains(pb, 2, 4, 6, 8) ;
++        pb.shiftUp(3) ;
++        pb.set(3, 1) ;
++        contains(pb, 2, 4, 6, 1, 8) ;
++        pb.shiftDown(3) ;
++        contains(pb, 2, 4, 6, 8) ;
++    }    
++
++    @Test public void ptrbuffer08()
++    {
++        PtrBuffer pb = make(4,5) ;
++        contains(pb, 2, 4, 6, 8) ;
++        pb.shiftDown(3) ;
++        contains(pb, 2, 4, 6) ;
++        pb.shiftUp(2) ;
++        contains(pb, 2, 4, -1, 6) ;
++    }    
++
++    // Errors - IllegalArgumentException
++    @Test(expected=BufferException.class) 
++    public void ptrbuffer09()
++    {
++        PtrBuffer pb = make(4,5) ;
++        contains(pb, 2, 4, 6, 8) ;
++        pb.shiftDown(4) ;
++    }  
++    
++    @Test(expected=BufferException.class) 
++    public void ptrbuffer10()
++    {
++        PtrBuffer pb = make(4,5) ;
++        contains(pb, 2, 4, 6, 8) ;
++        pb.shiftUp(4) ;
++    }  
++
++    @Test(expected=BufferException.class) 
++    public void ptrbuffer11()
++    {
++        PtrBuffer pb = make(5,5) ;
++        contains(pb, 2, 4, 6, 8, 10) ;
++        pb.add(12) ;
++    }  
++    
++    // Copy, duplicate, clear
++    @Test public void ptrbuffer12()
++    {
++        PtrBuffer pb = make(5,5) ;
++        contains(pb, 2, 4, 6, 8, 10) ;
++        PtrBuffer pb2 = pb.duplicate() ;
++        pb2.set(1, 99) ;
++        contains(pb, 2, 4, 6, 8, 10) ;
++        contains(pb2, 2, 99, 6, 8, 10) ;
++    }
++    
++    @Test public void ptrbuffer13()
++    {
++        PtrBuffer pb = make(5,5) ;
++        contains(pb, 2, 4, 6, 8, 10) ;
++        pb.clear(1, 3) ;
++        contains(pb, 2, -1, -1, -1, 10) ;
++    }
++    
++    @Test public void ptrbuffer14()
++    {
++        PtrBuffer pb = make(5,5) ;
++        contains(pb, 2, 4, 6, 8, 10) ;
++        
++        PtrBuffer pb2 = make(5,5) ;
++        contains(pb2, 2, 4, 6, 8, 10) ;
++        
++        pb.copy(0, pb2, 1, 4) ;
++        contains(pb2, 2, 2, 4, 6, 8) ;
++    }
++
++    // Remove tests
++
++    @Test public void ptrbuffer15()
++    {
++        PtrBuffer pb = make(5,5) ;
++        contains(pb, 2, 4, 6, 8, 10) ;
++        pb.removeTop() ;
++        contains(pb, 2, 4, 6, 8) ;
++        pb.remove(1) ;
++        contains(pb, 2, 6, 8) ;
++        pb.remove(2) ;
++        contains(pb, 2, 6) ;
++        pb.remove(0) ;
++        contains(pb, 6) ;
++        pb.remove(0) ;
++        contains(pb) ;
++    }
++    
++    @Test public void ptrbuffer20()
++    {
++        PtrBuffer pb1 = make(5,5) ;
++        contains(pb1, 2, 4, 6, 8, 10) ;
++        PtrBuffer pb2 = make(0,5) ;
++        contains(pb2) ;
++        
++        pb1.shiftRight(pb2) ;
++        contains(pb1, 2, 4, 6, 8) ;
++        contains(pb2, 10) ;
++    }
++    
++    @Test public void ptrbuffer21()
++    {
++        PtrBuffer pb1 = make(3,5) ;
++        contains(pb1, 2, 4, 6) ;
++        PtrBuffer pb2 = make(0,5) ;
++        contains(pb2) ;
++        
++        pb1.shiftRight(pb2) ;
++        contains(pb1, 2, 4) ;
++        contains(pb2, 6) ;
++    }
++    
++    @Test public void ptrbuffer22()
++    {
++        PtrBuffer pb1 = make(3,5) ;
++        contains(pb1, 2, 4, 6) ;
++        PtrBuffer pb2 = make(2,5) ;
++        contains(pb2, 2, 4) ;
++        
++        pb1.shiftRight(pb2) ;
++        contains(pb1, 2, 4) ;
++        contains(pb2, 6, 2, 4) ;
++    }
++    
++    @Test public void ptrbuffer24()
++    {
++        PtrBuffer pb1 = make(0,5) ;
++        contains(pb1) ;
++        PtrBuffer pb2 = make(5,5) ;
++        contains(pb2, 2, 4, 6, 8, 10) ;
++        
++        pb1.shiftLeft(pb2) ;
++        contains(pb1, 2) ;
++        contains(pb2, 4, 6, 8, 10) ;
++    }
++    
++    @Test public void ptrbuffer25()
++    {
++        PtrBuffer pb1 = make(0,5) ;
++        contains(pb1) ;
++        PtrBuffer pb2 = make(3,5) ;
++        contains(pb2, 2, 4, 6) ;
++        
++        pb1.shiftLeft(pb2) ;
++        contains(pb1, 2) ;
++        contains(pb2, 4, 6) ;
++    }
++    
++    @Test public void ptrbuffer26()
++    {
++        PtrBuffer pb1 = make(2,5) ;
++        contains(pb1, 2, 4) ;
++        PtrBuffer pb2 = make(3,5) ;
++        contains(pb2, 2, 4, 6) ;
++        
++        pb1.shiftLeft(pb2) ;
++        contains(pb1, 2, 4, 2) ;
++        contains(pb2, 4, 6) ;
++    }
++    
++    @Test public void ptrbuffer27()
++    {
++        PtrBuffer pb1 = make(2,4) ;
++        PtrBuffer pb2 = make(2,4) ; 
++        pb1.copyToTop(pb2) ;
++        contains(pb2, 2,4,2,4) ;
++    }
++    
++    @Test public void ptrbuffer28()
++    {
++        PtrBuffer pb1 = make(0,5) ;
++        PtrBuffer pb2 = make(2,4) ; 
++        pb1.copyToTop(pb2) ;
++        contains(pb2, 2,4) ;
++    }
++
++    @Test public void ptrbuffer29()
++    {
++        PtrBuffer pb1 = make(0,5) ;
++        PtrBuffer pb2 = make(2,4) ; 
++        pb2.copyToTop(pb1) ;
++        contains(pb1, 2,4) ;
++    }
++
++    // ---- Support
++    private static void contains(PtrBuffer pb, int... vals)
++    {
++        assertEquals("Length mismatch: ", vals.length, pb.size()) ;
++        for ( int i = 0 ; i < vals.length ; i++ )
++            if ( vals[i] == -1 )
++                assertTrue(pb.isClear(i))  ;
++            else
++                assertEquals("Value mismatch: ", vals[i], pb.get(i)) ;
++    }
++
++    // Make : 2,4,6,8, ..
++    private static PtrBuffer make(int n) { return make(n,n) ; }
++    private static PtrBuffer make(int n, int len)
++    { 
++        ByteBuffer bb = ByteBuffer.allocate(4*len) ;
++        PtrBuffer pb = new PtrBuffer(bb, 0) ;
++        for ( int i = 0 ; i < n ; i++ )
++            pb.add(2+2*i) ;
++        return pb ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/buffer/TestRecordBuffer.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/buffer/TestRecordBuffer.java
index 0000000,0000000..d4b9726
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/buffer/TestRecordBuffer.java
@@@ -1,0 -1,0 +1,345 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.buffer;
++
++import static org.seaborne.dboe.test.RecordLib.intToRecord ;
++import static org.seaborne.dboe.test.RecordLib.r ;
++
++import java.util.Iterator ;
++import java.util.List ;
++
++import org.junit.Assert ;
++import org.junit.AfterClass ;
++import org.junit.BeforeClass ;
++import org.junit.Test ;
++import org.seaborne.dboe.base.buffer.BufferException ;
++import org.seaborne.dboe.base.buffer.RecordBuffer ;
++import org.seaborne.dboe.base.record.Record ;
++import org.seaborne.dboe.base.record.RecordFactory ;
++import org.seaborne.dboe.sys.SystemIndex ;
++import org.seaborne.dboe.test.RecordLib ;
++
++public class TestRecordBuffer extends Assert
++{
++    static RecordFactory recordFactory = new RecordFactory(RecordLib.TestRecordLength, 0) ;
++    
++    static boolean originalNullOut ; 
++    @BeforeClass static public void beforeClass()
++    {
++        originalNullOut = SystemIndex.getNullOut() ;
++        SystemIndex.setNullOut(true) ;    
++    }
++    
++    @AfterClass static public void afterClass()
++    {
++        SystemIndex.setNullOut(originalNullOut) ;    
++    }
++
++    @Test public void recBuffer01()
++    {
++        RecordBuffer rb = make(4, 4) ;
++        contains(rb, 2, 4, 6, 8) ;
++    }
++    
++    @Test public void recBuffer02()
++    {
++        RecordBuffer rb = make(4, 4) ;
++        int idx = -1 ;
++        idx = find(rb, 6) ;
++        assertEquals(2, idx) ;
++        idx = find(rb, 8) ;
++        
++        assertEquals(3, idx) ;
++        idx = find(rb, 4) ;
++        assertEquals(1, idx) ;
++        idx = find(rb, 2) ;
++        assertEquals(0, idx) ;
++
++        idx = find(rb, 3) ;
++        assertEquals(-2, idx) ;
++        idx = find(rb, 0) ;
++        assertEquals(-1, idx) ;
++        idx = find(rb, 10) ;
++        assertEquals(-5, idx) ;
++    }
++
++    // Shift at LHS
++    @Test public void recBuffer03()
++    {
++        RecordBuffer rb = make(4,5) ;
++        contains(rb, 2, 4, 6, 8) ;
++        rb.shiftUp(0) ;
++        rb.set(0, r(0)) ;
++        contains(rb, 0, 2, 4, 6, 8) ;
++        rb.shiftDown(0) ;
++        contains(rb, 2, 4, 6, 8) ;
++    }    
++    
++    @Test public void recBuffer04()
++    {
++        RecordBuffer rb = make(4,5) ;
++        contains(rb, 2, 4, 6, 8) ;
++        rb.shiftDown(0) ;
++        
++        contains(rb, 4, 6, 8) ;
++        rb.shiftUp(0) ;
++
++        rb.set(0,r(1)) ;
++        contains(rb, 1, 4, 6, 8) ;
++    }    
++    
++
++    // Shift at middle
++    @Test public void recBuffer05()
++    {
++        RecordBuffer rb = make(4,5) ;
++        contains(rb, 2, 4, 6, 8) ;
++        rb.shiftUp(2) ;
++        rb.set(2, r(0)) ;
++        contains(rb, 2, 4, 0, 6, 8) ;
++        rb.shiftDown(2) ;
++        contains(rb, 2, 4, 6, 8) ;
++    }    
++
++    @Test public void recBuffer06()
++    {
++        RecordBuffer rb = make(4,5) ;
++        contains(rb, 2, 4, 6, 8) ;
++        rb.shiftDown(2) ;
++        contains(rb, 2, 4, 8) ;
++        rb.shiftUp(2) ;
++        contains(rb, 2, 4, -1, 8) ;
++    }    
++
++    // Shift RHS - out of bounds
++    @Test public void recBuffer07()
++    {
++        RecordBuffer rb = make(4,5) ;
++        contains(rb, 2, 4, 6, 8) ;
++        rb.shiftUp(3) ;
++        rb.set(3, r(1)) ;
++        contains(rb, 2, 4, 6, 1, 8) ;
++        rb.shiftDown(3) ;
++        contains(rb, 2, 4, 6, 8) ;
++    }    
++
++    @Test public void recBuffer08()
++    {
++        RecordBuffer rb = make(4,5) ;
++        contains(rb, 2, 4, 6, 8) ;
++        rb.shiftDown(3) ;
++        contains(rb, 2, 4, 6) ;
++        rb.shiftUp(2) ;
++        contains(rb, 2, 4, -1, 6) ;
++    }    
++
++    // Errors
++    
++    @Test(expected=BufferException.class) 
++    public void recBuffer09()
++    {
++        RecordBuffer rb = make(4,5) ;
++        contains(rb, 2, 4, 6, 8) ;
++        rb.shiftDown(4) ;
++    }  
++    
++    @Test(expected=BufferException.class) 
++    public void recBuffer10()
++    {
++        RecordBuffer rb = make(4,5) ;
++        contains(rb, 2, 4, 6, 8) ;
++        rb.shiftUp(4) ;
++    }  
++
++    @Test(expected=BufferException.class) 
++    public void recBuffer11()
++    {
++        RecordBuffer rb = make(5,5) ;
++        contains(rb, 2, 4, 6, 8, 10) ;
++        rb.add(r(12)) ;
++    }  
++    
++    // Copy, duplicate, clear
++    @Test public void recBuffer12()
++    {
++        RecordBuffer rb = make(5,5) ;
++        contains(rb, 2, 4, 6, 8, 10) ;
++        RecordBuffer rb2 = rb.duplicate() ;
++        rb2.set(1, r(99)) ;
++        contains(rb, 2, 4, 6, 8, 10) ;
++        contains(rb2, 2, 99, 6, 8, 10) ;
++    }
++    
++    @Test public void recBuffer13()
++    {
++        RecordBuffer rb = make(5,5) ;
++        contains(rb, 2, 4, 6, 8, 10) ;
++        rb.clear(1, 3) ;
++        contains(rb, 2, -1, -1, -1, 10) ;
++    }
++    
++    @Test public void recBuffer14()
++    {
++        RecordBuffer rb = make(5,5) ;
++        contains(rb, 2, 4, 6, 8, 10) ;
++        RecordBuffer rb2 = make(5,5) ;
++        contains(rb2, 2, 4, 6, 8, 10) ;
++        rb.copy(0, rb2, 1, 4) ;
++        contains(rb2, 2, 2, 4, 6, 8) ;
++    }
++
++    // Remove tests
++    
++    @Test public void recBuffer15()
++    {
++        RecordBuffer rb = make(5,5) ;
++        contains(rb, 2, 4, 6, 8, 10) ;
++        rb.removeTop() ;
++        contains(rb, 2, 4, 6, 8) ;
++        rb.remove(1) ;
++        contains(rb, 2, 6, 8) ;
++        rb.remove(2) ;
++        contains(rb, 2, 6) ;
++        rb.remove(0) ;
++        contains(rb, 6) ;
++        rb.remove(0) ;
++        contains(rb) ;
++    }
++
++    @Test public void recBufferIterate01()
++    {
++        RecordBuffer rb = make(5,5) ;
++        same(rb.iterator(), 2,4,6,8,10) ;
++    }
++
++    @Test public void recBufferIterate02()
++    {
++        RecordBuffer rb = make(3,5) ;
++        Iterator<Record> iter = rb.iterator() ;
++        same(iter, 2, 4, 6) ;
++    }
++
++    @Test public void recBufferIterate03()
++    {
++        RecordBuffer rb = make(3,5) ;
++        Iterator<Record> iter = rb.iterator( intToRecord(4), null) ;
++        same(iter, 4, 6) ;
++    }
++
++    @Test public void recBufferIterate04()
++    {
++        RecordBuffer rb = make(3,5) ;
++        Iterator<Record> iter = rb.iterator( intToRecord(3), null) ;
++        same(iter, 4, 6) ;
++    }
++
++    @Test public void recBufferIterate05()
++    {
++        RecordBuffer rb = make(3,5) ;
++        Iterator<Record> iter = rb.iterator( intToRecord(1), null) ;
++        same(iter, 2, 4, 6) ;
++    }
++
++    @Test public void recBufferIterate06()
++    {
++        RecordBuffer rb = make(3,5) ;
++        Iterator<Record> iter = rb.iterator( null, intToRecord(1)) ;
++        same(iter) ;
++    }
++
++    @Test public void recBufferIterate07()
++    {
++        RecordBuffer rb = make(3,5) ;
++        Iterator<Record> iter = rb.iterator( null, intToRecord(2)) ;
++        same(iter ) ;
++    }
++
++    @Test public void recBufferIterate08()
++    {
++        RecordBuffer rb = make(3,5) ;
++        Iterator<Record> iter = rb.iterator( null, intToRecord(3)) ;
++        same(iter,2 ) ;
++    }
++    
++    @Test public void recBufferIterate09()
++    {
++        RecordBuffer rb = make(5,5) ;
++        Iterator<Record> iter = rb.iterator( null, intToRecord(99)) ;
++        same(iter, 2, 4, 6, 8, 10) ;
++    }
++
++    @Test public void recBufferIterate10()
++    {
++        RecordBuffer rb = make(5,5) ;
++        Iterator<Record> iter = rb.iterator( intToRecord(4), intToRecord(8)) ;
++        same(iter, 4, 6 ) ;
++    }
++
++    @Test public void recBufferIterate11()
++    {
++        RecordBuffer rb = make(5,5) ;
++        Iterator<Record> iter = rb.iterator( intToRecord(3), intToRecord(9)) ;
++        same(iter, 4, 6, 8 ) ;
++    }
++    
++    // ---- Support
++    private static void contains(RecordBuffer rb, int... vals)
++    {
++        assertEquals("Length mismatch: ", vals.length, rb.size()) ;
++        
++        for ( int i = 0 ; i < vals.length ; i++ )
++            if ( vals[i] == -1 )
++                assertTrue(rb.isClear(i))  ;
++            else
++            {
++                Record r = RecordLib.intToRecord(vals[i]) ;
++                Record r2 = rb.get(i) ;
++                int x = RecordLib.recordToInt(r2) ;
++                assertEquals("Value mismatch: ", vals[i], x) ;
++            }
++    }
++    
++    private static void same(Iterator<Record> iter, int... vals)
++    {
++        List<Integer> list = RecordLib.toIntList(iter) ;
++        assertEquals("Length mismatch: ", vals.length, list.size()) ;
++        
++        for ( int i = 0 ; i < vals.length ; i++ )
++        {
++            int x = list.get(i) ;
++            assertEquals("Value mismatch: ", vals[i], x) ;
++            }
++    }
++
++    
++    public int find(RecordBuffer rb, int v)
++    {
++        return rb.find(r(v)) ;
++    }
++
++    private static RecordBuffer make(int n, int len)
++    { 
++        RecordBuffer rb = new RecordBuffer(recordFactory, len) ;
++        for ( int i = 0 ; i < n ; i++ )
++        {
++            Record r = RecordLib.intToRecord(2*i+2) ;  
++            rb.add(r) ;
++        }
++        return rb ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/AbstractTestBinaryDataFile.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/AbstractTestBinaryDataFile.java
index 0000000,0000000..a40003c
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/AbstractTestBinaryDataFile.java
@@@ -1,0 -1,0 +1,83 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++import org.apache.jena.atlas.lib.StrUtils ;
++import org.junit.After ;
++import org.junit.Before ;
++import org.junit.Test ;
++import org.seaborne.dboe.base.file.BinaryDataFile ;
++import static org.junit.Assert.* ;
++
++public abstract class AbstractTestBinaryDataFile {
++    private BinaryDataFile file ;
++    protected abstract BinaryDataFile createBinaryDataFile() ;
++    
++    static final String stringData = "Hello world\n";
++    static final byte[] data = StrUtils.asUTF8bytes(stringData) ;
++    
++    // Default action.
++    protected void releaseBinaryDataFile(BinaryDataFile file) {
++        file.close() ;
++    }
++    
++    @Before public void before() {
++        file = createBinaryDataFile() ;
++        file.open();
++        file.truncate(0) ; 
++    }
++    
++    @After public void after() {
++        releaseBinaryDataFile(file) ; 
++    }
++
++    @Test public void basic_open() { 
++        assertTrue(file.isOpen()) ;
++    }
++
++    @Test public void basic_open_close() { 
++        assertTrue(file.isOpen()) ;
++        file.close() ;
++        assertFalse(file.isOpen()) ;
++    }
++    
++    @Test public void writeread_01() { 
++        assertEquals(0, file.length()) ;
++    }
++    
++    @Test public void writeread_02() { 
++        assertEquals(0, file.length()) ;
++        file.write(data);
++        assertNotEquals(0, file.length()) ;
++        assertEquals(data.length, file.length()) ;        
++    }
++    
++    @Test public void writeread_03() { 
++        long x = file.write(data);
++        byte[] data2 = new byte[data.length+100] ;
++        int x1 = file.read(x, data2) ;
++        assertEquals(data.length, x1) ;
++        int x2 = file.read(data.length, data2) ;
++        assertEquals(-1, x2) ;
++    }
++
++    
++
++}
++
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/AbstractTestBlockAccessFixedSize.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/AbstractTestBlockAccessFixedSize.java
index 0000000,0000000..284ac5a
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/AbstractTestBlockAccessFixedSize.java
@@@ -1,0 -1,0 +1,101 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++import static org.seaborne.dboe.test.BufferTestLib.sameValue ;
++import org.junit.Assert ;
++import org.junit.After ;
++import org.junit.Before ;
++import org.junit.Test ;
++import org.seaborne.dboe.base.block.Block ;
++import org.seaborne.dboe.base.file.BlockAccess ;
++import org.seaborne.dboe.base.file.FileException ;
++
++public abstract class AbstractTestBlockAccessFixedSize extends Assert
++{
++    // Fixed block tests.
++    
++    int blkSize ;
++    
++    protected AbstractTestBlockAccessFixedSize(int blkSize)
++    {
++        this.blkSize = blkSize ;
++    }
++    
++    protected abstract BlockAccess make() ;
++    protected static Block data(BlockAccess file, int len)
++    {
++        Block b = file.allocate(len) ;
++        for (int i = 0 ; i < len ; i++ )
++            b.getByteBuffer().put((byte)(i&0xFF)) ;
++        return b ;
++    }
++
++    private BlockAccess file ;
++    @Before public void before() { file = make() ; }
++    @After  public void after()  { file.close() ; }
++
++    @Test public void fileaccess_01()
++    {
++        assertTrue(file.isEmpty()) ;
++    }
++    
++    @Test public void fileaccess_02()
++    {
++        Block b = data(file, blkSize) ;
++        file.write(b) ;
++    }
++
++    @Test public void fileaccess_03()
++    {
++        Block b1 = data(file, blkSize) ;
++        file.write(b1) ;
++        long x = b1.getId() ;
++        Block b9 = file.read(x) ;
++        assertNotSame(b1, b9) ;
++        assertTrue(sameValue(b1, b9)) ;
++        b9 = file.read(x) ;
++        assertNotSame(b1, b9) ;
++        assertTrue(sameValue(b1, b9)) ;
++    }
++    
++    @Test public void fileaccess_04()
++    {
++        Block b1 = data(file, blkSize) ;
++        Block b2 = data(file, blkSize) ;
++        file.write(b1) ;
++        file.write(b2) ;
++        
++        long x = b1.getId() ;
++        Block b8 = file.read(b1.getId()) ;
++        Block b9 = file.read(b1.getId()) ;
++        assertNotSame(b8, b9) ;
++        assertTrue(b8.getId() == b9.getId()) ;
++    }
++    
++    @Test(expected=FileException.class)
++    public void fileaccess_05()
++    {
++        Block b1 = data(file, 10) ;
++        Block b2 = data(file, 20) ;
++        file.write(b1) ;
++        
++        // Should not work. b2 not written.   
++        Block b2a = file.read(b2.getId()) ;
++    }    
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/AbstractTestBlockAccessVarSize.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/AbstractTestBlockAccessVarSize.java
index 0000000,0000000..2fd3722
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/AbstractTestBlockAccessVarSize.java
@@@ -1,0 -1,0 +1,49 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++import org.junit.Test ;
++import org.seaborne.dboe.base.block.Block ;
++import org.seaborne.dboe.base.file.BlockAccess ;
++import static org.seaborne.dboe.test.BufferTestLib.* ;
++
++public abstract class AbstractTestBlockAccessVarSize extends AbstractTestBlockAccessFixedSize
++{
++    protected AbstractTestBlockAccessVarSize()
++    {
++        super(25) ;
++    }
++    
++    @Test
++    public void fileaccess_50()
++    {
++        BlockAccess file = make() ;
++        Block b1 = data(file, 10) ;
++        Block b2 = data(file, 20) ;
++        file.write(b1) ;
++        file.write(b2) ;
++        
++        Block b1a = file.read(b1.getId()) ;
++        Block b2a = file.read(b2.getId()) ;
++
++        assertNotSame(b1a, b1) ;
++        assertNotSame(b2a, b2) ;
++        sameValue(b1, b1a) ;
++        sameValue(b2, b2a) ;
++    }        
++}


[38/50] [abbrv] jena git commit: JENA-1396: Merge //github/afs/mantis as subdirectory jena-db/

Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionalComponentBase.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionalComponentBase.java
index 0000000,0000000..18e091f
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionalComponentBase.java
@@@ -1,0 -1,0 +1,76 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction.txn;
++
++import java.nio.ByteBuffer ;
++
++import org.apache.jena.query.ReadWrite ;
++
++/**
++ * A transaction component that does nothing - can be used as a helper for
++ * management tasks hooked into the transaction component lifecycle but which
++ * are not stateful across restarts.
++ */
++public class TransactionalComponentBase<X> extends TransactionalComponentLifecycle<X> {
++    
++    public TransactionalComponentBase(ComponentId id) {
++        super(id) ;
++    }
++    
++    @Override
++    public void startRecovery() {}
++
++    @Override
++    public void recover(ByteBuffer ref) {}
++
++    @Override
++    public void finishRecovery() {}
++    
++    @Override 
++    public void cleanStart() {}
++
++    @Override
++    protected X _begin(ReadWrite readWrite, TxnId txnId) {
++        return null ;
++    }
++
++    @Override
++    protected ByteBuffer _commitPrepare(TxnId txnId, X state) {
++        return null ;
++    }
++
++    @Override
++    protected void _commit(TxnId txnId, X state) {}
++
++    @Override
++    protected void _commitEnd(TxnId txnId, X state) {}
++
++    @Override
++    protected void _abort(TxnId txnId, X state) {}
++
++    @Override
++    protected void _complete(TxnId txnId, X state) {}
++
++    @Override
++    protected void _shutdown() {}
++
++    @Override
++    protected X _promote(TxnId txnId, X state) { return null; }
++
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionalComponentLifecycle.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionalComponentLifecycle.java
index 0000000,0000000..422753f
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionalComponentLifecycle.java
@@@ -1,0 -1,0 +1,480 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction.txn;
++
++import static org.seaborne.dboe.transaction.txn.TxnState.* ;
++
++import java.nio.ByteBuffer ;
++import java.util.Objects ;
++
++import org.apache.jena.query.ReadWrite ;
++
++import org.apache.jena.atlas.lib.InternalErrorException ;
++
++/** 
++ * Base implementation of the component interface for {@link TransactionalComponent}.
++ */
++public abstract class TransactionalComponentLifecycle<X> implements TransactionalComponent {
++    
++    // Pass down recovery operations.
++    // This class has no transaction-recorded state.
++    @Override public abstract void startRecovery();
++    @Override public abstract void recover(ByteBuffer ref);
++    @Override public abstract void finishRecovery();
++    
++    // ---- Normal operation
++    
++    private static final boolean CHECKING = false ;
++    private ThreadLocal<TxnState> trackTxn = CHECKING ? ThreadLocal.withInitial(() -> INACTIVE) : null ; 
++    
++    // Access to these two must be via the getter/setters below only.
++    // Allows stuff for thread switching.
++    private ThreadLocal<Transaction> threadTxn = new ThreadLocal<>() ;
++    private ThreadLocal<X> componentState = new ThreadLocal<>() ;
++    private final ComponentId componentId ;
++    
++    protected TransactionalComponentLifecycle(ComponentId componentId) {
++        this.componentId = componentId ;
++    }
++    
++    @Override
++    public ComponentId getComponentId() {
++        return componentId ;
++    }
++
++//    // Very dangerous!
++//    protected void setForThread(Transaction txn, X state) {
++//        threadTxn.set(txn);
++//        componentState.set(state);
++//    }
++    
++    /** Start a transaction */
++    @Override
++    final
++    public void begin(Transaction transaction) {
++        Objects.requireNonNull(transaction) ;
++        setTransaction(transaction); 
++        checkState(INACTIVE, COMMITTED, ABORTED) ;
++        setTrackTxn(ACTIVE);
++        X x = _begin(transaction.getMode(), transaction.getTxnId()) ;
++        setDataState(x);
++    }
++    
++    /** Promote a component in a transaction */
++    @Override
++    final
++    public boolean promote(Transaction transaction) {
++        Objects.requireNonNull(transaction) ;
++        checkState(ACTIVE) ;
++        X newState = _promote(transaction.getTxnId(), getDataState());
++        if ( newState == null )
++            return false;
++        setDataState(newState);
++        return true;
++    }
++    
++    /** Commit a transaction (make durable): prepare - commit - cleanup */  
++    @Override
++    final
++    public ByteBuffer commitPrepare(Transaction transaction) {
++        checkAligned(transaction) ;
++        checkState(ACTIVE) ;
++        try { return _commitPrepare(transaction.getTxnId(), getDataState()) ; }
++        finally { setTrackTxn(PREPARE) ; }
++    }
++
++    @Override
++    final
++    public void commit(Transaction transaction) {
++        checkAligned(transaction) ;
++        checkState(PREPARE) ;
++        _commit(transaction.getTxnId(), getDataState());
++        setTrackTxn(COMMIT) ;
++    }
++    
++    @Override
++    final
++    public void commitEnd(Transaction transaction) {
++        checkAligned(transaction) ;
++        checkState(COMMIT) ;
++        _commitEnd(transaction.getTxnId(), getDataState());
++        setTrackTxn(COMMITTED) ;
++        internalComplete(transaction) ;
++    }
++
++    @Override 
++    final
++    public void abort(Transaction transaction) {
++        checkAligned(transaction) ;
++        checkState(ACTIVE, PREPARE, COMMIT) ;
++        _abort(transaction.getTxnId(), getDataState()) ;
++        setTrackTxn(ABORTED) ;
++        internalComplete(transaction) ;
++    }
++
++    private void internalComplete(Transaction transaction) {
++        _complete(transaction.getTxnId(), getDataState());
++        setTrackTxn(INACTIVE) ;
++        releaseThreadState() ;
++    }
++
++    @Override
++    final
++    public void complete(Transaction transaction) {
++        if ( transaction.hasFinished() )
++            return ;
++        checkAligned(transaction) ;
++        ReadWrite m = getReadWriteMode() ;
++        switch(m) {
++            case READ:
++                checkState(ACTIVE, COMMITTED, ABORTED) ;
++                break ;
++            case WRITE:
++                // If bad, force abort?
++                checkState(COMMITTED, ABORTED) ; 
++                break ;
++        }
++        _complete(transaction.getTxnId(), getDataState());
++        switch(m) {
++            case READ:
++                internalComplete(transaction);
++                break ;
++            case WRITE:
++                // complete happened in the commit or abort.
++                break ;
++        }
++    }
++    
++    @Override 
++    final
++    public void shutdown() {
++        _shutdown() ;
++        clearInternal() ;
++    }
++    
++    @Override 
++    final public SysTransState detach() {
++        TxnState txnState = getTxnState() ;
++        if ( txnState == null )
++            return null ;
++        checkState(ACTIVE) ;
++        setTrackTxn(DETACHED) ;
++        SysTransState transState = new SysTransState(this, getTransaction(), getDataState()) ;
++        //****** Thread locals 
++        releaseThreadState() ;
++        return transState ;
++    }
++    
++    @Override
++    public void attach(SysTransState state) {
++        @SuppressWarnings("unchecked")
++        X x = (X)state.getState() ;
++//        // reset to not thread not in 
++//        if ( CHECKING )
++//            trackTxn : ThreadLocal<TxnState>
++//        
++//      
++        setTransaction(state.getTransaction());
++        setDataState(x);
++        setTrackTxn(ACTIVE) ;
++    }
++    
++    // -- Access object members.
++
++    public static class ComponentState<X> {
++        final TxnState state;
++        final Transaction txn ;
++        final X componentState ;
++        ComponentState(TxnState state, Transaction txn, X componentState) {
++            super() ;
++            this.state = state ;
++            this.txn = txn ;
++            this.componentState = componentState ;
++        }
++    }
++    
++    public ComponentState<X> getComponentState() {
++        return new ComponentState<>(getTrackTxn(), getTransaction(), getDataState()) ; 
++    }
++    
++    public void setComponentState(ComponentState<X> state) {
++        setTrackTxn(state.state);
++        setTransaction(state.txn);
++        setDataState(state.componentState);
++        
++    }
++
++    protected void releaseThreadState() {
++        // Remove thread locals
++        if ( trackTxn != null )
++            trackTxn.remove() ;
++        componentState.remove();
++        
++//        java version "1.8.0_31"
++//        Java(TM) SE Runtime Environment (build 1.8.0_31-b13)
++//        Java HotSpot(TM) 64-Bit Server VM (build 25.31-b07, mixed mode)
++//        
++//        openjdk version "1.8.0_40-internal"
++//        OpenJDK Runtime Environment (build 1.8.0_40-internal-b09)
++//        OpenJDK 64-Bit Server VM (build 25.40-b13, mixed mode)
++        
++        // This one is very important else the memory usage grows.  Not clear why.
++        // A Transaction has an internal AtomicReference.  Replacing the AtomicReference
++        // with a plain member variable slows the growth down greatly.
++        threadTxn.remove() ;
++    }
++    
++    protected void clearInternal() {
++        trackTxn = null ;
++        threadTxn = null ;
++        componentState = null ;
++    }
++    
++    //protected ComponentState<X> getState()
++    
++    protected X getDataState()                      { return componentState.get() ; }
++    protected void setDataState(X data)             { componentState.set(data) ; }
++
++    protected Transaction getTransaction()          { return threadTxn.get(); }
++    protected void setTransaction(Transaction txn)  { threadTxn.set(txn); }
++    
++    private void setTrackTxn(TxnState newState) {
++        if ( ! CHECKING ) return ;
++        trackTxn.set(newState);
++    }
++
++    // This is our record of the state - it is not necessarily the transactions
++    // view during changes.  This class tracks the expected incomign state and
++    // the transaction
++    private TxnState getTrackTxn() {
++        if ( ! CHECKING ) return null ;
++        return trackTxn.get();
++    }
++    // -- Access object members.
++    
++    // XXX Align to javadoc in TransactionalComponent.
++    
++    /* There are two lifecycles, one for write transaction, one
++     * for read transactions. This affects how transaction end so
++     * when/if promoted read->write transactions happen, a promoted
++     * transaction will follow the write lifecycle. 
++     * 
++     * In both lifecyles, the implementer can assume that calls
++     * happen at the right points and called only as needed.  Framework
++     * takes care of checking.  
++     * 
++     * Read lifecycle:
++     * A read transaction be be just begin(READ)-end() but may also
++     * have commit or abort before end. The _commitRead and _abortRead
++     * calls note if an explicit commit or abort occurs but may not be
++     * called. _endRead is always called exactly once.
++     *  
++     * _commitRead
++     * _abortRead
++     * _endRead
++     * _complete
++     * 
++     * Write lifecycle:
++     * A write transaction must have a commit() or abort() before end().
++     * The fraemwork will check this.
++     * 
++     * If the transaction commits:
++     * _commitPrepareWrite
++     * _commitWrite -- The transaction is 
++     * _commitEndWrite
++     * 
++     * If the transaction aborts:
++     * _abortWrite
++     * 
++     * After any lifecycle, a final call of
++     * _complete()
++     * 
++     * indicates ths transaction has fully finished.
++     * 
++     * Typically, an implementation does not need to take action in every call. 
++     */
++    
++//    /**
++//     * 
++//     * @param readWrite
++//     * @param txnId
++//     * @return
++//     */
++//    protected abstract X           _begin(ReadWrite readWrite, TxnId txnId) ;
++//    
++//    /**
++//     * 
++//     * @param txnId
++//     * @param state
++//     * @return
++//     */
++//    protected abstract ByteBuffer  _commitPrepareWrite(TxnId txnId, X  state) ;
++//    
++//    /**
++//     * 
++//     * @param txnId
++//     * @param state
++//     */
++//    protected abstract void        _commitWrite(TxnId txnId, X state) ;
++//    
++//    /**
++//     * 
++//     * @param txnId
++//     * @param state
++//     */
++//    protected abstract void        _commitEndWrite(TxnId txnId, X state) ;
++//    
++//    /**
++//     * 
++//     * @param txnId
++//     * @param state
++//     */
++//    protected abstract void        _abortWrite(TxnId txnId, X state) ;
++//    
++//    /**
++//     * 
++//     * @param txnId
++//     * @param state
++//     * @return
++//     */
++//    protected abstract ByteBuffer  _commitRead(TxnId txnId, X  state) ;
++//    
++//    /**
++//     * 
++//     * @param txnId
++//     * @param state
++//     * @return
++//     */
++//    protected abstract ByteBuffer  _abortRead(TxnId txnId, X  state) ;
++//
++//    /**
++//     * 
++//     * @param txnId
++//     * @param state
++//     */
++//    protected abstract void        _complete(TxnId txnId, X state) ;
++//    
++//    /**
++//     * 
++//     */
++//    protected abstract void        _shutdown() ;
++
++    protected abstract X           _begin(ReadWrite readWrite, TxnId txnId) ;
++    protected abstract X           _promote(TxnId txnId, X oldState) ;
++    protected abstract ByteBuffer  _commitPrepare(TxnId txnId, X  state) ;
++    protected abstract void        _commit(TxnId txnId, X state) ;
++    protected abstract void        _commitEnd(TxnId txnId, X state) ;
++    protected abstract void        _abort(TxnId txnId, X state) ;
++    protected abstract void        _complete(TxnId txnId, X state) ;
++    protected abstract void        _shutdown() ;
++    
++    protected ReadWrite getReadWriteMode() {
++        Transaction txn = getTransaction() ;
++        return txn.getMode() ;
++    }
++    
++    protected boolean isActiveTxn() {
++        TxnState txnState = getTxnState() ;
++        if ( txnState == null )
++            return false ; 
++        switch(getTxnState()) {
++            case INACTIVE: case END_ABORTED: case END_COMMITTED: 
++                return false ;
++            case ACTIVE: case DETACHED: case PREPARE: case ABORTED: case COMMIT: case COMMITTED:  
++                return true ;
++            //null: default: return false ;
++            // Get the compiler to check all states covered.
++        }
++        // Should not happen.
++        throw new InternalErrorException("Unclear transaction state") ;
++    }
++
++    protected boolean isReadTxn() { return ! isWriteTxn() ; }
++    
++    protected boolean isWriteTxn() {
++        Transaction txn = getTransaction();
++        return txn.isWriteTxn() ;
++    }
++    
++    protected void checkTxn() {
++        if ( ! isActiveTxn() )
++            throw new TransactionException("Not in a transaction") ;
++    }
++
++//    protected void requireWriteTxn() {
++//        Transaction txn = getTransaction();
++//        if ( txn == null )
++//            throw new TransactionException("Not a transaction");
++//        else 
++//            txn.requireWriteTxn() ;
++//    }
++    
++    protected void checkWriteTxn() {
++        Transaction txn = getTransaction();
++        if ( txn == null )
++            throw new TransactionException("Not a transaction");
++        else 
++            txn.requireWriteTxn() ;
++    }
++
++    // -- Access object members.
++    
++    private TxnState getTxnState() { 
++        Transaction txn = getTransaction() ;
++        if ( txn == null )
++            return null ;
++        return txn.getState() ;
++    }
++
++    private void checkAligned(Transaction transaction) {
++        if ( ! CHECKING ) return ;
++        Transaction txn = getTransaction();
++        if ( txn != transaction )
++            throw new TransactionException("Transaction is not the transaction of the thread") ; 
++    }
++    
++    private void checkState(TxnState expected) {
++        if ( ! CHECKING ) return ;
++        TxnState s = getTrackTxn() ;
++        if ( s != expected )
++            throw new TransactionException("Transaction is in state "+s+": expected state "+expected) ;
++    }
++
++    private void checkState(TxnState expected1, TxnState expected2) {
++        if ( ! CHECKING ) return ;
++        TxnState s = getTrackTxn() ;
++        if ( s != expected1 && s != expected2 )
++            throw new TransactionException("Transaction is in state "+s+": expected state "+expected1+" or "+expected2) ;
++    }
++
++    // Avoid varargs ... undue worry?
++    private void checkState(TxnState expected1, TxnState expected2, TxnState expected3) {
++        if ( ! CHECKING ) return ;
++        TxnState s = getTrackTxn() ;
++        if ( s != expected1 && s != expected2 && s != expected3 )
++            throw new TransactionException("Transaction is in state "+s+": expected state "+expected1+", "+expected2+" or "+expected3) ;
++    }
++
++    //    private void checkStateNot(State unexpected) {
++//        State s = state.get();
++//        if ( s == unexpected )
++//            throw new TransactionException("Transaction in unexpected state "+s) ;
++//    }
++
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionalComponentWrapper.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionalComponentWrapper.java
index 0000000,0000000..7ee8a75
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionalComponentWrapper.java
@@@ -1,0 -1,0 +1,107 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction.txn ;
++
++import java.nio.ByteBuffer ;
++
++public class TransactionalComponentWrapper implements TransactionalComponent {
++    protected final TransactionalComponent other ;
++
++    public TransactionalComponentWrapper(TransactionalComponent other) {
++        this.other = other ;
++    }
++
++    @Override
++    public void startRecovery() {
++        other.startRecovery() ;
++    }
++
++    @Override
++    public void recover(ByteBuffer ref) {
++        other.recover(ref) ;
++    }
++
++    @Override
++    public void finishRecovery() {
++        other.finishRecovery() ;
++    }
++    
++    @Override
++    public void cleanStart() {
++        other.cleanStart() ;
++    }
++
++    @Override
++    public ComponentId getComponentId() {
++        return other.getComponentId() ;
++    }
++
++    @Override
++    public void begin(Transaction transaction) {
++        other.begin(transaction) ;
++    }
++
++    @Override
++    public boolean promote(Transaction transaction) {
++        return other.promote(transaction) ;
++    }
++
++    @Override
++    public ByteBuffer commitPrepare(Transaction transaction) {
++        return other.commitPrepare(transaction) ;
++    }
++
++    @Override
++    public void commit(Transaction transaction) {
++        other.commit(transaction) ;
++    }
++
++    @Override
++    public void commitEnd(Transaction transaction) {
++        other.commitEnd(transaction) ;
++    }
++
++    @Override
++    public void abort(Transaction transaction) {
++        other.abort(transaction) ;
++    }
++
++    @Override
++    public void complete(Transaction transaction) {
++        other.complete(transaction) ;
++    }
++
++    @Override
++    public SysTransState detach() {
++        return other.detach() ;
++    }
++
++    @Override
++    public void attach(SysTransState systemState) {
++        other.attach(systemState) ;
++    }
++
++    @Override
++    public void shutdown() {
++        other.shutdown() ;
++    }
++
++    @Override 
++    public String toString() { return "W:"+other.toString() ; }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionalMRSW.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionalMRSW.java
index 0000000,0000000..89ce3b2
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionalMRSW.java
@@@ -1,0 -1,0 +1,136 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.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 ;
++
++/** 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<Object> {
++    // MRSW implementation of TransactionMVCC
++    // XXX Update to Jena style TransactionalLock
++    private ReadWriteLock lock = new ReentrantReadWriteLock() ;
++    
++    public TransactionalMRSW(ComponentId componentId) {
++        super(componentId) ;
++    }
++
++    // ---- Recovery phase
++    @Override
++    public void startRecovery() {}
++    
++    @Override
++    public void recover(ByteBuffer ref) {
++        Log.warn(this, "Called to recover a transaction (ignored)") ; 
++    }
++
++    @Override
++    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 
++            startReadTxn(); 
++        return createState();                    
++    }
++
++    private Object createState() {
++        return new Object();
++    }
++    
++    @Override
++    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;
++        }
++        lock.readLock().unlock(); 
++        return createState(); 
++    }
++
++    // Checks.
++    
++    protected void startReadTxn()   {}
++    protected void startWriteTxn()  {}
++    protected void finishReadTxn()  {}
++    protected void finishWriteTxn() {}
++
++    @Override
++    protected ByteBuffer _commitPrepare(TxnId txnId, Object obj) {
++        return null ;
++    }
++
++    @Override
++    protected void _commit(TxnId txnId, Object obj) {
++        clearup() ;
++    }
++
++    @Override
++    protected void _commitEnd(TxnId txnId, Object obj) {
++        clearup() ;
++    }
++
++    @Override
++    protected void _abort(TxnId txnId, Object obj) {
++        clearup() ;
++    }
++
++    @Override
++    protected void _complete(TxnId txnId, Object obj) {
++    }
++
++    @Override
++    protected void _shutdown() {
++        lock = null ;
++    }
++
++    private void clearup() {
++        Lock lock = getLock() ;
++        if ( isWriteTxn() )
++            finishWriteTxn(); 
++        else 
++            finishReadTxn(); 
++        lock.unlock(); 
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionalSystem.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionalSystem.java
index 0000000,0000000..05d3a51
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TransactionalSystem.java
@@@ -1,0 -1,0 +1,82 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction.txn;
++
++import org.apache.jena.query.ReadWrite ;
++import org.seaborne.dboe.transaction.Transactional ;
++
++/** Implementation side of a {@link Transactional}.
++ *  {@link Transactional} presents the application facing view
++ *  whereas this has all the possible steps of an implementation.  
++ *  Normally, the implementation of {@link #commit} is split up.
++ */
++public interface TransactionalSystem extends Transactional {
++    
++    @Override
++    public default void commit() {
++        commitPrepare() ;
++        commitExec() ;
++    }
++    
++    /** Do the 2-phase "prepare" step after which
++     *  the transaction coordinator decides whether to commit
++     *  or abort.  A TransactionalSystem must be prepared for
++     *  both possibilities.   
++     */
++    public void commitPrepare();
++
++    /** Do the 2-phase "commit" step */
++    public void commitExec();
++
++    /** Suspend this transaction, detaching from the current thread.
++     * A new transaction on this thread can performed but the detached
++     * transaction still exists and if it is a write transaction
++     * it can still block other write transactions.
++     */
++    public TransactionCoordinatorState detach() ;
++    
++    /** 
++     * Attach a transaction to this thread.
++     * A transaction system implementation usually imposes a rule that 
++     * only one thread can have a transaction attached at a time. 
++     */
++    public void attach(TransactionCoordinatorState coordinatorState) ;
++
++    /** Get the associated {@link TransactionCoordinator} */
++    public TransactionCoordinator getTxnMgr() ; 
++    
++    /**  Return the Read/Write state from the point of view of the caller.
++     * Return null when not in a transaction.
++     */
++    public ReadWrite getState() ;
++    
++    @Override
++    public default boolean isInTransaction() { return getState() != null ; }  
++    
++    /** Return an information view of the transaction for this thread, if any.
++     *  Returns null when there is no active transaction for this tread. 
++     */
++    public TransactionInfo getTransactionInfo() ;
++    
++    /** Return the transaction object for this thread.  
++     *  Low-level use only.
++     *  To get information about the current transaction, call {@link #getTransactionInfo}.
++     */
++    public Transaction getThreadTransaction() ;
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TxnId.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TxnId.java
index 0000000,0000000..61eb649
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TxnId.java
@@@ -1,0 -1,0 +1,63 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction.txn;
++
++
++/**
++ * {@code TxnId} is a identfier for a transaction.
++ * A component in a transaction can use it as a unique key.
++ * The {@code TxnId}
++ * <ul>
++ * <li>must be unique across a JVM run
++ * <li>unique across JVm runs if used as a persistent name
++ * <li>Must provide value equality semantics (two {@code TxnId} are {@code .equals}
++ * if 
++ * </ul>
++ * <p>
++ * It is preferrable that the TxnId is global unique over time and space.
++ */
++public interface TxnId {
++//    public static TxnId create() { return TxnIdSimple.create() ; }
++//    
++//    public static TxnId create(byte[] bytes) {
++//        switch(bytes.length) {
++//            case 8:  return TxnIdSimple.create(bytes) ;
++//            case 16: return TxnIdUuid.create(bytes) ;
++//            default:
++//                throw new TransactionException("TxnId bytes unrecognized: length="+bytes.length) ;
++//        }
++//    }
++//    
++    // Reminder to implement.
++    @Override
++    public int hashCode() ;
++    @Override
++    public boolean equals(Object other) ;
++    
++    public String name() ;
++    public byte[] bytes() ;
++    /**  A long that is a subset, or all or, the bytes.
++     * This should be unique for the lifetime of the transaction and
++     * ideally unique per system instance. It is not a persistent record
++     * of a transaction, it is for a transaction identifier in running code.
++     * ("system" maybe larger than on e JVM).    
++     */
++    public long runtime() ;
++    
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TxnIdFactory.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TxnIdFactory.java
index 0000000,0000000..1fece18
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TxnIdFactory.java
@@@ -1,0 -1,0 +1,60 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction.txn;
++
++import org.apache.commons.lang3.NotImplementedException ;
++
++/** Factory for some forms of {@link TxnId}.
++ * This is ony some possible {@link TxnIdGenerator} 
++ * 
++ * @see TxnId
++ */
++public class TxnIdFactory {
++    /** Generator for {@link TxnId}s for the counter based implemention. */
++    public static final TxnIdGenerator txnIdGenSimple = ()->TxnIdSimple.create() ;
++    /** Generator for {@link TxnId}s for the UUID based implemention. */
++    public static final TxnIdGenerator txnIdGenUuid   = ()->TxnIdUuid.create() ;
++    
++    /** Return the default, good enough for one JVM
++     * (usually the simple counter based implemention) 
++     */
++    public static TxnId create() {
++        return createSimple() ;
++    }
++    
++    /** Return a TxnId from the counter based implemention. */
++    public static TxnId createSimple() {
++        return txnIdGenSimple.generate() ; 
++    }
++    
++    /** Return a TxnId from the UUID based implemention. */
++    public static TxnId createUuid() {
++        return txnIdGenUuid.generate() ; 
++    }
++    
++    public static TxnId create(byte[] bytes) {
++        switch(bytes.length) {
++            case 8 : return TxnIdSimple.create(bytes) ;
++            case 16 :return TxnIdUuid.create(bytes) ;
++            default:
++                throw new NotImplementedException("Unrcognized bytes length: "+bytes.length) ;
++        }
++    }
++
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TxnIdGenerator.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TxnIdGenerator.java
index 0000000,0000000..09c291f
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TxnIdGenerator.java
@@@ -1,0 -1,0 +1,38 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction.txn;
++
++
++/**
++ * Generator of {@link TxnId}s. 
++ * {@code TxnId} is a identfier for a transaction.
++ * A component in a transaction can use it as a unique key.
++ * The {@code TxnId}
++ * <ul>
++ * <li>must be unique across a JVM run
++ * <li>unique across JVm runs if used as a persistent name
++ * <li>Must provide value equality semantics (two {@code TxnId} are {@code .equals}
++ * if 
++ * </ul>
++ * <p>
++ * It is preferrable that the TxnId is global unique over time and space.
++ */
++public interface TxnIdGenerator {
++    public TxnId generate() ;
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TxnIdSimple.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TxnIdSimple.java
index 0000000,0000000..8a0a3b8
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TxnIdSimple.java
@@@ -1,0 -1,0 +1,83 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction.txn;
++
++import java.util.concurrent.atomic.AtomicLong ;
++
++import org.apache.jena.atlas.lib.Bytes ;
++
++/**
++ *  Simple TxnId, mainly for debugging.
++ */
++public class TxnIdSimple implements TxnId {
++    private static AtomicLong counter = new AtomicLong(0) ;
++    
++    static TxnIdSimple create() {
++        return new TxnIdSimple(counter.incrementAndGet()) ; 
++    }
++
++    public static TxnIdSimple create(byte[] bytes) {
++        return new TxnIdSimple(Bytes.getLong(bytes)) ;
++    }
++
++    private final long x ;
++    
++    public TxnIdSimple(long x) { 
++        this.x = x ; 
++    }
++
++    @Override
++    public String name() {
++        return String.format("0x%04X",x) ;
++    }
++
++    @Override
++    public byte[] bytes() {
++        return Bytes.packLong(x) ;
++    }
++
++    @Override
++    public int hashCode() {
++        return Long.hashCode(x) ;
++    }
++
++    @Override
++    public boolean equals(Object obj) {
++        if ( this == obj )
++            return true ;
++        if ( obj == null )
++            return false ;
++        if ( getClass() != obj.getClass() )
++            return false ;
++        TxnIdSimple other = (TxnIdSimple)obj ;
++        if ( x != other.x )
++            return false ;
++        return true ;
++    }
++    
++    @Override
++    public String toString() {
++        return "txn:"+x ;
++    }
++
++    @Override
++    public long runtime() {
++        return x ;
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TxnIdUuid.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TxnIdUuid.java
index 0000000,0000000..92ef964
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TxnIdUuid.java
@@@ -1,0 -1,0 +1,108 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction.txn;
++
++import java.util.UUID ;
++
++import org.apache.jena.atlas.lib.Bytes ;
++import org.seaborne.dboe.migrate.L ;
++
++import org.apache.jena.shared.uuid.JenaUUID ;
++
++/** {@link TxnId} based on a {@link UUID}. 
++ */
++public class TxnIdUuid implements TxnId {
++    
++    static TxnIdUuid create() {
++        UUID id = JenaUUID.generate().asUUID() ; // UUID.randomUUID() ;
++        return new TxnIdUuid(id) ; 
++    }
++
++    public static TxnIdUuid create(byte[] bytes) {
++        long mostSignificantBits = Bytes.getLong(bytes, 0) ;
++        long leastSignificantBits = Bytes.getLong(bytes, 8) ;
++        return new TxnIdUuid(mostSignificantBits, leastSignificantBits) ; 
++    }
++
++    private long mostSignificantBits ;
++    private long leastSignificantBits ;
++    private byte[] bytes = null ;
++    private String name = null ;
++    
++    /*package*/ TxnIdUuid(UUID id) { 
++        mostSignificantBits = id.getMostSignificantBits() ;
++        leastSignificantBits = id.getLeastSignificantBits() ;
++    }
++
++    /*package*/ TxnIdUuid(long mostSig, long leastSig) { 
++        mostSignificantBits = mostSig ;
++        leastSignificantBits = leastSig ;
++    }
++
++    @Override
++    public String name() {
++        if ( name == null )
++            name = L.uuidToString(mostSignificantBits, leastSignificantBits) ;
++        return name ;
++    }
++
++    @Override
++    public byte[] bytes() {
++        if ( bytes == null )
++            bytes = L.uuidAsBytes(mostSignificantBits, leastSignificantBits) ;
++        return bytes ;
++    }
++
++    @Override
++    public long runtime() {
++        // In type 1, the mostSignificantBits have the timestamp in it.
++        return mostSignificantBits ;
++    }
++
++    @Override
++    public int hashCode() {
++        final int prime = 31 ;
++        int result = 1 ;
++        result = prime * result + (int)(leastSignificantBits ^ (leastSignificantBits >>> 32)) ;
++        result = prime * result + (int)(mostSignificantBits ^ (mostSignificantBits >>> 32)) ;
++        return result ;
++    }
++
++    @Override
++    public boolean equals(Object obj) {
++        if ( this == obj )
++            return true ;
++        if ( obj == null )
++            return false ;
++        if ( getClass() != obj.getClass() )
++            return false ;
++        TxnIdUuid other = (TxnIdUuid)obj ;
++        if ( leastSignificantBits != other.leastSignificantBits )
++            return false ;
++        if ( mostSignificantBits != other.mostSignificantBits )
++            return false ;
++        return true ;
++    }
++
++    @Override
++    public String toString() {
++        //return name() ;
++        return String.format("[%04X]", mostSignificantBits&0xFFFF) ;
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TxnState.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TxnState.java
index 0000000,0000000..dd33056
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/TxnState.java
@@@ -1,0 -1,0 +1,21 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction.txn;
++
++/** The states of the transaction lifecycle */ 
++public enum TxnState { INACTIVE, ACTIVE, DETACHED, PREPARE, COMMIT, COMMITTED, ABORTED, END_COMMITTED, END_ABORTED }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/journal/Journal.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/journal/Journal.java
index 0000000,0000000..023b698
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/journal/Journal.java
@@@ -1,0 -1,0 +1,333 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction.txn.journal;
++
++import static org.seaborne.dboe.sys.Sys.SizeOfInt ;
++
++import java.nio.ByteBuffer ;
++import java.util.Iterator ;
++import java.util.zip.Adler32 ;
++
++import org.apache.jena.atlas.iterator.IteratorSlotted ;
++import org.apache.jena.atlas.lib.ByteBufferLib ;
++import org.apache.jena.atlas.lib.Closeable ;
++import org.apache.jena.atlas.lib.FileOps ;
++import org.apache.jena.atlas.lib.Sync ;
++import org.seaborne.dboe.base.file.BufferChannel ;
++import org.seaborne.dboe.base.file.BufferChannelFile ;
++import org.seaborne.dboe.base.file.BufferChannelMem ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.dboe.sys.Names ;
++import org.seaborne.dboe.transaction.txn.ComponentId ;
++import org.seaborne.dboe.transaction.txn.PrepareState ;
++import org.seaborne.dboe.transaction.txn.TransactionException ;
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++/** A transaction journal.
++* The journal is append-only for writes, with truncation of the file
++* every so often. It is read during recovery.
++* The size of entries depends on per-component redo/undo records;
++* the control records like COMMIT are quite small.
++* Entries have a CRC to ensure that part-entries are not acted on.
++*/
++
++public final
++class Journal implements Sync, Closeable
++{
++    private static final boolean LOGGING = false ;
++    private static Logger log = LoggerFactory.getLogger(Journal.class) ;
++    
++    private static boolean logging() {
++        return LOGGING && log.isInfoEnabled() ;
++    }
++    
++    private static void log(String fmt, Object...args) {
++        if ( ! logging() )
++            return ;
++        log.info(String.format(fmt, args));
++    }
++    
++    private BufferChannel channel ;
++    private long position ;
++    
++    // Header: fixed, inc CRC
++    //   length of data             4 bytes
++    //   CRC of whole entry.        4 bytes
++    //   entry type                 4 bytes (1 byte and 3 alignment)
++    //   component                  16 bytes (fixed??)
++    // Data area : variable
++    //   Bytes
++    
++    private static final int posnLength     = 0 ;
++    private static final int posnCRC        = posnLength + SizeOfInt ;
++    private static final int posnEntry      = posnCRC    + SizeOfInt ;
++    private static final int posnComponent  = posnEntry  + SizeOfInt ;
++    // Start of the component data area.
++    private static final int posnData       = posnComponent  + ComponentId.SIZE ;
++    
++    // Currently, the header is fixed size so this is the size.
++    private static int HeaderLen            = posnData-posnLength ;
++    
++    private ByteBuffer header    = ByteBuffer.allocate(HeaderLen) ;
++    
++    public static boolean exists(Location location) {
++        if ( location.isMem() )
++            return false ;
++        return FileOps.exists(journalFilename(location)) ;
++    }
++
++    public static Journal create(Location location) {
++        BufferChannel chan ;
++        String channelName = journalFilename(location) ;
++        if ( location.isMem() )
++            chan = BufferChannelMem.create(channelName) ;
++        else
++            chan = BufferChannelFile.create(channelName) ;
++        return create(chan) ;
++    }
++
++    public static Journal create(BufferChannel chan) {
++        return new Journal(chan) ;
++    }
++
++    private static String journalFilename(Location location) {
++        return location.absolute(Names.journalFile) ;
++    }
++
++    private Journal(BufferChannel channel) {
++        this.channel = channel ;
++        position = 0 ;
++    }
++
++    // synchronized : excessive?
++    // Given the calling context, we know it's thread safe.
++    
++    synchronized public long writeJournal(JournalEntry entry) {
++        long posn = write(entry.getType(), entry.getComponentId(), entry.getByteBuffer()) ;
++
++        if ( entry.getPosition() < 0 ) {
++            entry.setPosition(posn) ;
++            entry.setEndPosition(position) ;
++        }
++        return posn ;
++    }
++
++//    /** Write an entry and return its location in the journal */
++//    synchronized public void write(List<PrepareState> prepareStates) {
++//        prepareStates.forEach(this::write) ;
++//    }
++
++    public long write(PrepareState prepareState) {
++        return write(JournalEntryType.REDO, prepareState.getComponent(), prepareState.getData()) ;
++    }
++    
++    /** Write an entry and return it's location in the journal */
++    synchronized public long write(JournalEntryType type, ComponentId componentId, ByteBuffer buffer) {
++        // Check buffer set right.
++        if ( LOGGING ) {
++            log("write@%-3d >> %s %s %s", position, type.name(),
++                componentId == null ? "<null>" : componentId.label(),
++                buffer == null ? "<null>" : ByteBufferLib.details(buffer)) ;
++        }
++
++        long posn = position ;
++        int len = -1 ;
++        int bufferLimit = -1 ;
++        int bufferPosition = -1 ;
++        if ( buffer != null ) {
++            bufferLimit = buffer.limit() ;
++            bufferPosition = buffer.position() ;
++            buffer.rewind() ;
++            len = buffer.remaining() ;
++        }
++
++        // Header: (length/4, crc/4, entry/4, component/16)
++
++        header.clear() ;
++        header.putInt(len) ;
++        header.putInt(0) ; // Set CRC to zero
++        header.putInt(type.id) ;
++        header.put(componentId.getBytes()) ;
++        header.flip() ;
++        // Need to put CRC in before writing.
++
++        Adler32 adler = new Adler32() ;
++        adler.update(header.array()) ;
++
++        if ( len > 0 ) {
++            adler.update(buffer) ;
++            buffer.rewind() ;
++        }
++
++        int crc = (int)adler.getValue() ;
++        header.putInt(posnCRC, crc) ;
++        if ( LOGGING )
++            log("write@    -- crc = %s", Integer.toHexString(crc) ) ;
++        channel.write(header) ;
++        if ( len > 0 ) {
++            channel.write(buffer) ;
++            buffer.position(bufferPosition) ;
++            buffer.limit(bufferLimit) ;
++        }
++        position += HeaderLen + len ;
++        if ( LOGGING )
++            log("write@%-3d << %s", position, componentId.label()) ;
++        
++        if ( len > 0 ) {
++            buffer.position(bufferPosition) ;
++            buffer.limit(bufferLimit) ;
++        }
++        
++        return posn ;
++    }
++
++    synchronized public JournalEntry readJournal(long id) {
++        return _readJournal(id) ;
++    }
++
++    private JournalEntry _readJournal(long id) {
++        long x = channel.position() ;
++        if ( x != id )
++            channel.position(id) ;
++        JournalEntry entry = _read() ;
++        long x2 = channel.position() ;
++        entry.setPosition(id) ;
++        entry.setEndPosition(x2) ;
++        if ( x != id )
++            channel.position(x) ;
++        return entry ;
++    }
++
++    // read one entry at the channel position.
++    // Move position to end of read.
++    private JournalEntry _read() {
++        if ( LOGGING ) {
++            log("read@%-3d >>", channel.position()) ;   
++        }
++        
++        header.clear() ;
++        int lenRead = channel.read(header) ;
++        if ( lenRead == -1 ) {
++            // probably broken file.
++            throw new TransactionException("Read off the end of a journal file") ;
++            // return null ;
++        }
++        if ( lenRead != header.capacity() )
++            throw new TransactionException("Partial read of journal file") ;
++            
++        header.rewind() ;
++        // Header: (length/4, crc/4, entry/4, component/16)
++        int len = header.getInt() ;
++        int checksum = header.getInt() ;
++        header.putInt(posnCRC, 0) ;
++        int entryType = header.getInt() ;
++        byte[] bytes = new byte[ComponentId.SIZE] ;
++        header.get(bytes) ;
++        ComponentId component = ComponentId.create(null, bytes) ;
++
++        Adler32 adler = new Adler32() ;
++        adler.update(header.array()) ;
++
++        ByteBuffer bb = null ;
++        if ( len > 0 ) {
++            bb = ByteBuffer.allocate(len) ;
++            lenRead = channel.read(bb) ;
++            if ( lenRead != len )
++                throw new TransactionException("Failed to read the journal entry data: wanted " + len + " bytes, got " + lenRead) ;
++            bb.rewind() ;
++            adler.update(bb) ;
++            bb.rewind() ;
++        }
++
++        int crc = (int)adler.getValue() ;
++        if ( checksum != crc )
++            throw new TransactionException("Checksum error reading from the Journal. "+Integer.toHexString(checksum)+" / "+Integer.toHexString(crc)) ;
++
++        JournalEntryType type = JournalEntryType.type(entryType) ;
++        JournalEntry entry = new JournalEntry(type, component, bb) ;
++        if ( LOGGING )
++            log("read@%-3d >> %s", channel.position(), entry) ;   
++        return entry ;
++    }
++
++    /**
++     * Iterator of entries from current point in Journal, going forward. Must be
++     * JournalEntry aligned at start.
++     */
++    private class IteratorEntries extends IteratorSlotted<JournalEntry> {
++        JournalEntry slot = null ;
++        final long   endPoint ;
++        long         iterPosn ;
++
++        public IteratorEntries(long startPosition) {
++            iterPosn = startPosition ;
++            endPoint = channel.size() ;
++        }
++
++        @Override
++        protected JournalEntry moveToNext() {
++            // synchronized necessary? Outer policy is single thread?
++            synchronized (Journal.this) {
++                if ( iterPosn >= endPoint )
++                    return null ;
++                JournalEntry e = _readJournal(iterPosn) ;
++                iterPosn = e.getEndPosition() ;
++                return e ;
++            }
++        }
++
++        @Override
++        protected boolean hasMore() {
++            return iterPosn < endPoint ;
++        }
++    }
++
++    public Iterator<JournalEntry> entries() {
++        return new IteratorEntries(0) ;
++    }
++
++    synchronized public Iterator<JournalEntry> entries(long startPosition) {
++        return new IteratorEntries(startPosition) ;
++    }
++
++    @Override
++    public void sync()  { channel.sync() ; }
++
++    @Override
++    public void close() { channel.close() ; }
++
++    public long size()  { return channel.size() ; }
++    
++    public boolean isEmpty()  { return channel.size() == 0 ; }
++
++    public void truncate(long size) { channel.truncate(size) ; }
++    
++    public void reset() { 
++        truncate(0) ;
++        sync() ;
++    }
++    
++//    public void append()    { position(size()) ; }
++    
++     public long position() { return channel.position() ; }  
++    
++//    public void position(long posn) { channel.position(posn) ; }
++    
++    public String getFilename() { return channel.getFilename() ; }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/journal/JournalControl.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/journal/JournalControl.java
index 0000000,0000000..9066e00
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/journal/JournalControl.java
@@@ -1,0 -1,0 +1,296 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction.txn.journal;
++
++import java.util.Iterator ;
++
++import org.seaborne.dboe.base.file.BufferChannelFile ;
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++
++public class JournalControl
++{
++    private static Logger log = LoggerFactory.getLogger(JournalControl.class) ;
++
++    // In TransactionCoordinator
++//    // Interface
++//    public static void recovery(TransactionCoordinator txnCoord)
++//    {
++//        txnCoord.getJournal() ;
++//        
++//    }
++//    
++//    public static void replay(Transaction transaction)
++//    {}
++    
++    /** Dump a journal - debug support function - opens the journal specially - inconsistent views possible */
++    public static void print(String filename) {
++        BufferChannelFile chan = BufferChannelFile.createUnmanaged(filename, "r") ;
++        Journal journal = Journal.create(chan) ;
++        JournalControl.print(journal) ;
++        chan.close() ;
++    }
++    
++    public static void print(Journal journal) {
++        System.out.println("Size: "+journal.size()) ;
++        Iterator<JournalEntry> iter = journal.entries() ; 
++        
++        for (  ; iter.hasNext() ; )
++        {
++            JournalEntry e = iter.next() ;
++            //System.out.println("Posn: "+journal.position()+" : ("+(journal.size()-journal.position())+")") ;
++            System.out.println(JournalEntry.format(e)) ;
++        }
++    }
++
++//    /** Recover a base storage DatasetGraph */
++//    public static void recovery(Location location)
++//    {
++//        if ( location.isMem() )
++//            return ;
++//        
++//        // Do we need to recover?
++//        Journal journal = findJournal(location) ;
++//        if ( journal == null || journal.isEmpty() )
++//            return ;
++//        
++//        for ( FileRef fileRef : dsg.getConfig().nodeTables.keySet() )
++//            recoverNodeDat(dsg, fileRef) ;
++//        recoverFromJournal(dsg.getConfig(), journal) ;
++//        
++//        journal.close() ;
++//        // Recovery complete.  Tidy up.  Node journal files have already been handled.
++//        if ( journal.getFilename() != null )
++//        {
++//            if ( FileOps.exists(journal.getFilename()) )
++//                FileOps.delete(journal.getFilename()) ;
++//        }
++//    }
++//    
++//    private static Journal findJournal(Location location)
++//    {
++//        String journalFilename = location.absolute(Names.journalFile) ;
++//        File f = new File(journalFilename) ;
++//        //if ( FileOps.exists(journalFilename)
++//        
++//        if ( f.exists() && f.isFile() && f.length() > 0 )
++//            return Journal.create(location) ;
++//        else
++//            return null ;
++//    }
++//
++//    /** Recovery from a journal.
++//     *  Find if there is a commit record; if so, replay the journal to that point.
++//     *  Try to see if there is another commit record ...
++//     *  Return true if a recovery was attempted; return false if we decided no work needed.
++//     */
++//    public static boolean recoverFromJournal(StorageConfig sConf, Journal jrnl)
++//    {
++//        if ( jrnl.isEmpty() )
++//            return false ;
++//
++//        long posn = 0 ;
++//        for ( ;; )
++//        {
++//            // Any errors indicate a partially written journal.
++//            // A commit was not written properly in the prepare phase.
++//            // e.g. JVM died half-way though writing the prepare phase data.
++//            // The valid journal ends at this point. Exit loop and clean up.  
++//
++//            long x ;
++//            try { x = scanForCommit(jrnl, posn) ; }
++//            catch (TDBException ex) { x = -1 ; }
++//            
++//            if ( x == -1 ) break ;
++//            recoverSegment(jrnl, posn, x, sConf) ;
++//            posn = x ;
++//        }
++//
++//        // We have replayed the journals - clean up.
++//        jrnl.truncate(0) ;
++//        jrnl.sync() ;
++//        syncAll(sConf) ;
++//        return true ;
++//    }
++//
++//    /** Scan to a commit entry, starting at a given position in the journal.
++//     * Return address of entry after commit if found, else -1.
++//     */
++//    private static long scanForCommit(Journal jrnl, long startPosn)
++//    {
++//        Iterator<JournalEntry> iter = jrnl.entries(startPosn) ;
++//        try {
++//            for ( ; iter.hasNext() ; )
++//            {
++//                JournalEntry e = iter.next() ;
++//                if ( e.getType() == JournalEntryType.Commit )
++//                    return e.getEndPosition() ;
++//            }
++//            return -1 ;
++//        } finally { Iter.close(iter) ; }
++//    }
++//    
++//    /** Recover one transaction from the start position given.
++//     *  Scan to see if theer is a commit; if found, play the
++//     *  journal from the start point to the commit.
++//     *  Return true is a commit was found.
++//     *  Leave journal positioned just after commit or at end if none found.
++//     */
++//    private static void recoverSegment(Journal jrnl, long startPosn, long endPosn, StorageConfig sConf)
++//    {
++//        Iterator<JournalEntry> iter = jrnl.entries(startPosn) ;
++//        iter = jrnl.entries(startPosn) ;
++//        try {
++//            for ( ; iter.hasNext() ; )
++//            {
++//                JournalEntry e = iter.next() ;
++//                if ( e.getType() == JournalEntryType.Commit )
++//                {
++//                    if ( e.getEndPosition() != endPosn )
++//                        log.warn(format("Inconsistent: end at %d; expected %d", e.getEndPosition(), endPosn)) ;
++//                    return ;
++//                }
++//                replay(e, sConf) ;
++//            }
++//        } finally { Iter.close(iter) ; }
++//    }
++//    
++//    /** Recover a node data file (".dat").
++//     *  Node data files are append-only so recovering, then not using the data is safe.
++//     *  Node data file is a precursor for full recovery that works from the master journal.
++//     */
++//    private static void recoverNodeDat(DatasetGraphTDB dsg, FileRef fileRef)
++//    {
++//        // See DatasetBuilderTxn - same name generation code.
++//        // [TxTDB:TODO]
++//        
++//        RecordFactory recordFactory = new RecordFactory(SystemTDB.LenNodeHash, SystemTDB.SizeOfNodeId) ;
++//        NodeTable baseNodeTable = dsg.getConfig().nodeTables.get(fileRef) ;
++//        String objFilename = fileRef.getFilename()+"-"+Names.extJournal ;
++//        objFilename = dsg.getLocation().absolute(objFilename) ;
++//        File jrnlFile = new File(objFilename) ;
++//        if ( jrnlFile.exists() && jrnlFile.length() > 0 )
++//        {
++//            syslog.info("Recovering node data: "+fileRef.getFilename()) ;
++//            ObjectFile dataJrnl = FileFactory.createObjectFileDisk(objFilename) ;
++//            NodeTableTrans ntt = new NodeTableTrans(null, objFilename, baseNodeTable, new IndexMap(recordFactory), dataJrnl) ;
++//            ntt.append() ;
++//            ntt.close() ;
++//            dataJrnl.close() ;
++//            baseNodeTable.sync() ;
++//        }
++//        if ( jrnlFile.exists() )
++//            FileOps.delete(objFilename) ;
++//    }
++//    
++//    public static void replay(Transaction transaction)
++//    {
++//        if ( syslog.isDebugEnabled())
++//            syslog.debug("Replay "+transaction.getLabel()) ;
++//        Journal journal = transaction.getJournal() ;
++//        DatasetGraphTDB dsg = transaction.getBaseDataset() ;
++//        // Currently, we (crudely) replay the whole journal.
++//        replay(journal, dsg.getConfig()) ;
++//    }
++//    
++//    /** Replay a journal onto a dataset */
++//    public static void replay(Journal journal, DatasetGraphTDB dsg)
++//    {
++//        replay(journal, dsg.getConfig()) ;
++//    }
++//    
++//    /** Replay a journal onto a store configuration (the file resources) */
++//    private static void replay(Journal journal, StorageConfig sConf)
++//    {
++//        if ( journal.size() == 0 )
++//            return ;
++//        
++//        journal.position(0) ;
++//        try {
++//            Iterator<JournalEntry> iter = journal.entries() ; 
++//
++//            for (  ; iter.hasNext() ; )
++//            {
++//                JournalEntry e = iter.next() ;
++//                replay(e, sConf) ;
++//
++//                // There is no point sync here.  
++//                // No writes via the DSG have been done. 
++//                // so all internal flags "syncNeeded" are false.
++//                //dsg.sync() ;
++//            }
++//        } 
++//        catch (RuntimeException ex)
++//        { 
++//            // Bad news travels fast.
++//            syslog.error("Exception during journal replay", ex) ;
++//            throw ex ;
++//        }
++//        
++//        Collection<BlockMgr> x = sConf.blockMgrs.values() ;
++//        for ( BlockMgr blkMgr : x )
++//            blkMgr.syncForce() ;
++//        // Must do a hard sync before this.
++//        journal.truncate(0) ;
++//    }
++//
++//    /** return true for "go on" */
++//    private static boolean replay(JournalEntry e, StorageConfig sConf)
++//    {
++//        switch (e.getType())
++//        {
++//            case Block:
++//            {
++//                BlockMgr blkMgr = sConf.blockMgrs.get(e.getFileRef()) ;
++//                Block blk = e.getBlock() ;
++//                log.debug("Replay: {} {}",e.getFileRef(), blk) ;
++//                blk.setModified(true) ;
++//                blkMgr.overwrite(blk) ; 
++//                return true ;
++//            }   
++//            case Buffer:
++//            {
++//                BufferChannel chan = sConf.bufferChannels.get(e.getFileRef()) ;
++//                ByteBuffer bb = e.getByteBuffer() ;
++//                log.debug("Replay: {} {}",e.getFileRef(), bb) ;
++//                chan.write(bb, 0) ; // YUK!
++//                return true ;
++//            }
++//                
++//            case Commit:
++//                return false ;
++//            case Abort:
++//            case Object:
++//            case Checkpoint:
++//                errlog.warn("Unexpected block type: "+e.getType()) ;
++//        }
++//        return false ;
++//    }
++//
++//    private static void syncAll(StorageConfig sConf)
++//    {
++//        Collection<BlockMgr> x = sConf.blockMgrs.values() ;
++//        for ( BlockMgr blkMgr : x )
++//            blkMgr.syncForce() ;
++//        Collection<BufferChannel> y = sConf.bufferChannels.values() ;
++//        for ( BufferChannel bChan : y )
++//            bChan.sync() ;
++//        //sConf.nodeTables ;
++//    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/journal/JournalEntry.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/journal/JournalEntry.java
index 0000000,0000000..c6ac343
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/journal/JournalEntry.java
@@@ -1,0 -1,0 +1,89 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction.txn.journal;
++
++import java.nio.ByteBuffer ;
++
++import org.apache.jena.atlas.lib.ByteBufferLib ;
++import org.apache.jena.atlas.lib.Bytes ;
++import org.seaborne.dboe.transaction.txn.ComponentId ;
++import org.seaborne.dboe.transaction.txn.ComponentIds ;
++
++public class JournalEntry
++{
++//    static public final JournalEntry Redo       = new JournalEntry(JournalEntryType.REDO) ;
++//    static public final JournalEntry Undo       = new JournalEntry(JournalEntryType.UNDO) ;
++
++    
++    // Zero payload JournalEntry - create once.
++    static public final JournalEntry COMMIT     = new JournalEntry(JournalEntryType.COMMIT, ComponentIds.idSystem) ;
++    static public final JournalEntry ABORT      = new JournalEntry(JournalEntryType.ABORT, ComponentIds.idSystem) ;
++    
++    private long position = -1 ;                // Location in the Journal (if known).
++    private long endPosition = -1 ;             // End location in the Journal: offset of next entry start.
++
++    private final JournalEntryType type ;
++    private final ComponentId componentId ;
++    private final ByteBuffer data ;
++    
++    private JournalEntry(JournalEntryType type, ComponentId id) {
++        this(type, id, null) ;
++    }
++    
++    public JournalEntry(JournalEntryType type, ComponentId componentId, ByteBuffer bytes) {
++        this.type = type ;
++        this.componentId = componentId ;
++        this.data = bytes ;
++    }
++
++    void setPosition(long posn)             { position = posn ; }
++    void setEndPosition(long endPosn)       { endPosition = endPosn ; }
++
++    public long getPosition()               { return position ; }
++    long getEndPosition()                   { return endPosition ; }
++    
++    public JournalEntryType getType()       { return type ; }
++    public ComponentId getComponentId()     { return componentId ; }
++    public ByteBuffer getByteBuffer()       { return data ; }
++    
++    @Override
++    public String toString()
++    {
++        return "JournalEntry: "+type+" "+componentId ;
++    }
++    
++    static public String format(JournalEntry entry)
++    {
++        StringBuilder sbuff = new StringBuilder() ;
++        
++        sbuff.append("Entry: ") ;
++        sbuff.append("  "+entry.type) ;
++        if ( entry.componentId != null ) {
++            String label = entry.componentId.label() ;
++            if ( label != null )
++                sbuff.append(label) ;
++            sbuff.append(" [..") ;
++            int z = Bytes.getInt(entry.componentId.getBytes(), entry.componentId.getBytes().length-4) ;
++            sbuff.append(Integer.toHexString(z)) ;
++            sbuff.append("]") ;
++        }
++        if ( entry.data != null )
++            sbuff.append("  "+ByteBufferLib.details(entry.data)) ;
++        return sbuff.toString() ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/journal/JournalEntryType.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/journal/JournalEntryType.java
index 0000000,0000000..48398b2
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/journal/JournalEntryType.java
@@@ -1,0 -1,0 +1,57 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction.txn.journal;
++
++import org.apache.jena.atlas.lib.InternalErrorException ;
++import org.apache.jena.atlas.logging.Log ;
++
++/** 
++ * Types of Journal entry.
++ * This set is quite general and so not all cases may be used in practice.
++ * <p>
++ * The id must be stable across new versions on the code as it ends up
++ * in the journal on-disk so we are explicit about id even though there is
++ * {@link Enum#ordinal} 
++ */
++public enum JournalEntryType 
++{ 
++    /*
++     * REDO, UNDO -- Actions (UNDO unused)
++     * COMMIT, ABORT -- Transaction action (ABORT unused)
++     * CHECKPOINT -- data written to the journal as a safe spill file (unused)
++     */
++    REDO(1), UNDO(2), COMMIT(3), ABORT(4) 
++    /*, CHECKPOINT(6)*/
++    ;
++
++    final int id ;
++    JournalEntryType(int x) { id = x ; }
++    int getId() { return id ; }
++    static public JournalEntryType type(int x)
++    {
++        if      ( x == REDO.id )            return REDO ;
++        else if ( x == UNDO.id )            return UNDO ;
++        else if ( x == COMMIT.id )          return COMMIT ;
++        else if ( x == ABORT.id )           return ABORT ;
++        //else if ( x == CHECKPOINT.id )      return CHECKPOINT ;
++        else {
++            Log.error(JournalEntryType.class, "Unknown type: "+x) ;
++            throw new InternalErrorException("Unknown type: "+x) ;
++        }
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/package-info.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/package-info.java
index 0000000,0000000..60f3bdb
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/main/java/org/seaborne/dboe/transaction/txn/package-info.java
@@@ -1,0 -1,0 +1,12 @@@
++/** Transactions.
++ * Key clases: 
++ * <ul>
++ * <li>{@link org.seaborne.dboe.transaction.txn.Transaction Transaction} 
++ * <li>{@link org.seaborne.dboe.transaction.txn.TransactionCoordinator TransactionCoordinator}
++ * <li>
++ * </ul> 
++ * 
++ */
++
++package org.seaborne.dboe.transaction.txn;
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/AbstractTestTxn.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/AbstractTestTxn.java
index 0000000,0000000..8454c7d
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/AbstractTestTxn.java
@@@ -1,0 -1,0 +1,63 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction;
++
++import static org.junit.Assert.assertEquals ;
++
++import java.util.Arrays ;
++import java.util.List ;
++
++import org.junit.After ;
++import org.junit.Before ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.dboe.transaction.TransInteger ;
++import org.seaborne.dboe.transaction.TransMonitor ;
++import org.seaborne.dboe.transaction.Transactional ;
++import org.seaborne.dboe.transaction.txn.* ;
++import org.seaborne.dboe.transaction.txn.journal.Journal ;
++
++public abstract class AbstractTestTxn {
++    protected TransactionCoordinator txnMgr ;
++    protected TransInteger counter1 = new TransInteger(0) ; 
++    protected TransInteger counter2 = new TransInteger(0) ;
++    protected TransMonitor monitor  = new TransMonitor(ComponentId.allocLocal()) ;
++    protected Transactional unit ;
++    
++    @Before public void setup() {
++        Journal jrnl = Journal.create(Location.mem()) ;
++        List<TransactionalComponent> cg = Arrays.asList
++            (counter1, new TransactionalComponentWrapper(counter2), monitor) ;
++        txnMgr = new TransactionCoordinator(jrnl, cg) ;
++        unit = new TransactionalBase(txnMgr) ;
++        txnMgr.start() ;
++    }
++    
++    @After public void clearup() {
++        txnMgr.shutdown(); 
++    }
++    
++    protected void checkClear() {
++        assertEquals(0, txnMgr.countActive()) ;
++        assertEquals(0, txnMgr.countBegin()-txnMgr.countFinished()) ;
++        assertEquals(0, txnMgr.countActiveReaders()) ;
++        assertEquals(0, txnMgr.countActiveWriter()) ;
++        
++
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TS_Transactions.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TS_Transactions.java
index 0000000,0000000..d859939
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-transaction/src/test/java/org/seaborne/dboe/transaction/TS_Transactions.java
@@@ -1,0 -1,0 +1,43 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.transaction;
++
++import org.junit.runner.RunWith ;
++import org.junit.runners.Suite ;
++import org.junit.runners.Suite.SuiteClasses ;
++
++@RunWith(Suite.class)
++@SuiteClasses( {
++    TestTxnId.class
++    , TestJournal.class
++    , TestStateMgrData.class
++    , TestTransactionLifecycle.class
++    , TestTransactionLifecycle2.class
++    , TestTransactionCoordinator.class
++    , TestTransactionCoordinatorControl.class
++    , TestTxnLib.class
++    , TestTxnLib2.class
++    , TestThreadingTransactions.class
++    , TestTxnSwitching.class
++    
++    
++})
++public class TS_Transactions {
++
++}
++


[42/50] [abbrv] jena git commit: JENA-1396: Merge //github/afs/mantis as subdirectory jena-db/

Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPTreeRecordsMgr.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPTreeRecordsMgr.java
index 0000000,0000000..aae6b9f
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPTreeRecordsMgr.java
@@@ -1,0 -1,0 +1,115 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.bplustree;
++
++import org.seaborne.dboe.base.block.Block ;
++import org.seaborne.dboe.base.block.BlockType ;
++import org.seaborne.dboe.base.page.BlockConverter ;
++import org.seaborne.dboe.base.page.PageBlockMgr ;
++import org.seaborne.dboe.base.record.RecordFactory ;
++import org.seaborne.dboe.base.recordbuffer.RecordBufferPage ;
++import org.seaborne.dboe.base.recordbuffer.RecordBufferPageMgr ;
++import org.seaborne.dboe.base.recordbuffer.RecordBufferPageMgr.Block2RecordBufferPage ;
++
++/** Bridge for making, getting and putting BPTreeRecords over a RecordBufferPageMgr */
++final public class BPTreeRecordsMgr extends PageBlockMgr<BPTreeRecords>
++{
++    // Only "public" for external very low level tools in development to access this class.
++    // Assume package access.
++
++    private final RecordBufferPageMgr rBuffPageMgr ;
++    private final BPlusTree bpTree ;
++    
++    BPTreeRecordsMgr(BPlusTree bpTree, RecordFactory recordFactory, RecordBufferPageMgr rBuffPageMgr) {
++        super(null , rBuffPageMgr.getBlockMgr()) ;
++        this.bpTree = bpTree ;
++        super.setConverter(new Block2BPTreeRecords(this, recordFactory)) ;
++        // bpt is uninitialized at this point.
++        // so record rBuffPageMgr
++        this.rBuffPageMgr = rBuffPageMgr ;
++    }
++    
++    /** Converter BPTreeRecords -- make a RecordBufferPage and wraps it.*/ 
++    static class Block2BPTreeRecords implements BlockConverter<BPTreeRecords> {
++        private Block2RecordBufferPage recordBufferConverter ;
++        private BPTreeRecordsMgr       recordsMgr ;
++
++        Block2BPTreeRecords(BPTreeRecordsMgr mgr, RecordFactory recordFactory) {
++            this.recordsMgr = mgr ;
++            this.recordBufferConverter = new RecordBufferPageMgr.Block2RecordBufferPage(recordFactory) ;
++        }
++
++        @Override
++        public BPTreeRecords fromBlock(Block block) {
++            RecordBufferPage rbp = recordBufferConverter.fromBlock(block) ;
++            return new BPTreeRecords(recordsMgr, rbp) ;
++        }
++
++        @Override
++        public Block toBlock(BPTreeRecords t) {
++            return recordBufferConverter.toBlock(t.getRecordBufferPage()) ;
++        }
++
++        @Override
++        public BPTreeRecords createFromBlock(Block block, BlockType bType) {
++            RecordBufferPage rbp = recordBufferConverter.createFromBlock(block, bType) ;
++            return new BPTreeRecords(recordsMgr, rbp) ;
++        }
++    }
++    
++    public BPTreeRecords create() {
++        return super.create(BlockType.RECORD_BLOCK) ;
++//        
++//        RecordBufferPage rbp = rBuffPageMgr.create() ;
++//        BPTreeRecords bRec = new BPTreeRecords(bpTree, rbp) ;
++//        return bRec ;
++    }
++    
++    public RecordBufferPageMgr getRecordBufferPageMgr() { return rBuffPageMgr ; }
++    public BPlusTree getBPTree()                        { return bpTree ; }
++
++    boolean isWritable(int id) {
++        //System.err.println("BPTreeRecordsMgr.isWritable") ;
++        return false ;
++    }
++
++    @Override
++    public void startRead() {
++        rBuffPageMgr.startRead() ;
++    }
++
++    @Override
++    public void finishRead() {
++        rBuffPageMgr.finishRead() ;
++    }
++
++    @Override
++    public void startUpdate() {
++        rBuffPageMgr.startUpdate() ;
++    }
++
++    @Override
++    public void finishUpdate() {
++        rBuffPageMgr.finishUpdate() ;
++    }
++    
++    @Override
++    public void close() {
++        rBuffPageMgr.close() ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPlusTree.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPlusTree.java
index 0000000,0000000..395c651
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPlusTree.java
@@@ -1,0 -1,0 +1,544 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.bplustree;
++
++import java.nio.ByteBuffer ;
++import java.util.Iterator ;
++
++import org.apache.commons.lang3.NotImplementedException ;
++import org.apache.jena.atlas.io.IndentedWriter ;
++import org.apache.jena.atlas.iterator.Iter ;
++import org.apache.jena.atlas.lib.InternalErrorException ;
++import org.seaborne.dboe.base.record.Record ;
++import org.seaborne.dboe.base.record.RecordFactory ;
++import org.seaborne.dboe.base.record.RecordMapper ;
++import org.seaborne.dboe.index.RangeIndex ;
++import org.seaborne.dboe.transaction.txn.ComponentId ;
++import org.seaborne.dboe.transaction.txn.TransactionalComponentLifecycle ;
++import org.seaborne.dboe.transaction.txn.TxnId ;
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++import org.apache.jena.query.ReadWrite ;
++
++/** 
++ * 
++ * B-Tree taken from:
++ * <pre>
++ * Introduction to Algorithms, Second Edition
++ * Chapter 18: B-Trees
++ * by Thomas H. Cormen, Charles E. Leiserson, 
++ *    Ronald L. Rivest and Clifford Stein 
++ * </pre> 
++ * Includes implementation of removal
++ * then the B-Tree code converted to a B+Tree,
++ * then made MVCC/transactional.
++ * <p>
++ * Notes:
++ * <ul>
++ * <li>
++ * Stores "records", which are a key and value (the value may be absent).
++ * <li>
++ * In this B+Tree implementation, the (key,value) pairs are held in
++ * RecordBuffer, which wraps a ByteBuffer that only has records in it.  
++ * BPTreeRecords provides the B+Tree view of a RecordBuffer. All records
++ * are in RecordBuffer - the "tree" part is an index for finding the right
++ * page. The tree only holds keys, copies from the (key, value) pairs in
++ * the RecordBuffers. 
++ * <li>
++ * The version above splits nodes on the way down when full,
++ * not when needed where a split can bubble up from below.
++ * It means it only ever walks down the tree on insert.
++ * Similarly, the delete code ensures a node is suitable
++ * before decending. 
++ * </ul>
++ */
++
++public class BPlusTree extends TransactionalComponentLifecycle<BptTxnState> implements RangeIndex
++{
++    /*
++     * Insertion:
++     * There are two styles for handling node splitting.
++     * 
++     * Classically, when a leaf is split, the separating key is inserted into
++     * the parent, which may itself be full and so that is split, etc propagting
++     * up to the root (splitting the root is the only time the depth of the
++     * BTree increases). This involves walking back up the tree.
++     * 
++     * It is more convenient to have a spare slot in a tree node, so that the
++     * new key can be inserted, then the keys and child pointers split.
++     * 
++     * Modification: during insertion, splitting is applied to any full node
++     * traversed on the way down, resulting in any node passed through having
++     * some space for a new key. When splitting starts at a leaf, only the
++     * immediate parent is changed because it must have space for the new key.
++     * There is no cascade back to the top of the tree (it would have happened on
++     * the way down); in other words, splitting is done early. This is insertion
++     * in a single downward pass.
++     * 
++     * When compared to the classic approach including the extra slot for
++     * convenient inserting, the space useage is approximately the same.
++     * 
++     * Deletion:    
++     * Deletion always occurs at a leaf; if it's an internal node, swap the key
++     * with the right-most left key (predecessor) or left-most right key (successor),
++     * and delete in the leaf.
++     * 
++     * The classic way is to propagate node merging back up from the leaf.  The
++     * book outlines a way that checks that a nod eis delte-suitable (min+1 in size)
++     * on the way down.  This is implemented here; this is one-pass(ish).  
++     * 
++     * Variants:
++     * http://en.wikipedia.org/wiki/Btree
++     * 
++     * B+Tree: Tree contains keys, and only the leaves have the values. Used for
++     * secondary indexes (external pointers) but also for general on-disk usage
++     * because more keys are packed into a level. Can chain the leaves for a
++     * sorted-order traversal.
++     * 
++     * B*Tree: Nodes are always 2/3 full. When a node is full, keys are shared adjacent
++     * nodes and if all they are all full do 2 nodes get split into 3 nodes.
++     * Implementation wise, it is more complicated; can cause more I/O.
++     * 
++     * B#Tree: A B+Tree where the operations try to swap nodes between immediate
++     * sibling nodes instead of immediately splitting (like delete, only on insert).
++     */ 
++    
++    private static Logger log = LoggerFactory.getLogger(BPlusTree.class) ;
++    
++    // Root id across transactions
++    // Changes as the tree evolves in write transactions. 
++    private int rootIdx = -199 ;
++    private BPTStateMgr stateManager ;
++    private BPTreeNodeMgr nodeManager ; 
++    private BPTreeRecordsMgr recordsMgr; 
++    private final BPlusTreeParams bpTreeParams ;
++    private Mode mode = Mode.TRANSACTIONAL ;
++    private BptTxnState nonTxnState = null ; 
++    
++    // Construction is a two stage process
++    //    1/ Create the object, uninitialized
++    //      (Setup data structures, without referring to any BPlusTree methods)
++    //    2/ initialize
++    /*package*/ BPlusTree(ComponentId componentId, BPlusTreeParams bpTreeParams) {
++        super(componentId) ;
++        this.rootIdx = -99 ;
++        this.bpTreeParams = bpTreeParams ;
++        this.nodeManager = null ;
++        this.recordsMgr = null ;
++    }
++
++    /*package*/ void init(BPTStateMgr stateManager, BPTreeNodeMgr  nodeManager, BPTreeRecordsMgr recordsMgr) {
++        // Second part of creating.
++        // Some of these point to the BPlusTree object so we create te BPlusTree as 
++        // absic structure then initialize fully here.
++        this.rootIdx = stateManager.getRoot() ;
++        this.stateManager = stateManager ;
++        this.nodeManager = nodeManager ;
++        this.recordsMgr = recordsMgr ;
++    }
++
++    private BPTreeNode getRootRead() {
++        if ( isTransactional() ) {
++            super.checkTxn() ;
++            int rootId = super.getDataState().getRoot() ;
++            return nodeManager.getRead(rootId, BPlusTreeParams.RootParent) ;
++        }
++        return nodeManager.getRead(rootIdx, BPlusTreeParams.RootParent) ;
++    }
++
++    private BPTreeNode getRootWrite() {
++        if ( isTransactional() ) {
++            super.checkWriteTxn() ;
++            int rootId = super.getDataState().getRoot() ;
++            return nodeManager.getRead(rootId, BPlusTreeParams.RootParent) ;
++        }
++        return nodeManager.getRead(rootIdx, BPlusTreeParams.RootParent) ;
++    }
++
++    private boolean isTransactional() {
++        return mode == Mode.TRANSACTIONAL || mode == Mode.TRANSACTIONAL_AUTOCOMMIT ;
++    }
++    
++    private void releaseRootRead(BPTreeNode rootNode) {
++        rootNode.release() ;
++    }
++
++    private void releaseRootWrite(BPTreeNode rootNode) {
++        rootNode.release() ;
++    }
++
++    private void setRoot(BPTreeNode node) {
++        throw new InternalErrorException("BPlusTree.setRoot") ;
++    }
++
++    public void newRoot(BPTreeNode newRoot) {
++        if ( isTransactional() )
++            getDataState().setRoot(newRoot.getId()) ;
++        else
++            rootIdx = newRoot.getId() ; 
++    }
++
++//    // Very, very dangerous operation. 
++//    public void $testForce$(int rootIdx) {
++//        this.rootIdx = rootIdx ;
++//    }
++
++    public int getRootId() {
++        if ( super.isActiveTxn() )
++            return super.getDataState().getRoot() ;
++        else
++            return rootIdx ;
++    }
++
++    BptTxnState state() {
++        if ( mode == Mode.TRANSACTIONAL ) {
++            if ( super.isActiveTxn() )
++                return super.getDataState() ;
++            return null ;
++        }
++        return nonTxnState ;
++    }  
++    
++    /** Get the parameters describing this B+Tree */
++    public BPlusTreeParams getParams()          { return bpTreeParams ; } 
++
++    /** Only use for careful manipulation of structures */
++    public BPTStateMgr getStateManager()          { return stateManager ; } 
++
++    /** Only use for careful manipulation of structures */
++    public BPTreeNodeMgr getNodeManager()       { return nodeManager ; }
++    
++    /** Only use for careful manipulation of structures */
++    public BPTreeRecordsMgr getRecordsMgr()     { return recordsMgr ; }
++    
++    @Override
++    public RecordFactory getRecordFactory() {
++        return bpTreeParams.recordFactory ;
++    }
++
++    @Override
++    public Record find(Record record) {
++        startReadBlkMgr() ;
++        BPTreeNode root = getRootRead() ;
++        Record v = BPTreeNode.search(root, record) ;
++        releaseRootRead(root) ;
++        finishReadBlkMgr() ;
++        return v ;
++    }
++
++    @Override
++    public boolean contains(Record record) {
++        Record r = find(record) ;
++        return r != null ;
++    }
++
++    @Override
++    public Record minKey() {
++        startReadBlkMgr() ;
++        BPTreeNode root = getRootRead() ;
++        Record r = BPTreeNode.minRecord(root) ;
++        releaseRootRead(root) ;
++        finishReadBlkMgr() ;
++        return r ;
++    }
++
++    @Override
++    public Record maxKey() {
++        startReadBlkMgr() ;
++        BPTreeNode root = getRootRead() ;
++        Record r = BPTreeNode.maxRecord(root) ;
++        releaseRootRead(root) ;
++        finishReadBlkMgr() ;
++        return r ;
++    }
++
++    @Override
++    public boolean insert(Record record) {
++        return insertAndReturnOld(record) == null ;
++    }
++
++    /** Add a record into the B+Tree */
++    public Record insertAndReturnOld(Record record) {
++        startUpdateBlkMgr() ;
++        BPTreeNode root = getRootWrite() ;
++        Record r = BPTreeNode.insert(root, record) ;
++        releaseRootWrite(root) ;
++        finishUpdateBlkMgr() ;
++        return r ;
++    }
++    
++    @Override
++    public boolean delete(Record record) {
++        return deleteAndReturnOld(record) != null ;
++    }
++
++    public Record deleteAndReturnOld(Record record) {
++        startUpdateBlkMgr() ;
++        BPTreeNode root = getRootWrite() ;
++        Record r = BPTreeNode.delete(root, record) ;
++        releaseRootWrite(root) ;
++        finishUpdateBlkMgr() ;
++        return r ;
++    }
++
++    private static Record noMin = null ; 
++    private static Record noMax = null ; 
++    
++    @Override
++    public Iterator<Record> iterator() {
++        return iterator(noMin, noMax) ; 
++    }
++
++    @Override
++    public Iterator<Record> iterator(Record fromRec, Record toRec) {
++        startReadBlkMgr() ;
++        BPTreeNode root = getRootRead() ;
++        releaseRootRead(root) ;
++        finishReadBlkMgr() ;
++        return BPTreeRangeIterator.create(root, fromRec, toRec) ;
++        //return iterator(fromRec, toRec, RecordFactory.mapperRecord) ;
++    }
++    
++    @Override
++    public <X> Iterator<X> iterator(Record minRec, Record maxRec, RecordMapper<X> mapper) {
++        throw new NotImplementedException("Mapping iterator") ;
++    }
++    
++    // Internal calls.
++    void startReadBlkMgr() {
++        nodeManager.startRead() ;
++        recordsMgr.startRead() ;
++    }
++
++    void finishReadBlkMgr() {
++        nodeManager.finishRead() ;
++        recordsMgr.finishRead() ;
++    }
++
++    private void startUpdateBlkMgr() {
++        nodeManager.startUpdate() ;
++        recordsMgr.startUpdate() ;
++    }
++
++    private void finishUpdateBlkMgr() {
++        nodeManager.finishUpdate() ;
++        recordsMgr.finishUpdate() ;
++    }
++
++    @Override
++    public boolean isEmpty() {
++        startReadBlkMgr() ;
++        BPTreeNode root = getRootRead() ;
++        boolean b = !root.hasAnyKeys() ;
++        releaseRootRead(root) ;
++        finishReadBlkMgr() ;
++        return b ;
++    }
++    
++    private static int SLICE = 10000 ;
++    @Override
++    public void clear() {
++        Record[] records = new Record[SLICE] ;
++        while(true) {
++            Iterator<Record> iter = iterator() ;
++            int i = 0 ; 
++            for ( i = 0 ; i < SLICE ; i++ ) {
++                if ( ! iter.hasNext() )
++                    break ;
++                Record r = iter.next() ;
++                records[i] = r ;
++            }
++            if ( i == 0 )
++                break ;
++            for ( int j = 0 ; j < i ; j++ ) {
++                delete(records[j]) ;
++                records[j] = null ;
++            }
++        }
++    }
++    
++    @Override
++    public void sync() {
++        if ( nodeManager.getBlockMgr() != null )
++            nodeManager.getBlockMgr().sync() ;
++        if ( recordsMgr.getBlockMgr() != null )
++            recordsMgr.getBlockMgr().sync() ;
++    }
++
++    @Override
++    public void close() {
++        nodeManager.close() ;
++        recordsMgr.close() ;
++        stateManager.close() ;
++    }
++    
++//    public void closeIterator(Iterator<Record> iter)
++//    {
++//    }
++
++    @Override
++    public long size() {
++        Iterator<Record> iter = iterator() ;
++        return Iter.count(iter) ;
++    }
++
++    @Override
++    public void check() {
++        BPTreeNode root = getRootRead() ;
++        try { root.checkNodeDeep() ; }
++        finally { releaseRootRead(root) ; }
++    }
++
++    public void dump() {
++        // Caution - nesting via startReadBlkMgr
++        startReadBlkMgr() ;
++        BPTreeNode root = getRootRead() ;
++        boolean b = BPT.Logging ;
++        BPT.Logging = false ;
++        try { root.dump() ; }
++        finally { 
++            releaseRootRead(root) ;
++            BPT.Logging = b ;
++            }
++        finishReadBlkMgr() ;
++    }
++
++    public void dump(IndentedWriter out) {
++        BPTreeNode root = getRootRead() ;
++        try { root.dump(out) ; }
++        finally { releaseRootRead(root) ; }
++    }
++
++    public void nonTransactional() {
++        setMode(Mode.MUTABLE) ;
++    }
++    
++    private void setMode(Mode newMode) { 
++        
++        mode = newMode ;
++        
++        switch(mode) {
++            case IMMUTABLE : 
++                nonTxnState = new BptTxnState(BPlusTreeParams.RootId, 
++                                              nodeManager.allocLimit(),
++                                              recordsMgr.allocLimit()) ;
++                break ;
++            case IMMUTABLE_ALL:
++                nonTxnState = new BptTxnState(BPlusTreeParams.RootId, 
++                                              Long.MAX_VALUE,
++                                              Long.MAX_VALUE) ;
++                break ;
++            case MUTABLE :
++                nonTxnState = new BptTxnState(BPlusTreeParams.RootId, 0, 0) ;
++                break ;
++            case MUTABLE_ROOT :
++                // Imperfect.
++                nonTxnState = new BptTxnState(BPlusTreeParams.RootId, 1, 1) ;
++                break ;
++            case TRANSACTIONAL :
++                nonTxnState= null ;
++                break ;
++            case TRANSACTIONAL_AUTOCOMMIT :
++                // TODO TRANSACTIONAL_AUTOCOMMIT
++                throw new NotImplementedException("TRANSACTIONAL_AUTOCOMMIT not implemented");
++        }
++    }
++    
++    @Override
++    public void startRecovery() {}
++
++    @Override
++    public void recover(ByteBuffer ref) {
++        stateManager.setState(ref) ;
++        rootIdx = stateManager.getRoot() ;
++        nodeManager.resetAlloc(stateManager.getNodeBlocksLimit()) ;
++        recordsMgr.resetAlloc(stateManager.getRecordsBlocksLimit()) ;
++    }
++
++    @Override
++    public void finishRecovery() {
++        stateManager.sync();
++    }
++    
++    @Override
++    public void cleanStart() { }
++
++    @Override
++    protected BptTxnState _begin(ReadWrite readWrite, TxnId txnId) {
++        return createState();
++    }
++
++    private BptTxnState createState() {
++        return new BptTxnState(rootIdx, 
++                               nodeManager.allocLimit(),
++                               recordsMgr.allocLimit()) ;
++    }
++    
++    /* The persistent transactional state of a B+Tree is new root and the
++     * allocation limits of both block managers.
++     */
++    
++    @Override
++    protected BptTxnState _promote(TxnId txnId, BptTxnState oldState) {
++        BptTxnState newState = createState();
++        return newState;
++    }
++
++    @Override
++    protected ByteBuffer _commitPrepare(TxnId txnId, BptTxnState state) {
++        nodeManager.getBlockMgr().sync();
++        recordsMgr.getBlockMgr().sync();
++        
++        long nodeLimit = nodeManager.allocLimit() ;
++        long recordsLimit = recordsMgr.allocLimit() ;
++        // But don't write it yet.
++        stateManager.setState(state.getRoot(), nodeLimit, recordsLimit); 
++        return stateManager.getState() ;
++    }
++
++    @Override
++    protected void _commit(TxnId txnId, BptTxnState state) {
++        if ( isWriteTxn() ) {
++            rootIdx = state.getRoot() ;
++            stateManager.sync();
++        }
++    }
++
++    @Override
++    protected void _commitEnd(TxnId txnId, BptTxnState state) {
++    }
++
++    @Override
++    protected void _abort(TxnId txnId, BptTxnState state) {
++        rootIdx = state.initialroot ;
++        // Truncate - logically in block manager space.
++        nodeManager.resetAlloc(state.boundaryBlocksNode) ;
++        recordsMgr.resetAlloc(state.boundaryBlocksRecord) ;
++        stateManager.setState(state.initialroot, state.boundaryBlocksNode, state.boundaryBlocksRecord); 
++        stateManager.sync();
++    }
++
++    @Override
++    protected void _complete(TxnId txnId, BptTxnState state) {
++        
++    }
++
++    @Override
++    protected void _shutdown() {}
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPlusTreeFactory.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPlusTreeFactory.java
index 0000000,0000000..27e3e05
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPlusTreeFactory.java
@@@ -1,0 -1,0 +1,332 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.bplustree;
++
++import org.seaborne.dboe.DBOpEnvException ;
++import org.seaborne.dboe.base.block.BlockMgr ;
++import org.seaborne.dboe.base.block.BlockMgrFactory ;
++import org.seaborne.dboe.base.block.BlockMgrLogger ;
++import org.seaborne.dboe.base.file.BufferChannel ;
++import org.seaborne.dboe.base.file.BufferChannelMem ;
++import org.seaborne.dboe.base.file.FileFactory ;
++import org.seaborne.dboe.base.file.FileSet ;
++import org.seaborne.dboe.base.record.RecordFactory ;
++import org.seaborne.dboe.base.recordbuffer.RecordBufferPage ;
++import org.seaborne.dboe.base.recordbuffer.RecordBufferPageMgr ;
++import org.seaborne.dboe.index.RangeIndex ;
++import org.seaborne.dboe.sys.Names ;
++import org.seaborne.dboe.sys.SystemIndex ;
++import org.seaborne.dboe.transaction.txn.ComponentId ;
++
++/** Make BPlusTrees - this code works in close association with the BPlusTree constructor */
++public class BPlusTreeFactory {
++    private BPlusTreeFactory() {}
++    
++    /** Create the java structures to correspond to
++     * the supplied block managers for the persistent storage.
++     * Initialize the persistent storage to the empty B+Tree if it does not exist.
++     * This is primitive operation that underpins creation of a sB+Tree.
++     */
++    public static BPlusTree create(ComponentId id, BPlusTreeParams params, BufferChannel chan, BlockMgr blkMgrNodes, BlockMgr blkMgrLeaves) {
++        if ( id == null )
++            id = ComponentId.allocLocal() ;
++        BPlusTree bpt = attach(id, params, false, chan, blkMgrNodes, blkMgrLeaves) ;
++        return bpt ;
++    }
++
++    /** Create the java structures to correspond to
++     * the supplied block managers for the persistent storage.
++     * Initialize the persistent storage to the empty B+Tree if it does not exist.
++     */
++    public static BPlusTree createNonTxn(BPlusTreeParams params, BufferChannel chan, BlockMgr blkMgrNodes, BlockMgr blkMgrLeaves) {
++        // Allocate a random ComponentId
++        BPlusTree bpt = create(null, params, chan, blkMgrNodes, blkMgrLeaves) ;
++        bpt.nonTransactional() ;
++        return bpt ;
++    }
++
++    /** Reset an existing B+Tree with different storage units.
++     *  For each, null means "use same as original" 
++     */
++    public static BPlusTree rebuild(BPlusTree bpt, BufferChannel chan, BlockMgr blkMgrNodes, BlockMgr blkMgrLeaves) {
++        if ( chan == null )
++            chan = bpt.getStateManager().getBufferChannel() ;
++        if ( blkMgrNodes == null )
++            blkMgrNodes = bpt.getNodeManager().getBlockMgr() ;
++        if ( blkMgrLeaves == null )
++            blkMgrLeaves = bpt.getNodeManager().getBlockMgr() ;
++        BPlusTree bpt2 = attach(bpt.getComponentId(), bpt.getParams(), true, chan, blkMgrNodes, blkMgrLeaves) ;
++        return bpt2 ;
++    }
++
++    public static RangeIndex makeBPlusTree(ComponentId cid, FileSet fs, int blkSize, 
++                                           int readCacheSize, int writeCacheSize,
++                                           int dftKeyLength, int dftValueLength) {
++        RecordFactory recordFactory = makeRecordFactory(dftKeyLength, dftValueLength) ;
++        int order = BPlusTreeParams.calcOrder(blkSize, recordFactory.recordLength()) ;
++        RangeIndex rIndex = createBPTree(cid, fs, order, blkSize, readCacheSize, writeCacheSize, recordFactory) ;
++        return rIndex ;
++    }
++
++    public static RecordFactory makeRecordFactory(int keyLen, int valueLen) {
++        return new RecordFactory(keyLen, valueLen) ;
++    }
++
++    /** Create a B+Tree using defaults */
++    public static BPlusTree createBPTree(ComponentId cid, FileSet fileset, RecordFactory factory)
++    {
++        int readCacheSize = SystemIndex.BlockReadCacheSize ;
++        int writeCacheSize = SystemIndex.BlockWriteCacheSize ;
++        int blockSize = SystemIndex.BlockSize ;
++        if ( fileset.isMem() )
++        {
++            readCacheSize = 0 ;
++            writeCacheSize = 0 ;
++            blockSize = SystemIndex.BlockSizeTest ;
++        }
++        
++        return createBPTreeByBlockSize(cid, fileset, blockSize, readCacheSize, writeCacheSize, factory) ; 
++    }
++
++    /** Create a B+Tree by BlockSize */
++    public static BPlusTree createBPTreeByBlockSize(ComponentId cid, FileSet fileset,
++                                                    int blockSize,
++                                                    int readCacheSize, int writeCacheSize,
++                                                    RecordFactory factory)
++    {
++        return createBPTree(cid, fileset, -1, blockSize, readCacheSize, writeCacheSize, factory) ; 
++    }
++
++    /** Create a B+Tree by Order */
++    public static BPlusTree createBPTreeByOrder(ComponentId cid, FileSet fileset,
++                                                int order,
++                                                int readCacheSize, int writeCacheSize,
++                                                RecordFactory factory)
++    {
++        return createBPTree(cid, fileset, order, -1, readCacheSize, writeCacheSize, factory) ; 
++    }
++
++    /** Create a B+Tree by Order */
++    public static BPlusTree createBPTreeByOrder(ComponentId cid, FileSet fileset,
++                                                int order,
++                                                RecordFactory factory)
++    {
++        return createBPTree(cid, fileset, order, -1, SystemIndex.BlockReadCacheSize, SystemIndex.BlockWriteCacheSize, factory) ; 
++    }
++
++    /** Knowing all the parameters, create a B+Tree */
++    public static BPlusTree createBPTree(ComponentId cid, FileSet fileset, int order, int blockSize,
++                                         int readCacheSize, int writeCacheSize,
++                                         RecordFactory factory)
++    {
++        // ---- Checking
++        if (blockSize < 0 && order < 0) throw new IllegalArgumentException("Neither blocksize nor order specified") ;
++        if (blockSize >= 0 && order < 0) order = BPlusTreeParams.calcOrder(blockSize, factory.recordLength()) ;
++        if (blockSize >= 0 && order >= 0)
++        {
++            int order2 = BPlusTreeParams.calcOrder(blockSize, factory.recordLength()) ;
++            if (order != order2) throw new IllegalArgumentException("Wrong order (" + order + "), calculated = "
++                                                                    + order2) ;
++        }
++    
++        // Iffy - does not allow for slop.
++        if (blockSize < 0 && order >= 0) {
++            // Only in-memory.
++            blockSize = BPlusTreeParams.calcBlockSize(order, factory) ;
++        }
++    
++        BPlusTreeParams params = new BPlusTreeParams(order, factory) ;
++        BufferChannel bptState = FileFactory.createBufferChannel(fileset, Names.extBptState) ;
++        BlockMgr blkMgrNodes = BlockMgrFactory.create(fileset, Names.extBptTree, blockSize, readCacheSize, writeCacheSize) ;
++        BlockMgr blkMgrRecords = BlockMgrFactory.create(fileset, Names.extBptRecords, blockSize, readCacheSize, writeCacheSize) ;
++        return BPlusTreeFactory.create(cid, params, bptState, blkMgrNodes, blkMgrRecords) ;
++    }
++
++    /**
++     * Create the in-memory structures to correspond to the supplied block
++     * managers for the persistent storage. Does not initalize the B+Tree - it
++     * assumes the block managers correspond to an existing B+Tree.
++     */
++    private static BPlusTree attach(ComponentId cid, BPlusTreeParams params,
++                                    boolean isReset,
++                                    BufferChannel rootData, BlockMgr blkMgrNodes, BlockMgr blkMgrRecords) {
++        // Creating and initializing the BPlusTree object is a two stage process.
++
++        // * Create the Java object so it can be in other structures
++        //   but it is not fully initialized yet.
++        // * Create datastructures being careful not to use the object
++        // * Ensure formatted
++        // * Initialize.
++        
++        BPlusTree bpt = new BPlusTree(cid, params) ; 
++        BPTStateMgr stateMgr = new BPTStateMgr(rootData) ;
++        blkMgrNodes.resetAlloc(stateMgr.getNodeBlocksLimit());
++        blkMgrRecords.resetAlloc(stateMgr.getRecordsBlocksLimit());
++        
++        BPTreeNodeMgr nodeManager = new BPTreeNodeMgr(bpt, blkMgrNodes) ;
++        
++        RecordBufferPageMgr recordPageMgr = new RecordBufferPageMgr(params.getRecordFactory(), blkMgrRecords) ;
++        BPTreeRecordsMgr recordsMgr = new BPTreeRecordsMgr(bpt, params.getRecordFactory(), recordPageMgr) ;
++        
++        createIfAbsent(isReset, stateMgr, nodeManager, recordsMgr) ;
++        
++        bpt.init(stateMgr, nodeManager, recordsMgr) ;
++        if ( BPT.CheckingNode ) {
++            nodeManager.startRead();
++            BPTreeNode root = nodeManager.getRead(bpt.getRootId(), BPlusTreeParams.RootParent) ;
++            root.checkNodeDeep() ;
++            nodeManager.release(root) ;
++            nodeManager.finishRead();
++        }
++        return bpt ;
++    }
++
++    /** (Testing mainly) Make an in-memory B+Tree, with copy-in, copy-out block managers */
++    public static BPlusTree makeMem(int order, int keyLength, int valueLength)
++    { return makeMem(null, order, keyLength, valueLength) ; }
++
++    /** (Testing mainly) Make an in-memory B+Tree, with copy-in, copy-out block managers */
++    public static BPlusTree makeMem(String name, int order, int keyLength, int valueLength)
++    { return makeMem(name, order, -1, keyLength, valueLength) ; }
++
++    /** (Testing mainly) Make an in-memory B+Tree, with copy-in, copy-out block managers */
++    public static BPlusTree makeMem(int order, int minDataRecords, int keyLength, int valueLength)
++    { return makeMem(null, order, minDataRecords, keyLength, valueLength) ; }
++
++    /** (Testing mainly) Make an in-memory B+Tree, with copy-in, copy-out block managers */
++    public static BPlusTree makeMem(String name, int order, int minDataRecords, int keyLength, int valueLength) {
++        if ( name == null )
++            name = "Mem" ;
++        BPlusTreeParams params = new BPlusTreeParams(order, keyLength, valueLength) ;
++    
++        int blkSize ;
++        if ( minDataRecords > 0 ) {
++            int maxDataRecords = 2 * minDataRecords ;
++            // int rSize = RecordBufferPage.HEADER+(maxRecords*params.getRecordLength()) ;
++            blkSize = RecordBufferPage.calcBlockSize(params.getRecordFactory(), maxDataRecords) ;
++        } else
++            blkSize = params.getCalcBlockSize() ;
++    
++        // By FileSet
++        BufferChannel chan = BufferChannelMem.create(name+"(root)") ; 
++        BlockMgr mgr1 = BlockMgrFactory.createMem(name + "(nodes)", params.getCalcBlockSize()) ;
++        BlockMgr mgr2 = BlockMgrFactory.createMem(name + "(records)", blkSize) ;
++        ComponentId cid = ComponentId.allocLocal() ;
++        BPlusTree bpTree = BPlusTreeFactory.create(cid, params, chan, mgr1, mgr2) ;
++        return bpTree ;
++    }
++
++    /** Debugging */
++    public static BPlusTree addTracking(BPlusTree bpTree) {
++        BufferChannel mgrRoot = null ;
++        BlockMgr mgr1 = bpTree.getNodeManager().getBlockMgr() ;
++        BlockMgr mgr2 = bpTree.getRecordsMgr().getBlockMgr() ;
++        mgr1 = BlockTracker.track(mgr1) ;
++        mgr2 = BlockTracker.track(mgr2) ;
++        return BPlusTreeFactory.rebuild(bpTree, mgrRoot, mgr1, mgr2) ;
++    }
++    
++    /** Debugging */
++    public static BPlusTree addLogging(BPlusTree bpTree) {
++        BufferChannel mgrRoot = null ;
++        BlockMgr mgr1 = bpTree.getNodeManager().getBlockMgr() ;
++        BlockMgr mgr2 = bpTree.getRecordsMgr().getBlockMgr() ;
++        mgr1 = new BlockMgrLogger(mgr1, false) ; 
++        mgr2 = new BlockMgrLogger(mgr2, false) ; 
++        return BPlusTreeFactory.rebuild(bpTree, mgrRoot, mgr1, mgr2) ;
++    }
++    
++    /** Create if does not exist */ 
++    private static int createIfAbsent(boolean isReset, BPTStateMgr stateMgr, BPTreeNodeMgr nodeManager, BPTreeRecordsMgr recordsMgr) {
++        
++        int rootId = stateMgr.getRoot() ; 
++        
++        if ( nodeManager.getBlockMgr().isEmpty() != recordsMgr.getBlockMgr().isEmpty() )
++            throw new BPTreeException(
++                "Node block manager empty = "+nodeManager.getBlockMgr().isEmpty()+
++                " // "+
++                "Records block manager empty = "+recordsMgr.getBlockMgr().isEmpty()) ;
++        
++        if ( ! nodeManager.getBlockMgr().isEmpty() ) {
++            return rootId ;
++        } 
++//        else {
++//            if ( isReset )
++//                throw new BPTreeException("Reset on uninitialized B+Tree") ;
++//        }
++
++        // Create/format
++
++        // Fresh BPlusTree root node.
++        int rootIdx = createEmptyBPT(stateMgr, nodeManager, recordsMgr) ;
++        if ( rootIdx != 0 )
++            throw new InternalError() ;
++
++        // Sync created blocks to disk - any caches are now clean.
++        stateMgr.sync();
++        nodeManager.getBlockMgr().sync() ;
++        recordsMgr.getBlockMgr().sync() ;
++        return rootIdx ;
++    }
++
++    /** Allocate root node space. The root is a Node with a Records block.
++     * @param stateMgr */ 
++    private static int createEmptyBPT(BPTStateMgr stateMgr, BPTreeNodeMgr nodeManager, BPTreeRecordsMgr recordsMgr) { 
++        // Create an empty records block.
++        
++        nodeManager.startUpdate();
++        recordsMgr.startUpdate();
++        // Empty tree.
++        stateMgr.setState(0, 0, 0); 
++        try {
++            BPTreeRecords recordsPage = recordsMgr.create() ;
++            if ( recordsPage.getId() != BPlusTreeParams.RootId )
++                throw new DBOpEnvException("Root blocks must be at position zero (got "+recordsPage.getId()+")") ;
++            // Empty data block.
++            recordsMgr.write(recordsPage) ;
++            recordsMgr.release(recordsPage) ;
++
++            // Not this - we haven't full initialized and the BPTreeRecords has null BPTreeRecordsMgr
++            //recordsPage.write();
++            //recordsPage.release() ;
++
++            BPTreeNode n = nodeManager.createNode(BPlusTreeParams.RootParent) ;
++            // n.ptrs is currently invalid.  count was 0 so thinks it has a pointer.
++            // Force to right layout.
++            n.ptrs.setSize(0) ;                 // No pointers
++            n.ptrs.add(recordsPage.getId()) ;   // Add the page below
++
++            //n.ptrs.set(0, page.getId()) ; // This is the same as the size is one.
++
++            n.setIsLeaf(true) ;
++            n.setCount(0) ;     // Count is count of records.
++            int rootId = n.getId()  ;
++            nodeManager.write(n) ;
++            nodeManager.release(n) ;
++            // This makes the next blocks alocated 1 and 1 - just like a transaction.
++            // Blocks 0/0 are always an empty tree unless the bulk loader built the tree.
++            stateMgr.setState(0, 1, 1) ;
++            return rootId ;
++        } finally {
++            recordsMgr.finishUpdate();
++            nodeManager.finishUpdate();
++        }
++        // stateMgr.setState(0, 1, 1);
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPlusTreeParams.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPlusTreeParams.java
index 0000000,0000000..e94322c
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPlusTreeParams.java
@@@ -1,0 -1,0 +1,235 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.bplustree;
++
++import static org.seaborne.dboe.sys.SystemIndex.SizeOfPointer ;
++import org.apache.jena.atlas.logging.Log ;
++import org.seaborne.dboe.DBOpEnvException ;
++import org.seaborne.dboe.base.file.MetaFile ;
++import org.seaborne.dboe.base.record.RecordFactory ;
++import org.seaborne.dboe.sys.Names ;
++
++/** Configuration for a B+Tree */ 
++final public class BPlusTreeParams
++{
++    // Metadata
++    //public static final String NS = BPlusTreeParams.class.getName() ;
++    public static final String NS                  = Names.keyNSBPlusTree ;
++    public static final String ParamOrder          = NS+".order" ;
++    public static final String ParamKeyLength      = NS+".keyLength" ;
++    public static final String ParamValueLength    = NS+".valueLength" ;
++    public static final String ParamBlockSize      = NS+".blockSize" ;
++
++    /* The gap is extra space in a node - some books have node size as 2*N 
++     * (often for the classic insertion algorithm where it's easier to implement
++     * by inserting then splitting).
++     */ 
++    private static final int Gap            = 0 ;  
++    
++    public static final int RootId          = 0 ;
++    public static final int RootParent      = -2 ;
++    public static final int NoParent        = -99 ;
++    // Used when getting a block and we don't need/want/know the parent.
++    public static final int UnsetParent     = -98 ;
++
++    // Per instance settings
++    
++    /** Order of the BTree */
++    final int order ;
++    
++    /** Record factory */
++    final RecordFactory recordFactory ;
++
++    /** Factory for key-only records */ 
++    final RecordFactory keyFactory ;
++    
++    // ---- Derived constants.
++
++    /** Maximum number of keys per non-leaf block */
++    final int MaxRec ; 
++
++    /** Maximum number of pointers per block per non-leaf block */
++    final int MaxPtr  ;
++
++    /** Minimum number of keys per non-leaf block */
++    final int MinRec  ;
++
++    /** Minimum number of pointers per block */
++    final int MinPtr  ;
++    
++    /** Index of the split point */
++    final int SplitIndex ;
++
++    /** High index of keys array */
++    final int HighRec ;
++    
++    /** High index of pointers array */
++    final int HighPtr  ;
++ 
++    /** Space in a block needed for extra information - the count
++     * The parent is not stored on-disk because a block is always created by fetching from it's parent. 
++     */
++    static int BlockHeaderSize = 4 ;
++    
++    @Override
++    public String toString() {
++        return String.format("Order=%d : Records [key=%d, value=%d] : records=[%d,%d] : pointers=[%d,%d] : split=%d",
++                             order,
++                             keyFactory.keyLength() ,
++                             recordFactory.valueLength() ,
++                             MinRec, MaxRec, 
++                             MinPtr, MaxPtr,
++                             SplitIndex
++                             ) ;
++    }
++
++    public static BPlusTreeParams readMeta(MetaFile mf) {
++        try {
++            int pOrder = mf.getPropertyAsInteger(ParamOrder) ;
++            int pKeyLen = mf.getPropertyAsInteger(ParamKeyLength) ;
++            int pRecLen = mf.getPropertyAsInteger(ParamValueLength) ;
++            return new BPlusTreeParams(pOrder, pKeyLen, pRecLen) ;
++        } catch (NumberFormatException ex)
++        {
++            Log.error(BPlusTreeParams.class, "Badly formed metadata for B+Tree") ;
++            throw new DBOpEnvException("Failed to read metadata") ;
++        }
++    }
++    
++    public void addToMetaData(MetaFile mf) {
++        mf.setProperty(ParamOrder, order) ;
++        mf.setProperty(ParamKeyLength, recordFactory.keyLength()) ;
++        mf.setProperty(ParamValueLength, recordFactory.valueLength()) ;
++        mf.flush() ;
++    }
++
++    public BPlusTreeParams(int order, int keyLen, int valLen) { 
++        this(order, new RecordFactory(keyLen, valLen)) ;
++    }
++    
++    public BPlusTreeParams(int order, RecordFactory factory) {
++        // BTrees of order one aren't strictly BTrees
++        // Order 1 => Min size = 0 and max size = 2*N-1 = 1.
++        // If there is a gap, then the code may be defensive enough
++        // and something will work.  The B+Trees may have empty nodes
++        // (i.e. no keys, single child).
++
++        if ( order < 2 )
++            throw new IllegalArgumentException("BPTree: illegal order (min 2): "+order);
++
++        this.order = order ;
++        recordFactory = factory ;
++        keyFactory = factory.keyFactory() ;
++
++        // Derived constants.
++        MaxRec  = 2*order-1 + Gap ;
++        MaxPtr  = 2*order + Gap ; 
++        MinRec = order-1 ;
++        MinPtr  = order ;
++        
++        SplitIndex = order-1+Gap;
++        
++        HighPtr  = MaxPtr - 1 ;
++        HighRec  = HighPtr-1 ;
++    }
++
++    public int getOrder() {
++        return order ;
++    }
++
++    public int getPtrLength() {
++        return SizeOfPointer ;
++    }
++
++    public int getRecordLength() {
++        return recordFactory.recordLength() ;
++    }
++
++    public RecordFactory getRecordFactory(){
++        return recordFactory ;
++    }
++
++    public int getKeyLength() {
++        return keyFactory.recordLength() ;
++    }
++
++    public RecordFactory getKeyFactory() {
++        return keyFactory ;
++    }
++
++    public int getCalcBlockSize() {
++        return calcBlockSize(order, recordFactory) ;
++    }
++
++    /**
++     * Return the best fit for the blocksize and the record length. Knows about
++     * block header space.
++     */
++    public static int calcOrder(int blockSize, RecordFactory factory) {
++        return calcOrder(blockSize, factory.recordLength()) ;
++    }
++
++    /**
++     * Return the best fit for the blocksize and the record length. Knows about
++     * block header space.
++     */
++    public static int calcOrder(int blockSize, int recordLength)    {
++        // Length = X*recordLength+(X+1)*PtrLength
++        // => X = (Length-PtrLength)/(recordLength+PtrLength)
++        // BTree order N
++        // MaxRec = 2N-1+Gap = X
++        // N = (X+1-Gap)/2
++        blockSize -= BlockHeaderSize ;
++        
++        int X = (blockSize-recordLength)/(recordLength+SizeOfPointer) ;
++        int order = (X+1-Gap)/2 ;
++        return order ;
++    }
++
++    /** return the size of a block */
++    public static int calcBlockSize(int bpTreeOrder, RecordFactory factory) {
++        BPlusTreeParams p = new BPlusTreeParams(bpTreeOrder, factory) ;
++        int x = p.getMaxRec() * factory.recordLength() + p.getMaxPtr() * SizeOfPointer ;
++        x += BlockHeaderSize ;
++        return x ;
++    }
++
++    public int getMaxRec() {
++        return MaxRec ;
++    }
++
++    public int getMaxPtr() {
++        return MaxPtr ;
++    }
++
++    public int getMinRec() {
++        return MinRec ;
++    }
++
++    public int getMinPtr() {
++        return MinPtr ;
++    }
++
++//    /** return the size of a block */
++//    public static int calcBlockSize(int bTreeOrder, int recordLength) {
++//        BTreeParams p = new BTreeParams(bTreeOrder, recordLength, 0) ;
++//        int x = p.getMaxRec()*recordLength + p.getMaxPtr()*PtrLength ;
++//        x += BlockHeaderSize ;
++//        return x ;
++//    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPlusTreeTools.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPlusTreeTools.java
index 0000000,0000000..f3998ef
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPlusTreeTools.java
@@@ -1,0 -1,0 +1,148 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.bplustree;
++
++import java.io.IOException ;
++import java.io.RandomAccessFile ;
++import java.nio.ByteBuffer ;
++import java.nio.channels.FileChannel ;
++
++import org.apache.jena.atlas.lib.ByteBufferLib ;
++import org.seaborne.dboe.base.buffer.RecordBuffer ;
++import org.seaborne.dboe.base.recordbuffer.RecordBufferPage ;
++import org.seaborne.dboe.base.recordbuffer.RecordBufferPageMgr ;
++
++/** Tools for working with B+Trees and datastructures associated with them, rather directly */
++class BPlusTreeTools {
++    /*public*/private static void binDump(String filename)
++    {
++        try(RandomAccessFile fh = new RandomAccessFile(filename, "r")) {
++            ByteBuffer bb = ByteBuffer.allocate(8*1024) ;
++            FileChannel ch = fh.getChannel() ;
++            int idx = 0 ;
++            while(true)
++            {
++                int x = ch.read(bb) ;
++                if ( x < 0 )
++                    break ;
++                ByteBufferLib.print(bb) ;
++                bb.clear() ;
++            }
++        } catch (IOException ex)
++        {
++            ex.printStackTrace();
++        }
++    }
++    
++//    /** Scan/dump a file of RecordBuffers */
++//    /*public*/private static void bpt_scan_record_buffer(String filename, boolean verbose)
++//    {
++//        BlockMgr blkMgr = BlockMgrFactory.createStdFileNoCache(filename, SystemIndex.BlockSize) ;
++//        bpt_scan_record_buffer(blkMgr, verbose) ;
++//        blkMgr.close();
++//    }
++//
++//    /*public*/private static void bpt_scan_record_buffer(BPlusTree bpt, boolean verbose)
++//    {
++//        bpt_scan_record_buffer(bpt.getRecordsMgr().getBlockMgr(), verbose) ;
++//    }
++//    
++//    /*public*/private static void bpt_scan_record_buffer(BlockMgr blkMgr, boolean verbose)
++//    {
++//        RecordFactory f = SystemIndex.indexRecordTripleFactory ; 
++//        RecordBufferPageMgr recordPageMgr = new RecordBufferPageMgr(f, blkMgr) ;
++//        bpt_scan_record_buffer(recordPageMgr,verbose) ;
++//    }
++
++
++    /*public*/private static void bpt_scan_record_buffer(RecordBufferPageMgr recordPageMgr, boolean verbose)
++    {
++        System.out.print("[Scan Records] start\n") ;
++        int idx = 0 ;
++        int n = 0 ;
++        int total = 0 ;
++        if ( verbose ) System.out.printf("recordPageMgr = %s\n", recordPageMgr) ;
++
++        // Blocks in scan order
++        try {
++            while ( idx >= 0 )
++            {
++                if ( verbose ) System.out.printf("idx = %d\n", idx) ;
++                RecordBufferPage page = recordPageMgr.getRead(idx) ;
++                if ( verbose ) System.out.printf("%04d :: id=%04d -> link=%04d [count=%d, max=%d]\n", n, page.getId(), page.getLink(), page.getCount(), page.getMaxSize()) ;
++                RecordBuffer rb = page.getRecordBuffer() ;
++                if ( verbose ) System.out.printf("     :: %d %d\n", rb.getSize(), rb.maxSize() ) ;
++                total += rb.size();
++                idx = page.getLink() ;
++                n++ ;
++                recordPageMgr.release(page) ;
++            }
++        } catch (Exception ex)
++        {
++            System.out.println("Exception: "+ex) ;
++        }
++        
++        System.out.printf("[Scan Records] Count = %d in %d blocks (avg: %.2f)\n", total, n, ((float)total)/n) ;
++    }
++    
++//    /*public*/private static void bpt_scan_nodes(String filename, boolean verbose)
++//    {
++//        RecordFactory f = FactoryGraphTDB.indexRecordTripleFactory ;
++//        BPlusTree.
++//        BlockMgr blkMgr = BlockMgrFactory.createStdFileNoCache(filename, SystemIndex.BlockSize) ;
++//        bpt_scan_nodes(bpt, blkMgr, verbose) ;
++//        blkMgr.close();
++//    }
++    
++    /*public*/private static void bpt_scan_nodes(BPlusTree bpt, boolean verbose)
++    {
++        System.out.print("[Scan Nodes] start\n") ;
++        BPTreeNodeMgr nodeMgr = new BPTreeNodeMgr(bpt, bpt.getNodeManager().getBlockMgr()) ;
++        
++        int idx = 0 ;
++        int n = 0 ;
++        int total = 0 ;
++        if ( verbose ) System.out.printf("BPTreeNodeMgr = %s\n", nodeMgr) ;
++
++        // Blocks in file order
++        try {
++            while ( idx >= 0 )
++            {
++                if ( verbose ) System.out.printf("idx = %d\n", idx) ;
++
++                BPTreeNode node = nodeMgr.getRead(idx, 0) ;
++                if ( node == null )
++                    break ;
++                System.out.println(node) ;
++
++                //            if ( verbose ) 
++                //                System.out.printf("%04d :: id=%04d -> link=%04d [count=%d, max=%d]\n", n, page.getId(), page.getLink(), page.getCount(), page.getMaxSize()) ;
++                n++ ;
++                idx ++ ;
++                nodeMgr.release(node) ;
++            }
++        } catch (Exception ex)
++        {
++            System.out.println("Exception: "+ex) ;
++            ex.printStackTrace() ;
++        }
++
++        System.out.printf("[Scan Nodes] Count = %d in %d blocks (avg: %.2f)\n", total, n, ((float)total)/n) ;
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BlockTracker.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BlockTracker.java
index 0000000,0000000..0271891
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BlockTracker.java
@@@ -1,0 -1,0 +1,426 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.bplustree ;
++
++import static org.seaborne.dboe.trans.bplustree.BlockTracker.Action.Alloc ;
++import static org.seaborne.dboe.trans.bplustree.BlockTracker.Action.BeginRead ;
++import static org.seaborne.dboe.trans.bplustree.BlockTracker.Action.BeginUpdate ;
++import static org.seaborne.dboe.trans.bplustree.BlockTracker.Action.EndRead ;
++import static org.seaborne.dboe.trans.bplustree.BlockTracker.Action.EndUpdate ;
++import static org.seaborne.dboe.trans.bplustree.BlockTracker.Action.Free ;
++import static org.seaborne.dboe.trans.bplustree.BlockTracker.Action.GetRead ;
++import static org.seaborne.dboe.trans.bplustree.BlockTracker.Action.GetWrite ;
++import static org.seaborne.dboe.trans.bplustree.BlockTracker.Action.Promote ;
++import static org.seaborne.dboe.trans.bplustree.BlockTracker.Action.Release ;
++import static org.seaborne.dboe.trans.bplustree.BlockTracker.Action.Write ;
++
++import java.util.ArrayList ;
++import java.util.List ;
++
++import org.apache.jena.atlas.lib.Pair ;
++import org.apache.jena.ext.com.google.common.collect.HashMultiset ;
++import org.apache.jena.ext.com.google.common.collect.Multiset ;
++import org.seaborne.dboe.DBOpEnvException ;
++import org.seaborne.dboe.base.block.Block ;
++import org.seaborne.dboe.base.block.BlockException ;
++import org.seaborne.dboe.base.block.BlockMgr ;
++import org.seaborne.dboe.base.block.BlockMgrTracker ;
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++/** Track the lifecycles of allocate-write, getRead-promote-write and getWrite-write.
++ *  Does not track read only or iterators blocks.
++ */
++
++// In MVCC, there are many read blocks that are forgotten.
++// Promotion does not release them (should it?).
++
++// In the B+Tree iteraor, pages are get()'ed but pages
++// don't expose a "get for iterator" and always use getRead.
++// But iterators don't release and don't always finish. 
++    
++ 
++public class BlockTracker implements BlockMgr {
++    public static Logger logger             = LoggerFactory.getLogger(BlockTracker.class) ;
++    
++    public static boolean collectHistory    = true ;
++
++    static enum Action {
++        Alloc, Promote, GetRead, GetWrite, Write, Release, Free, 
++        BeginIter, EndIter, IterRead, 
++        BeginRead, EndRead, 
++        BeginUpdate, EndUpdate
++    }
++    static final Long                        NoId              = (long)-9 ;
++
++    // ---- State for tracking
++    // Track and count block references and releases
++    // No - the page is dirty.
++    protected final Multiset<Long>           activeWriteBlocks = HashMultiset.create() ;
++    protected final Multiset<Long>           activeReadBlocks  = HashMultiset.create() ;
++    // Track the operations
++    protected final List<Pair<Action, Long>> actions           = new ArrayList<>() ;
++    // ---- State for tracking
++
++    protected final BlockMgr                 blockMgr ;
++
++    private void clearBlockTracking() {
++        activeReadBlocks.clear() ;
++        activeWriteBlocks.clear() ;
++        actions.clear() ;
++    }
++    
++    public void clearHistory() {
++        actions.clear() ;
++    }
++
++    public void clearAll() {
++        clearBlockTracking() ;
++    }
++    
++    private int          inRead     = 0 ;
++    private int          inUpdate   = 0 ;
++    private final Logger log ;
++    private final String label ;
++
++    public static BlockMgr track(BlockMgr blkMgr) {
++        return track(blkMgr.getLabel(), blkMgr) ;
++    }
++
++    private static BlockMgr track(String label, BlockMgr blkMgr) {
++        return new BlockTracker(label, blkMgr) ;
++    }
++
++    private BlockTracker(BlockMgr blockMgr) {
++        this(LoggerFactory.getLogger(BlockMgrTracker.class), blockMgr.getLabel(), blockMgr) ;
++    }
++
++    private BlockTracker(String label, BlockMgr blockMgr) {
++        this(logger, label, blockMgr) ;
++    }
++
++    private BlockTracker(Logger logger, String label, BlockMgr blockMgr) {
++        this.blockMgr = blockMgr ;
++        this.log = logger ;
++        this.label = blockMgr.getLabel() ;
++    }
++
++    private void add(Action action, Long id) {
++        if ( collectHistory )
++            actions.add(new Pair<>(action, id)) ;
++    }
++
++    @Override
++    public Block allocate(int blockSize) {
++        Block block ;
++        synchronized (this) {
++            checkUpdate(Alloc) ;
++            block = blockMgr.allocate(blockSize) ;
++            Long id = block.getId() ;
++            activeWriteBlocks.add(id) ;
++            add(Alloc, id) ;
++        }
++        return block ;
++    }
++
++    @Override
++    public Block getRead(long id) {
++        // What if this is a write block already?
++        synchronized (this) {
++            checkRead(GetRead) ;
++            Long x = id ;
++            add(GetRead, x) ;
++
++            if ( activeWriteBlocks.contains(x) )
++                activeWriteBlocks.add(x) ;
++            else
++                activeReadBlocks.add(x) ;
++        }
++        return blockMgr.getRead(id) ;
++    }
++
++    @Override
++    public Block getWrite(long id) {
++        synchronized (this) {
++            checkUpdate(GetWrite) ;
++            Long x = id ;
++            add(GetWrite, x) ;
++            activeWriteBlocks.add(x) ;
++        }
++        return blockMgr.getWrite(id) ;
++    }
++
++    @Override
++    public Block promote(Block block) {
++        synchronized (this) {
++            checkUpdate(Promote) ;
++            Long id = block.getId() ;
++            add(Promote, id) ;
++
++            if ( !activeWriteBlocks.contains(id) && !activeReadBlocks.contains(id) )
++                error(Promote, id + " is not an active block") ;
++
++            while ( activeReadBlocks.contains(id) )
++                activeReadBlocks.remove(id) ;
++
++            // Double promotion results in only one entry.
++            if ( !activeWriteBlocks.contains(id) )
++                activeWriteBlocks.add(id) ;
++        }
++        return blockMgr.promote(block) ;
++    }
++
++    @Override
++    public void release(Block block) {
++        synchronized (this) {
++            Long id = block.getId() ;
++            add(Release, id) ;
++
++            // Iterator blocks are not released.
++//            if ( !activeReadBlocks.contains(id) && !activeWriteBlocks.contains(id) )
++//                error(Release, id + " is not an active block") ;
++
++            // May have been promoted.
++            if ( activeWriteBlocks.contains(id) )
++                activeWriteBlocks.remove(id) ;
++            else
++                activeReadBlocks.remove(block.getId()) ;
++        }
++        blockMgr.release(block) ;
++    }
++
++    @Override
++    public void write(Block block) {
++        if ( logger.isInfoEnabled() && block.getId() == 2 ) 
++            debugPoint(); 
++        writeTracker(block) ;
++        blockMgr.write(block) ;
++    }
++
++    @Override
++    synchronized public void overwrite(Block block) {
++        writeTracker(block) ;
++        blockMgr.overwrite(block) ;
++    }
++
++    private void writeTracker(Block block) {
++        synchronized (this) {
++            checkUpdate(Write) ;
++            Long id = block.getId() ;
++            add(Write, id) ;
++            if ( !activeWriteBlocks.contains(id) )
++                error(Write, id + " is not an active write block") ;
++        }
++    }
++
++    @Override
++    public void free(Block block) {
++        synchronized (this) {
++            checkUpdate(Free) ;
++            Long id = block.getId() ;
++            add(Free, id) ;
++            if ( activeReadBlocks.contains(id) )
++                warn(Free, id + " is a read block") ;
++            
++            if ( !activeWriteBlocks.contains(id) )
++                error(Free, id + " is not a write block") ;
++            
++            activeWriteBlocks.remove(id) ;
++            if ( activeWriteBlocks.count(id) != 0 )
++                warn(Free, id + " has "+activeWriteBlocks.count(id)+" outstanding write registrations") ;
++        }
++        blockMgr.free(block) ;
++    }
++
++    @Override
++    public void sync() {
++        blockMgr.sync() ;
++    }
++
++    @Override
++    public void syncForce() {
++        blockMgr.syncForce() ;
++    }
++
++    @Override
++    public void close() {
++        blockMgr.close() ;
++    }
++
++    @Override
++    public boolean isEmpty() {
++        return blockMgr.isEmpty() ;
++    }
++    
++    @Override
++    public long allocLimit() {
++        return blockMgr.allocLimit() ;
++    }
++    
++    @Override
++    public void resetAlloc(long boundary) {
++        blockMgr.resetAlloc(boundary) ;
++    }
++    
++    @Override
++    public boolean valid(int id) {
++        return blockMgr.valid(id) ;
++    }
++
++    @Override
++    public boolean isClosed() {
++        return blockMgr.isClosed() ;
++    }
++
++    @Override
++    synchronized public void beginRead() {
++        synchronized (this) {
++            if ( inUpdate != 0 )
++                error(BeginRead, "beginRead when already in update") ;
++            inRead++ ;
++        }
++        blockMgr.beginRead() ;
++    }
++
++    @Override
++    synchronized public void endRead() {
++        synchronized (this) {
++            if ( inRead == 0 )
++                error(EndRead, "endRead but not in read") ;
++            if ( inUpdate != 0 )
++                error(EndRead, "endRead when in update") ;
++
++            checkEmpty("Outstanding write blocks at end of read operations!", activeWriteBlocks) ;
++
++            if ( inRead == 0 ) {
++                // Check at end of multiple reads or a write
++                checkEmpty("Outstanding read blocks at end of read operations", activeReadBlocks) ;
++                clearBlockTracking() ;
++            }
++            inRead-- ;
++        }
++        blockMgr.endRead() ;
++    }
++
++    @Override
++    public void beginUpdate() {
++        synchronized (this) {
++            if ( inRead > 0 )
++                error(BeginUpdate, "beginUpdate when already in read") ;
++            if ( inUpdate > 0 )
++                error(BeginUpdate, "beginUpdate when already in update") ;
++            inUpdate++ ;
++            clearBlockTracking();
++        }
++        blockMgr.beginUpdate() ;
++    }
++
++    @Override
++    public void endUpdate() {
++        synchronized (this) {
++            if ( inUpdate == 0 )
++                error(EndUpdate, "endUpdate but not in update") ;
++            if ( inRead > 0 )
++                error(EndUpdate, "endUpdate when in read") ;
++
++            checkEmpty("Outstanding read blocks at end of update operations", activeReadBlocks) ;
++
++            checkEmpty("Outstanding write blocks at end of update operations", activeWriteBlocks) ;
++
++            inUpdate-- ;
++            clearBlockTracking() ;
++        }
++        blockMgr.endUpdate() ;
++    }
++
++    private void checkUpdate(Action action) {
++        if ( inUpdate == 0 )
++            error(action, "called outside update") ;
++    }
++
++    private void checkRead(Action action) {
++        if ( inUpdate == 0 && inRead == 0 )
++            error(action, "Called outside update and read") ;
++    }
++
++    private void checkEmpty(String string, Multiset<Long> blocks) {
++        if ( !blocks.isEmpty() ) {
++            error(string) ;
++            for ( Long id : blocks )
++                warn("    Block: " + id) ;
++            if ( collectHistory ) 
++                history() ;
++            throw new DBOpEnvException() ;
++            // debugPoint() ;
++        }
++    }
++
++    private String msg(String string) {
++        if ( label == null )
++            return string ;
++        return label + ": " + string ;
++    }
++
++    private void info(String string) {
++        log.info(msg(string)) ;
++    }
++
++    private void warn(String string) {
++        log.warn(msg(string)) ;
++    }
++
++    private void warn(Action action, String string) {
++        warn(action + ": " + string) ;
++    }
++
++    private void error(String string) {
++        log.error(msg(string)) ;
++    }
++
++    private void error(Action action, String string) {
++        error(action + ": " + string) ;
++        history() ;
++        throw new BlockException(msg(action + ": " + string)) ;
++        // debugPoint() ;
++    }
++
++    // Do nothing - but use as a breakpoint point.
++    private void debugPoint() {
++        return ;
++    }
++
++    private void history() {
++        info("History") ;
++        for ( Pair<Action, Long> p : actions ) {
++            if ( p.getRight() != NoId )
++                log.info(String.format("%s:     %-12s  %d", label, p.getLeft(), p.getRight())) ;
++            else
++                log.info(String.format("%s:     %-12s", label, p.getLeft())) ;
++        }
++    }
++
++    @Override
++    public String toString() {
++        return "BlockMgrTracker" + ((label == null) ? "" : (": " + label)) ;
++    }
++
++    @Override
++    public String getLabel() {
++        return label ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BptTxnState.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BptTxnState.java
index 0000000,0000000..a0e45bb
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BptTxnState.java
@@@ -1,0 -1,0 +1,63 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.bplustree;
++
++/** Transactional state of a {@link BPlusTree} */
++final class BptTxnState {
++    final int initialroot ;
++    private int root = -1 ;
++    
++    // Point at which we start adding blocks in a write transaction.
++    // The unmodifiable area is [0,boundary) and is different for each block manager. 
++    
++    final long boundaryBlocksNode ;
++    final long boundaryBlocksRecord ;
++    
++    BptTxnState(int initRoot, long boundaryNode, long boundaryRecords) {
++        this.initialroot = initRoot ;
++        this.root = initRoot ;
++        this.boundaryBlocksNode = boundaryNode ;
++        this.boundaryBlocksRecord = boundaryRecords ;
++    }
++    
++    boolean modifiableNodeBlock(int id) {
++        if ( BPT.forcePromoteModes )
++            return ! BPT.promoteDuplicateNodes ;
++        return id >= boundaryBlocksNode ;
++    }
++
++    boolean modifiableRecordsBlock(int id) {
++        if ( BPT.forcePromoteModes )
++            return ! BPT.promoteDuplicateRecords ;
++        return id >= boundaryBlocksRecord ;
++    }
++    
++    @Override
++    public String toString() {
++        return "BptTxnState: [root="+root+", n="+boundaryBlocksNode+", r="+boundaryBlocksRecord+"]";
++    }
++
++    public int getRoot() {
++        return root;
++    }
++
++    public void setRoot(int root) {
++        this.root = root;
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/Mode.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/Mode.java
index 0000000,0000000..c9090c7
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/Mode.java
@@@ -1,0 -1,0 +1,53 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.bplustree;
++
++/** Tree mode - changing the mode on an existing tree is not supported.
++ * The normal mode of operation is {@link Mode#TRANSACTIONAL}  
++ */
++public enum Mode {
++    /** 
++     * B+Tree changes are applied in place. MRSW applies.
++     */
++    MUTABLE,
++    /**
++     * All operations create new replicated blocks; a replicated block
++     * within the operation is not replicated.
++     */
++    IMMUTABLE,
++    /**
++     * All changes create new replicated blocks; replicated blocks
++     * are re-replicated.  (testing)
++     */
++    IMMUTABLE_ALL,
++    /**
++     * As above except the root alone is mutated, hence it is a fixed, known
++     * id. (testing)
++     */
++    MUTABLE_ROOT,
++    /**
++     * Transactional lifecycle, where blocks below the water marks are
++     * immutable.
++     */
++    TRANSACTIONAL,
++    /**
++     * Transactional lifecycle, with automatic transactions for update
++     * operations outside an explicit transaction.
++     */
++    TRANSACTIONAL_AUTOCOMMIT
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/rewriter/BPTreeNodeBuilder.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/rewriter/BPTreeNodeBuilder.java
index 0000000,0000000..2fe5fa7
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/rewriter/BPTreeNodeBuilder.java
@@@ -1,0 -1,0 +1,180 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.bplustree.rewriter ;
++
++import java.util.Iterator ;
++import java.util.NoSuchElementException ;
++
++import org.apache.jena.atlas.lib.Pair ;
++import org.seaborne.dboe.base.buffer.PtrBuffer ;
++import org.seaborne.dboe.base.buffer.RecordBuffer ;
++import org.seaborne.dboe.base.record.Record ;
++import org.seaborne.dboe.base.record.RecordFactory ;
++import org.seaborne.dboe.trans.bplustree.BPTreeNode ;
++import org.seaborne.dboe.trans.bplustree.BPTreeNodeMgr ;
++
++/** Take a stream of (block id,split record) pairs and generate B+Tree Nodes */
++class BPTreeNodeBuilder implements Iterator<Pair<Integer, Record>> {
++    private Pair<Integer, Record>           slot ;
++    private Iterator<Pair<Integer, Record>> iter ;
++    private final BPTreeNodeMgr             mgr ;
++
++    private final boolean                   leafLayer ;
++    private final RecordFactory             recordFactory ;
++
++    BPTreeNodeBuilder(Iterator<Pair<Integer, Record>> iter, BPTreeNodeMgr mgr, boolean leafLayer, RecordFactory recordFactory) {
++        this.iter = iter ;
++        this.mgr = mgr ;
++        this.leafLayer = leafLayer ;
++        this.recordFactory = recordFactory ;
++    }
++
++    @Override
++    public boolean hasNext() {
++        if ( slot != null )
++            return true ;
++
++        if ( iter == null )
++            return false ;
++
++        if ( !iter.hasNext() ) {
++            // Start of next block, no more input.
++            iter = null ;
++            return false ;
++        }
++
++        // At least one element to put in a new node.
++        // Unknown parent. Does not matter (parent is only in-memory)
++
++        BPTreeNode bptNode = mgr.createNode(-1) ;
++        bptNode.setIsLeaf(leafLayer) ;
++
++        RecordBuffer recBuff = bptNode.getRecordBuffer() ;
++        PtrBuffer ptrBuff = bptNode.getPtrBuffer() ;
++        recBuff.setSize(0) ;
++        ptrBuff.setSize(0) ; // Creation leaves this junk.
++
++        final boolean debug = false ;
++        int rMax = recBuff.maxSize() ;
++        int pMax = ptrBuff.maxSize() ;
++        if ( debug )
++            System.out.printf("Max: (%d, %d)\n", rMax, pMax) ;
++
++        for ( ; iter.hasNext() ; ) {
++
++            int X = bptNode.getCount() ;
++            int X2 = bptNode.getMaxSize() ;
++            int P = ptrBuff.size() ;
++            int P2 = ptrBuff.maxSize() ;
++            int R = recBuff.size() ;
++            int R2 = recBuff.maxSize() ;
++
++            // bptNode.getMaxSize() is drivel
++            // System.out.printf("N: %d/%d : P %d/%d : R %d/%d\n", X, X2, P, P2,
++            // R, R2) ;
++
++            Pair<Integer, Record> pair = iter.next() ;
++            if ( debug )
++                System.out.println("** Item: " + pair) ;
++            Record r = pair.cdr() ;
++
++            // [Issue: FREC]
++            // The record buffer size is wrong.
++            // Writes the whole record, only need to write the key part.
++            // **** r = recordFactory.createKeyOnly(r) ;
++
++            // [Issue: FREC]
++            // The record is key-only (which is correct) but until FREC fixed,
++            // we need key,value
++            r = recordFactory.create(r.getKey()) ;
++            // -- End FREC
++
++            // Always add - so ptrBuff is one ahead when we finish.
++            // There is always one more ptr than record in a B+Tree node.
++            if ( ptrBuff.isFull() )
++                System.err.println("PtrBuffer is full") ;
++
++            // Add pointer.
++            ptrBuff.add(pair.car()) ;
++
++            // [Issue: FREC]
++            // Either test shoudl work but due to the missetting of record
++            // buffer size
++            // testing recBuff does not work.
++            // if ( recBuff.isFull() )
++            // .... test ptrBuff
++            if ( ptrBuff.isFull() ) {
++                // End of this block.
++
++                // Does not add to ptrBuff so the one extra slot is done.
++                // Instead, the high point goes to the next level up.
++
++                // Internal consistency check.
++                if ( !ptrBuff.isFull() )
++                    System.err.println("PtrBuffer is not full") ;
++
++                // The split point for the next level up.
++                slot = new Pair<>(bptNode.getId(), pair.cdr()) ;
++
++                if ( debug )
++                    System.out.printf("Write(1): %d\n", bptNode.getId()) ;
++                if ( debug )
++                    System.out.println(bptNode) ;
++                if ( debug )
++                    System.out.println("Slot = " + slot) ;
++                mgr.put(bptNode) ;
++                // No count increment needed.
++                return true ;
++            }
++
++            recBuff.add(r) ;
++            bptNode.setCount(bptNode.getCount() + 1) ;
++        }
++
++        // If we get here, the input stream ran out before we finished a
++        // complete block.
++        // Fix up block (remove the last record)
++        Record r = recBuff.getHigh() ;
++        recBuff.removeTop() ;
++        bptNode.setCount(bptNode.getCount() - 1) ;
++        slot = new Pair<>(bptNode.getId(), r) ;
++
++        if ( debug )
++            System.out.printf("Write(2): %d\n", bptNode.getId()) ;
++        if ( debug )
++            System.out.println(bptNode) ;
++        if ( debug )
++            System.out.println("Slot = " + slot) ;
++        mgr.put(bptNode) ;
++        return true ;
++    }
++
++    @Override
++    public Pair<Integer, Record> next() {
++        if ( !hasNext() )
++            throw new NoSuchElementException() ;
++        Pair<Integer, Record> x = slot ;
++        slot = null ;
++        return x ;
++    }
++
++    @Override
++    public void remove() {
++        throw new UnsupportedOperationException() ;
++    }
++}


[11/50] [abbrv] jena git commit: Compaction tests

Posted by an...@apache.org.
Compaction tests


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

Branch: refs/heads/jena-tdb2
Commit: eee1a6abb34ff8226495edfcd711dff18c02d498
Parents: d8fd29a
Author: Andy Seaborne <an...@seaborne.org>
Authored: Sun Sep 3 17:38:49 2017 +0100
Committer: Andy Seaborne <an...@seaborne.org>
Committed: Sun Sep 3 17:38:49 2017 +0100

----------------------------------------------------------------------
 .../org/seaborne/tdb2/sys/TestDatabaseOps.java  | 83 +++++++++++++++++---
 1 file changed, 70 insertions(+), 13 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/eee1a6ab/tdb2/src/test/java/org/seaborne/tdb2/sys/TestDatabaseOps.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/sys/TestDatabaseOps.java b/tdb2/src/test/java/org/seaborne/tdb2/sys/TestDatabaseOps.java
index 09b9804..fa90400 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/sys/TestDatabaseOps.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/sys/TestDatabaseOps.java
@@ -17,13 +17,15 @@
 
 package org.seaborne.tdb2.sys;
 
-import static org.junit.Assert.assertEquals ;
+import static org.junit.Assert.* ;
 import static org.junit.Assert.assertFalse ;
 import static org.junit.Assert.assertNotEquals ;
 import static org.junit.Assert.assertTrue ;
 
 import org.apache.commons.io.FileUtils ;
 import org.apache.jena.atlas.lib.FileOps ;
+import org.apache.jena.graph.Graph ;
+import org.apache.jena.graph.Triple ;
 import org.apache.jena.riot.RDFDataMgr ;
 import org.apache.jena.sparql.core.DatasetGraph ;
 import org.apache.jena.sparql.core.Quad ;
@@ -43,8 +45,11 @@ public class TestDatabaseOps
     static String DIRx = ConfigTest.getCleanDir() ;
     static Location DIR = Location.create(DIRx);
     
-    static Quad quad1 = SSE.parseQuad("(_ _:a <p> 1)") ;
-    static Quad quad2 = SSE.parseQuad("(_ <s> <p> 1)") ;
+    static Quad quad1 = SSE.parseQuad("(_ <s> <p> 1)") ;
+    static Quad quad2 = SSE.parseQuad("(_ _:a <p> 2)") ;
+    static Triple triple1 = quad1.asTriple();
+    static Triple triple2 = quad2.asTriple();
+    static Triple triple3 = SSE.parseTriple("(<s> <q> 3)") ;
     
     @Before
     public void before() {
@@ -58,15 +63,15 @@ public class TestDatabaseOps
         FileUtils.deleteQuietly(IOX.asFile(DIR));
     }
 
-    @Test public void compact_1() {
+    @Test public void compact_dsg_1() {
         DatasetGraph dsg = DatabaseMgr.connectDatasetGraph(DIR);
         DatasetGraphSwitchable dsgs = (DatasetGraphSwitchable)dsg;
         DatasetGraph dsg1 = dsgs.get();
         Location loc1 = ((DatasetGraphTDB)dsg1).getLocation();
         
         Txn.executeWrite(dsg, ()-> {
-            dsg.add(quad1) ;
             dsg.add(quad2) ;
+            dsg.add(quad1) ;
         }) ;
         DatabaseMgr.compact(dsg);
         
@@ -79,32 +84,84 @@ public class TestDatabaseOps
         assertNotEquals(loc1, loc2);
 
         Txn.executeRead(dsg, ()-> {
-            assertTrue(dsg.contains(quad1)) ;
             assertTrue(dsg.contains(quad2)) ;
+            assertTrue(dsg.contains(quad1)) ;
         }) ;
         
         // dsg1 was closed and expelled. We must carefully reopen its storage only.
+        DatasetGraph dsgOld = StoreConnection.connectCreate(loc1).getDatasetGraph();
+        
+        Txn.executeWrite(dsgOld, ()->dsgOld.delete(quad2));
+        Txn.executeRead(dsg,     ()->assertTrue(dsg.contains(quad2)) );
+        Txn.executeRead(dsg2,    ()->assertTrue(dsg2.contains(quad2)) ) ;
+    }
+
+    @Test public void compact_graph_2() {
+        // graphs across compaction.
+        DatasetGraph dsg = DatabaseMgr.connectDatasetGraph(DIR);
+        Graph g = dsg.getDefaultGraph();
+        
+        DatasetGraphSwitchable dsgs = (DatasetGraphSwitchable)dsg;
+        DatasetGraph dsg1 = dsgs.get();
+        Location loc1 = ((DatasetGraphTDB)dsg1).getLocation();
+        
+        Txn.executeWrite(dsg, ()-> {
+            dsg.add(quad2) ;
+            dsg.add(quad1) ;
+        }) ;
+        DatabaseMgr.compact(dsg);
+        Txn.executeRead(dsg, ()-> {
+            assertEquals(2, g.size());
+            assertTrue(g.contains(triple2));
+        }) ;
         
+        // Check is not attached to the old graph.
         DatasetGraph dsgOld = StoreConnection.connectCreate(loc1).getDatasetGraph();
+
+        Txn.executeWrite(dsgOld, ()->dsgOld.getDefaultGraph().delete(triple2));
+        Txn.executeRead(dsg,     ()->assertTrue(g.contains(triple2)) );
         
-        Txn.executeWrite(dsgOld, ()->dsgOld.delete(quad1));
-        Txn.executeRead(dsg,     ()->assertTrue(dsg.contains(quad1)) );
-        Txn.executeRead(dsg2,    ()->assertTrue(dsg2.contains(quad1)) ) ;
+        Txn.executeWrite(dsg,    ()->g.add(triple3));
+        Txn.executeRead(dsgOld,  ()->assertFalse(dsgOld.getDefaultGraph().contains(triple3)) );
     }
+    
+    @Test public void compact_prefixes_3() {
+        // prefixes axcross compaction.
+        DatasetGraph dsg = DatabaseMgr.connectDatasetGraph(DIR);
+        Graph g = dsg.getDefaultGraph();
+        Txn.executeWrite(dsg, ()-> g.getPrefixMapping().setNsPrefix("ex", "http://example/") );
+        
+        DatasetGraphSwitchable dsgs = (DatasetGraphSwitchable)dsg;
+        DatasetGraph dsg1 = dsgs.get();
+        Location loc1 = ((DatasetGraphTDB)dsg1).getLocation();
+        
+        DatabaseMgr.compact(dsg);
+
+        Txn.executeRead(dsg, ()-> {
+            assertEquals("ex", g.getPrefixMapping().getNsURIPrefix("http://example/"));
+            assertEquals("http://example/", g.getPrefixMapping().getNsPrefixURI("ex"));
+        }) ;
+        
+        // Check is not attached to the old graph.
+        DatasetGraph dsgOld = StoreConnection.connectCreate(loc1).getDatasetGraph();
 
-//    @Test public void compact_2() {
-//    }
+        Txn.executeWrite(dsgOld, ()->dsgOld.getDefaultGraph().getPrefixMapping().removeNsPrefix("ex"));
+        Txn.executeRead(dsg,     ()->assertEquals("http://example/", g.getPrefixMapping().getNsPrefixURI("ex")));
+        
+        Txn.executeWrite(dsg,    ()->g.getPrefixMapping().setNsPrefix("ex2", "http://exampl2/") );
+        Txn.executeRead(dsgOld,  ()->assertNull(dsgOld.getDefaultGraph().getPrefixMapping().getNsPrefixURI("ex")));
+    }
 
     @Test public void backup_1() {
         DatasetGraph dsg = DatabaseMgr.connectDatasetGraph(DIR);
         Txn.executeWrite(dsg, ()-> {
-            dsg.add(quad1) ;
             dsg.add(quad2) ;
+            dsg.add(quad1) ;
         }) ;
         String file1 = DatabaseMgr.backup(dsg);
         DatasetGraph dsg2 = RDFDataMgr.loadDatasetGraph(file1);
         Txn.executeRead(dsg, ()-> {
-            assertTrue(dsg.contains(quad2)) ;
+            assertTrue(dsg.contains(quad1)) ;
             assertEquals(2, dsg.getDefaultGraph().size());
             assertTrue(dsg2.getDefaultGraph().isIsomorphicWith(dsg.getDefaultGraph()));
         }) ;


[15/50] [abbrv] jena git commit: [maven-release-plugin] prepare release mantis-0.3.0

Posted by an...@apache.org.
[maven-release-plugin] prepare release mantis-0.3.0


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

Branch: refs/heads/jena-tdb2
Commit: a23065e34ca5b93d3975b03809bf78836dd1e796
Parents: ef68a8d
Author: Andy Seaborne <an...@seaborne.org>
Authored: Mon Sep 4 12:56:29 2017 +0100
Committer: Andy Seaborne <an...@seaborne.org>
Committed: Mon Sep 4 12:56:29 2017 +0100

----------------------------------------------------------------------
 dboe-base/pom.xml        |  2 +-
 dboe-index-test/pom.xml  |  4 ++--
 dboe-index/pom.xml       |  4 ++--
 dboe-trans-data/pom.xml  | 10 +++++-----
 dboe-transaction/pom.xml |  4 ++--
 fuseki-tdb2/pom.xml      |  4 ++--
 pom.xml                  |  7 +++----
 tdb2-cmds/pom.xml        |  4 ++--
 tdb2/pom.xml             |  4 ++--
 9 files changed, 21 insertions(+), 22 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/a23065e3/dboe-base/pom.xml
----------------------------------------------------------------------
diff --git a/dboe-base/pom.xml b/dboe-base/pom.xml
index 1843969..4179450 100644
--- a/dboe-base/pom.xml
+++ b/dboe-base/pom.xml
@@ -26,7 +26,7 @@
   <parent>
     <groupId>org.seaborne.mantis</groupId>
     <artifactId>mantis</artifactId>
-    <version>0.3.0-SNAPSHOT</version>
+    <version>0.3.0</version>
   </parent> 
 
   <dependencies>

http://git-wip-us.apache.org/repos/asf/jena/blob/a23065e3/dboe-index-test/pom.xml
----------------------------------------------------------------------
diff --git a/dboe-index-test/pom.xml b/dboe-index-test/pom.xml
index 658d621..48c1b63 100644
--- a/dboe-index-test/pom.xml
+++ b/dboe-index-test/pom.xml
@@ -26,7 +26,7 @@
   <parent>
     <groupId>org.seaborne.mantis</groupId>
     <artifactId>mantis</artifactId>
-    <version>0.3.0-SNAPSHOT</version>
+    <version>0.3.0</version>
   </parent> 
 
   <dependencies>
@@ -34,7 +34,7 @@
     <dependency>
       <groupId>org.seaborne.mantis</groupId>
       <artifactId>dboe-index</artifactId>
-      <version>0.3.0-SNAPSHOT</version>
+      <version>0.3.0</version>
     </dependency>
 
     <dependency>

http://git-wip-us.apache.org/repos/asf/jena/blob/a23065e3/dboe-index/pom.xml
----------------------------------------------------------------------
diff --git a/dboe-index/pom.xml b/dboe-index/pom.xml
index 2b21bbe..dcc92cb 100644
--- a/dboe-index/pom.xml
+++ b/dboe-index/pom.xml
@@ -26,7 +26,7 @@
   <parent>
     <groupId>org.seaborne.mantis</groupId>
     <artifactId>mantis</artifactId>
-    <version>0.3.0-SNAPSHOT</version>
+    <version>0.3.0</version>
   </parent> 
 
   <dependencies>
@@ -34,7 +34,7 @@
     <dependency>
       <groupId>org.seaborne.mantis</groupId>
       <artifactId>dboe-base</artifactId>
-      <version>0.3.0-SNAPSHOT</version>
+      <version>0.3.0</version>
     </dependency>
 
   </dependencies>

http://git-wip-us.apache.org/repos/asf/jena/blob/a23065e3/dboe-trans-data/pom.xml
----------------------------------------------------------------------
diff --git a/dboe-trans-data/pom.xml b/dboe-trans-data/pom.xml
index 08bd822..a579aa4 100644
--- a/dboe-trans-data/pom.xml
+++ b/dboe-trans-data/pom.xml
@@ -25,7 +25,7 @@
   <parent>
     <groupId>org.seaborne.mantis</groupId>
     <artifactId>mantis</artifactId>
-    <version>0.3.0-SNAPSHOT</version>
+    <version>0.3.0</version>
   </parent> 
 
   <dependencies>
@@ -33,26 +33,26 @@
     <dependency>
       <groupId>org.seaborne.mantis</groupId>
       <artifactId>dboe-transaction</artifactId>
-      <version>0.3.0-SNAPSHOT</version>
+      <version>0.3.0</version>
     </dependency>
 
     <dependency>
       <groupId>org.seaborne.mantis</groupId>
       <artifactId>dboe-index</artifactId>
-      <version>0.3.0-SNAPSHOT</version>
+      <version>0.3.0</version>
     </dependency>
 
     <dependency>
       <groupId>org.seaborne.mantis</groupId>
       <artifactId>dboe-index-test</artifactId>
-      <version>0.3.0-SNAPSHOT</version>
+      <version>0.3.0</version>
       <scope>test</scope>
     </dependency>
 
     <dependency>
       <groupId>org.seaborne.mantis</groupId>
       <artifactId>dboe-base</artifactId>
-      <version>0.3.0-SNAPSHOT</version>
+      <version>0.3.0</version>
       <classifier>tests</classifier>
       <scope>test</scope>
     </dependency>

http://git-wip-us.apache.org/repos/asf/jena/blob/a23065e3/dboe-transaction/pom.xml
----------------------------------------------------------------------
diff --git a/dboe-transaction/pom.xml b/dboe-transaction/pom.xml
index 76b7902..f45beb7 100644
--- a/dboe-transaction/pom.xml
+++ b/dboe-transaction/pom.xml
@@ -25,7 +25,7 @@
   <parent>
     <groupId>org.seaborne.mantis</groupId>
     <artifactId>mantis</artifactId>
-    <version>0.3.0-SNAPSHOT</version>
+    <version>0.3.0</version>
   </parent> 
 
   <dependencies>
@@ -33,7 +33,7 @@
     <dependency>
       <groupId>org.seaborne.mantis</groupId>
       <artifactId>dboe-base</artifactId>
-      <version>0.3.0-SNAPSHOT</version>
+      <version>0.3.0</version>
     </dependency>
 
   </dependencies>

http://git-wip-us.apache.org/repos/asf/jena/blob/a23065e3/fuseki-tdb2/pom.xml
----------------------------------------------------------------------
diff --git a/fuseki-tdb2/pom.xml b/fuseki-tdb2/pom.xml
index 248881c..6c3383c 100644
--- a/fuseki-tdb2/pom.xml
+++ b/fuseki-tdb2/pom.xml
@@ -25,7 +25,7 @@
   <parent>
     <groupId>org.seaborne.mantis</groupId>
     <artifactId>mantis</artifactId>
-    <version>0.3.0-SNAPSHOT</version>
+    <version>0.3.0</version>
   </parent> 
 
   <packaging>jar</packaging>
@@ -36,7 +36,7 @@
     <dependency>
       <groupId>org.seaborne.mantis</groupId>
       <artifactId>tdb2</artifactId>
-      <version>0.3.0-SNAPSHOT</version>
+      <version>0.3.0</version>
     </dependency>
 
     <dependency>

http://git-wip-us.apache.org/repos/asf/jena/blob/a23065e3/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index af22a7d..72b8aba 100644
--- a/pom.xml
+++ b/pom.xml
@@ -23,7 +23,7 @@
   <artifactId>mantis</artifactId>
   <packaging>pom</packaging>
   <url>https://github.com/afs/mantis</url>
-  <version>0.3.0-SNAPSHOT</version>
+  <version>0.3.0</version>
   <name>Mantis :: Database Operation Environment</name>
   <description>Mantis :: Database Operation Environment</description>
 
@@ -56,7 +56,7 @@
     <url>https://github.com/afs/mantis</url>
     <connection>scm:git:https://github.com/afs/mantis</connection>
     <developerConnection>scm:git:git@github.com:afs/mantis.git</developerConnection>
-    <tag>HEAD</tag>
+    <tag>mantis-0.3.0</tag>
   </scm>
   
   <properties>
@@ -354,8 +354,7 @@
                   i.e. is it additive?
                   <configuration>
               <gpgArguments>
-                <arg>- -digest-algo=SHA512</arg> <comment fix
-              </gpgArguments>
+                <arg>- -digest-algo=SHA512</arg> <comment fix </gpgArguments>
             </configuration>
             -->
             <executions>

http://git-wip-us.apache.org/repos/asf/jena/blob/a23065e3/tdb2-cmds/pom.xml
----------------------------------------------------------------------
diff --git a/tdb2-cmds/pom.xml b/tdb2-cmds/pom.xml
index 9cd77bf..9885c77 100644
--- a/tdb2-cmds/pom.xml
+++ b/tdb2-cmds/pom.xml
@@ -27,7 +27,7 @@
   <parent>
     <groupId>org.seaborne.mantis</groupId>
     <artifactId>mantis</artifactId>
-    <version>0.3.0-SNAPSHOT</version>
+    <version>0.3.0</version>
   </parent> 
 
   <dependencies>
@@ -35,7 +35,7 @@
     <dependency>
       <groupId>org.seaborne.mantis</groupId>
       <artifactId>tdb2</artifactId>
-      <version>0.3.0-SNAPSHOT</version>
+      <version>0.3.0</version>
     </dependency>
 
     <dependency>

http://git-wip-us.apache.org/repos/asf/jena/blob/a23065e3/tdb2/pom.xml
----------------------------------------------------------------------
diff --git a/tdb2/pom.xml b/tdb2/pom.xml
index 0dd7f62..886e2da 100644
--- a/tdb2/pom.xml
+++ b/tdb2/pom.xml
@@ -27,7 +27,7 @@
   <parent>
     <groupId>org.seaborne.mantis</groupId>
     <artifactId>mantis</artifactId>
-    <version>0.3.0-SNAPSHOT</version>
+    <version>0.3.0</version>
   </parent> 
 
   <dependencies>
@@ -43,7 +43,7 @@
     <dependency>
       <groupId>org.seaborne.mantis</groupId>
       <artifactId>dboe-trans-data</artifactId>
-      <version>0.3.0-SNAPSHOT</version>
+      <version>0.3.0</version>
     </dependency>
 
     <dependency>


[24/50] [abbrv] jena git commit: JENA-1396: Merge //github/afs/mantis as subdirectory jena-db/

Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Values/manifest.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Values/manifest.ttl
index 0000000,0000000..3396af5
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Values/manifest.ttl
@@@ -1,0 -1,0 +1,76 @@@
++#  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.
++
++@prefix rdf:    <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
++@prefix rdfs:	<http://www.w3.org/2000/01/rdf-schema#> .
++@prefix mf:     <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
++@prefix qt:     <http://www.w3.org/2001/sw/DataAccess/tests/test-query#> .
++
++<>  rdf:type mf:Manifest ;
++    rdfs:label "Value tests" ;
++    mf:entries
++    ( 
++      [  mf:name    "Value-00" ;
++         mf:action
++            [ qt:query  <value-00.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Value-01" ;
++         mf:action
++            [ qt:query  <value-01.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Value-02" ;
++         mf:action
++            [ qt:query  <value-02.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Value-03" ;
++         mf:action
++            [ qt:query  <value-03.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++
++      [  mf:name    "Value-04" ;
++         mf:action
++            [ qt:query  <value-04.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Value-05" ;
++         mf:action
++            [ qt:query  <value-05.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Value-06" ;
++         mf:action
++            [ qt:query  <value-06.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Value-07" ;
++         mf:action
++            [ qt:query  <value-07.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Value-08" ;
++         mf:action
++            [ qt:query  <value-08.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++      [  mf:name    "Value-09" ;
++         mf:action
++            [ qt:query  <value-09.rq> ;
++              qt:data   <data-1.ttl> ] ;
++      ]
++    ) .

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Values/value-00.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Values/value-00.rq
index 0000000,0000000..d1fe80b
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Values/value-00.rq
@@@ -1,0 -1,0 +1,1 @@@
++SELECT * { ?s ?p ?o }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Values/value-01.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Values/value-01.rq
index 0000000,0000000..664f67a
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Values/value-01.rq
@@@ -1,0 -1,0 +1,1 @@@
++SELECT * { ?s ?p 1 }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Values/value-02.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Values/value-02.rq
index 0000000,0000000..5dfe5ec
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Values/value-02.rq
@@@ -1,0 -1,0 +1,1 @@@
++SELECT * { ?s ?p 1.5 }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Values/value-03.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Values/value-03.rq
index 0000000,0000000..7134be8
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Values/value-03.rq
@@@ -1,0 -1,0 +1,1 @@@
++SELECT * { ?s ?p "1" }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Values/value-04.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Values/value-04.rq
index 0000000,0000000..030116d
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Values/value-04.rq
@@@ -1,0 -1,0 +1,1 @@@
++SELECT * { ?s ?p "1"@en }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Values/value-05.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Values/value-05.rq
index 0000000,0000000..fbd8d9b
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Values/value-05.rq
@@@ -1,0 -1,0 +1,5 @@@
++SELECT *
++{ 
++    ?s ?p ?o . 
++    FILTER(?o = 1)
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Values/value-06.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Values/value-06.rq
index 0000000,0000000..8f34124
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Values/value-06.rq
@@@ -1,0 -1,0 +1,6 @@@
++PREFIX  xsd:    <http://www.w3.org/2001/XMLSchema#>
++
++SELECT *
++{ 
++    ?s ?p "2008-04-29+01:00"^^xsd:date .
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Values/value-07.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Values/value-07.rq
index 0000000,0000000..cafd777
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Values/value-07.rq
@@@ -1,0 -1,0 +1,6 @@@
++PREFIX  xsd:    <http://www.w3.org/2001/XMLSchema#>
++
++SELECT *
++{ 
++    ?s ?p ?x . FILTER (?x = "2008-04-29+01:00"^^xsd:date) .
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Values/value-08.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Values/value-08.rq
index 0000000,0000000..e6fc284
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Values/value-08.rq
@@@ -1,0 -1,0 +1,6 @@@
++PREFIX  xsd:    <http://www.w3.org/2001/XMLSchema#>
++
++SELECT *
++{ 
++    ?s ?p "2008-04-29T09:13:15+01:00"^^xsd:dateTime .
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/Values/value-09.rq
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/Values/value-09.rq
index 0000000,0000000..02dcc20
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/Values/value-09.rq
@@@ -1,0 -1,0 +1,7 @@@
++PREFIX  xsd:    <http://www.w3.org/2001/XMLSchema#>
++
++SELECT *
++{ 
++    # Out of range - not an immediate literal
++    ?s ?p "-3000-04-29T09:13:15Z"^^xsd:dateTime
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/testing/manifest.ttl
----------------------------------------------------------------------
diff --cc jena-db/tdb2/testing/manifest.ttl
index 0000000,0000000..aeaf80e
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/testing/manifest.ttl
@@@ -1,0 -1,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.
++
++@prefix rdf:    <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
++@prefix rdfs:	<http://www.w3.org/2000/01/rdf-schema#> .
++@prefix mf:     <http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#> .
++@prefix qt:     <http://www.w3.org/2001/sw/DataAccess/tests/test-query#> .
++
++<>  rdf:type mf:Manifest ;
++    rdfs:label "TDB - Scripts" ;
++    mf:include (
++        <Basic/manifest.ttl>
++        <Pattern/manifest.ttl>
++        <Quads/manifest.ttl>
++        <Values/manifest.ttl>
++        <UnionGraph/manifest.ttl>
++    ) .
++
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/use-fuseki-tdb2.md
----------------------------------------------------------------------
diff --cc jena-db/use-fuseki-tdb2.md
index 0000000,0000000..e64d99f
new file mode 100644
--- /dev/null
+++ b/jena-db/use-fuseki-tdb2.md
@@@ -1,0 -1,0 +1,60 @@@
++There is a combined Fuseki server jar:
++
++Release builds are available from the central maven repository:
++
++http://central.maven.org/maven2/org/seaborne/mantis/fuseki-tdb2-server/
++
++In addition, you will need a copy of Apache Jena Fuseki to get the web pages.
++
++```
++## Set the environment variable FUSEKI_HOME to the location of a
++## Fuseki2 distribution to pick up the admin UI web pages.
++export FUSEKI_HOME=???? 
++export FUSEKI_BASE="$FUSEKI_HOME"
++# Alternatively, copy or link "webapp" in this directory
++# to the webapp of a distribution.
++
++# Run:
++java -jar target/fuseki-tdb2-server-X.Y.Z.jar --conf config.ttl 
++```
++
++where a sample `config.ttl` for a service `/data` storing the database
++in `DB2` is:
++
++```
++@prefix :        <#> .
++@prefix fuseki:  <http://jena.apache.org/fuseki#> .
++@prefix rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
++@prefix rdfs:    <http://www.w3.org/2000/01/rdf-schema#> .
++@prefix tdb2:    <http://jena.apache.org/2016/tdb#> .
++@prefix ja:      <http://jena.hpl.hp.com/2005/11/Assembler#> .
++
++[] rdf:type fuseki:Server ;
++   fuseki:services (
++     <#service_tdb2>
++   ) .
++
++<#service_tdb2> rdf:type fuseki:Service ;
++    rdfs:label                      "TDB2 Service (RW)" ;
++    fuseki:name                     "data" ;
++    fuseki:serviceQuery             "query" ;
++    fuseki:serviceQuery             "sparql" ;
++    fuseki:serviceUpdate            "update" ;
++    fuseki:serviceUpload            "upload" ;
++    fuseki:serviceReadWriteGraphStore      "data" ;
++    # A separate read-only graph store endpoint:
++    fuseki:serviceReadGraphStore       "get" ;
++    fuseki:dataset           <#tdb_dataset_readwrite> ;
++    .
++
++<#tdb_dataset_readwrite> rdf:type      tdb2:DatasetTDB2 ;
++    tdb2:location "DB2" ;
++    ## This works: tdb2:unionDefaultGraph true ;
++     .
++```
++
++The key difference is the declared `rdf:type` of the dataset.
++
++Note that the Fuseki UI does not provide a way to create TDB2 databases;
++a configuration file must be used. Once setup, upload, query and graph
++editting will be routed to the TDB2 database.

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/use-tdb2-cmds.md
----------------------------------------------------------------------
diff --cc jena-db/use-tdb2-cmds.md
index 0000000,0000000..b420c9d
new file mode 100644
--- /dev/null
+++ b/jena-db/use-tdb2-cmds.md
@@@ -1,0 -1,0 +1,21 @@@
++*TDB2 is not compatible with Apache Jena TDB (TDB1).*
++
++The TDB2 command line tools are provided as a single combined jar
++``tdb2-cmds-VERSION.jar` containing all necessary dependencies. Each
++command has help message; the arguments are the mostly the same as TDB1.
++
++* `tdb2.tdbbackup`
++* `tdb2.tdbdump`
++* `tdb2.tdbcompact` (not in v 0.3.0)
++* `tdb2.tdbloader`
++* `tdb2.tdbquery`
++* `tdb2.tdbstats`
++* `tdb2.tdbupdate`
++
++[Command line jar (release builds)](http://central.maven.org/maven2/org/seaborne/mantis/tdb2-cmds/
++
++Example usage:
++
++```
++java -cp JAR tdb2.tdbloader --loc <DB location> file1 file2 ...
++```

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/use-tdb2.md
----------------------------------------------------------------------
diff --cc jena-db/use-tdb2.md
index 0000000,0000000..9208366
new file mode 100644
--- /dev/null
+++ b/jena-db/use-tdb2.md
@@@ -1,0 -1,0 +1,27 @@@
++*TDB2 is not compatible with Apache Jena TDB (TDB1).*
++
++The maven artifact, a non-Apache build, is in maven central:
++```
++   <dependency>
++     <groupId>org.seaborne.mantis</groupId>
++     <artifactId>tdb2</artifactId>
++     <version>0.3.0</version>
++   </dependency>
++```
++
++Simple migration is to use `TDB2Factory` in placeTDFCatory to create
++datasets. `DatasetGraph` objects are now created via `DatabaseMgr`.
++
++Example code: **`TDB2Factory`**
++
++```
++    public static void main(String[] args) {
++         Dataset ds = TDB2Factory.createDatasetGraph() ;
++         Txn.execWrite(ds, ()->{
++              RDFDataMgr.read(ds, "SomeData.ttl");
++         }) ;
++          Txn.execRead(dsg, ()->{
++             RDFDataMgr.write(System.out, ds, Lang.TRIG) ;
++         }) ;
++    }
++```


[23/50] [abbrv] jena git commit: Remove unused, untested TransIndexMap.

Posted by an...@apache.org.
Remove unused, untested TransIndexMap.

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

Branch: refs/heads/jena-tdb2
Commit: 71a70fd76ebc35cda26258bad0459e97f9860b04
Parents: 73e2cce
Author: Andy Seaborne <an...@seaborne.org>
Authored: Fri Sep 15 10:57:30 2017 +0100
Committer: Andy Seaborne <an...@seaborne.org>
Committed: Fri Sep 15 10:57:30 2017 +0100

----------------------------------------------------------------------
 .../seaborne/dboe/trans/data/TransIndexMap.java | 142 -------------------
 1 file changed, 142 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/71a70fd7/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/data/TransIndexMap.java
----------------------------------------------------------------------
diff --git a/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/data/TransIndexMap.java b/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/data/TransIndexMap.java
deleted file mode 100644
index 337fd07..0000000
--- a/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/data/TransIndexMap.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- *  Licensed 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.
- *
- *  See the NOTICE file distributed with this work for additional
- *  information regarding copyright ownership.
- */
-
-package org.seaborne.dboe.trans.data;
-
-import java.util.Arrays ;
-import java.util.HashMap ;
-import java.util.Iterator ;
-import java.util.Map ;
-
-import org.apache.jena.atlas.lib.Bytes ;
-import org.seaborne.dboe.base.record.Record ;
-import org.seaborne.dboe.base.record.RecordFactory ;
-import org.seaborne.dboe.index.Index ;
-import org.seaborne.dboe.transaction.txn.TransactionalMRSW ;
-
-/** MRSW in-memory "transaction" */  
-public class TransIndexMap extends TransactionalMRSW implements Index
-{
-    private final Map<ByteArray, ByteArray> index = new HashMap<>() ;
-    private final RecordFactory recordFactory ;
-    
-    public TransIndexMap(RecordFactory recordFactory) {
-        super(null) ;
-        this.recordFactory = recordFactory ;
-    }
-
-    @Override
-    public Record find(Record record) {
-        ByteArray k = wrap(record.getKey()) ;
-        ByteArray v = index.get(k) ;
-        if ( v == null )
-            return null ;
-        return record(k, v) ;
-    }
-
-    @Override
-    public boolean contains(Record record) {
-        Record r = find(record) ;
-        if ( r == null )
-            return false ;
-        if ( !recordFactory.hasValue() )
-            return true ;
-        return Bytes.compare(record.getValue(), r.getValue()) == 0 ;
-    }
-
-    @Override
-    public boolean insert(Record record) {
-        Record r = find(record) ;
-        if ( r != null && r.equals(record) )
-            return false ;
-        index.put(wrap(record.getKey()), wrap(record.getValue())) ;
-        return true ;
-    }
-
-    @Override
-    public boolean delete(Record record) {
-        ByteArray x = index.remove(wrap(record.getKey())) ;
-        if ( x == null )
-            return false ;
-        return true ;
-    }
-
-    @Override
-    public Iterator<Record> iterator() {
-        return index.entrySet().stream().map(e -> record(e.getKey(), e.getValue())).iterator();
-    }
-
-    @Override
-    public RecordFactory getRecordFactory() {
-        return recordFactory ;
-    }
-
-    @Override
-    public boolean isEmpty() {
-        return index.isEmpty() ;
-    }
-
-    @Override
-    public void clear() {
-        index.clear() ;
-    }
-
-    @Override
-    public void check() {}
-
-    @Override
-    public long size() {
-        return index.size() ;
-    }
-
-    @Override
-    public void sync()
-    {}
-
-    @Override
-    public void close()
-    {}
-
-    private static ByteArray wrap(byte[] b) { return new ByteArray(b) ; }
-    private Record record(ByteArray k, ByteArray v) { return recordFactory.create(k.bytes, v.bytes) ; }
-    
-    private static class ByteArray
-    {
-        byte[] bytes ;
-        ByteArray(byte[] bytes) { this.bytes = bytes ; }
-        
-        @Override
-        public int hashCode()
-        {
-            final int prime = 31 ;
-            int result = 1 ;
-            result = prime * result + Arrays.hashCode(bytes) ;
-            return result ;
-        }
-        @Override
-        public boolean equals(Object obj)
-        {
-            if (this == obj) return true ;
-            if (obj == null) return false ;
-            if (getClass() != obj.getClass()) return false ;
-            ByteArray other = (ByteArray)obj ;
-            if (!Arrays.equals(bytes, other.bytes)) return false ;
-            return true ;
-        }
-    }
-
-}


[17/50] [abbrv] jena git commit: Tweak documentation

Posted by an...@apache.org.
Tweak documentation


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

Branch: refs/heads/jena-tdb2
Commit: df5908256c889b28b4ab18dfa0d9bba18c79490a
Parents: dcfa94e
Author: Andy Seaborne <an...@seaborne.org>
Authored: Mon Sep 4 16:36:35 2017 +0100
Committer: Andy Seaborne <an...@seaborne.org>
Committed: Mon Sep 4 16:36:35 2017 +0100

----------------------------------------------------------------------
 use-fuseki-tdb2.md |  9 ++++--
 use-tdb2-cmds.md   |  3 +-
 use-tdb2.md        | 73 +++++++++----------------------------------------
 3 files changed, 20 insertions(+), 65 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/df590825/use-fuseki-tdb2.md
----------------------------------------------------------------------
diff --git a/use-fuseki-tdb2.md b/use-fuseki-tdb2.md
index bafdc30..4c02612 100644
--- a/use-fuseki-tdb2.md
+++ b/use-fuseki-tdb2.md
@@ -2,11 +2,11 @@ There is a combined Fuseki server jar:
 
 Release builds are available from the central maven repository:
 
-https://central.maven.org/maven2/org/seaborne/mantis/fuseki-tdb2/)
+https://central.maven.org/maven2/org/seaborne/mantis/fuseki-tdb2/
 
 and development builds at:
 
-https://oss.sonatype.org/content/repositories/snapshots/org/seaborne/mantis/fuseki-tdb2/)
+https://oss.sonatype.org/content/repositories/snapshots/org/seaborne/mantis/fuseki-tdb2/
 
 
 In addition, you will need a copy of Apache Jena Fuseki to get the web pages.
@@ -15,6 +15,7 @@ In addition, you will need a copy of Apache Jena Fuseki to get the web pages.
 ## Set the environment variable FUSEKI_HOME to the location of a
 ## Fuseki2 distribution to pick up the admin UI web pages.
 export FUSEKI_HOME=???? 
+export FUSEKI_BASE="$FUSEKI_HOME"
 # Alternatively, copy or link "webapp" in this directory
 # to the webapp of a distribution.
 
@@ -59,4 +60,6 @@ in `DB2` is:
 
 The key difference is the declared `rdf:type` of the dataset.
 
-Note that the Fuseki UI does not provide a way to create TDB2 databases; a configuration file must be used. Once setup, upload, query and graph editting will be routed to the TDB2 database.
+Note that the Fuseki UI does not provide a way to create TDB2 databases;
+a configuration file must be used. Once setup, upload, query and graph
+editting will be routed to the TDB2 database.

http://git-wip-us.apache.org/repos/asf/jena/blob/df590825/use-tdb2-cmds.md
----------------------------------------------------------------------
diff --git a/use-tdb2-cmds.md b/use-tdb2-cmds.md
index f2846ce..b420c9d 100644
--- a/use-tdb2-cmds.md
+++ b/use-tdb2-cmds.md
@@ -6,6 +6,7 @@ command has help message; the arguments are the mostly the same as TDB1.
 
 * `tdb2.tdbbackup`
 * `tdb2.tdbdump`
+* `tdb2.tdbcompact` (not in v 0.3.0)
 * `tdb2.tdbloader`
 * `tdb2.tdbquery`
 * `tdb2.tdbstats`
@@ -13,8 +14,6 @@ command has help message; the arguments are the mostly the same as TDB1.
 
 [Command line jar (release builds)](http://central.maven.org/maven2/org/seaborne/mantis/tdb2-cmds/
 
-[Development builds (Sonatype snapshot repository)](https://oss.sonatype.org/content/repositories/snapshots/org/seaborne/mantis/tdb2-cmds/)
-
 Example usage:
 
 ```

http://git-wip-us.apache.org/repos/asf/jena/blob/df590825/use-tdb2.md
----------------------------------------------------------------------
diff --git a/use-tdb2.md b/use-tdb2.md
index 2101437..9208366 100644
--- a/use-tdb2.md
+++ b/use-tdb2.md
@@ -1,74 +1,27 @@
 *TDB2 is not compatible with Apache Jena TDB (TDB1).*
 
-
-TDB2 example:
-
-Set the repositories (see below) and use:
+The maven artifact, a non-Apache build, is in maven central:
 ```
    <dependency>
      <groupId>org.seaborne.mantis</groupId>
      <artifactId>tdb2</artifactId>
-     <version>X.Y.Z</version>
+     <version>0.3.0</version>
    </dependency>
 ```
 
+Simple migration is to use `TDB2Factory` in placeTDFCatory to create
+datasets. `DatasetGraph` objects are now created via `DatabaseMgr`.
 
 Example code: **`TDB2Factory`**
 
-For disk location 
-
-```
-     public static void main(String[] args) {
-        Quad q1 = SSE.parseQuad("(_ <s> <p> 1 )") ; 
-        # In-memory dataset.
-        DatasetGraph dsg = TDB2Factory.createDatasetGraph() ;
-        # Transactions are required.
-        Txn.execWrite(dsg, ()->{
-            dsg.add(q1) ;        
-        }) ;
-            
-        Txn.execRead(dsg, ()->{
-            RDFDataMgr.write(System.out, dsg, Lang.TRIG) ;
-        }) ;
-        System.out.println("-----------") ;
-        
-        Quad q2 = SSE.parseQuad("(_ <s> <p> 2 )") ;
-        TransactionalSystem txnSystem = ((DatasetGraphTDB)dsg).getTxnSystem() ;
-        dsg.begin(ReadWrite.READ);
-        Transaction txn = txnSystem.getThreadTransaction() ;
-        boolean b = txn.promote() ;
-        if ( ! b ) {
-            System.out.println("Did not promote");
-            throw new RuntimeException() ;
-        }
-        
-        dsg.add(q2) ;
-        dsg.commit() ;
-        dsg.end() ;
-        
-        Txn.execRead(dsg, ()->{
-            RDFDataMgr.write(System.out, dsg, Lang.TRIG) ;
-        }) ;
-    }        
-```
-
-Maven repositories setup:
-
-For development builds:
-
 ```
-  <repositories>
-    <!-- Sonatype snapshot repository -->
-    <repository>
-      <id>sonatype.public</id>
-      <name>Sonatype Snapshots Repository</name>
-      <url>https://oss.sonatype.org/content/repositories/snapshots/</url>
-      <releases>
-        <enabled>false</enabled>
-      </releases>
-      <snapshots>
-        <enabled>true</enabled>
-      </snapshots>
-    </repository>
-  </repositories>
+    public static void main(String[] args) {
+         Dataset ds = TDB2Factory.createDatasetGraph() ;
+         Txn.execWrite(ds, ()->{
+              RDFDataMgr.read(ds, "SomeData.ttl");
+         }) ;
+          Txn.execRead(dsg, ()->{
+             RDFDataMgr.write(System.out, ds, Lang.TRIG) ;
+         }) ;
+    }
 ```


[19/50] [abbrv] jena git commit: Fix central.maven link

Posted by an...@apache.org.
Fix central.maven link


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

Branch: refs/heads/jena-tdb2
Commit: 4d02f5750207fc82a801ed3ad4104cc5f98bab38
Parents: cf4b7a8
Author: Andy Seaborne <an...@seaborne.org>
Authored: Mon Sep 4 23:13:48 2017 +0100
Committer: Andy Seaborne <an...@seaborne.org>
Committed: Mon Sep 4 23:13:48 2017 +0100

----------------------------------------------------------------------
 use-fuseki-tdb2.md | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/4d02f575/use-fuseki-tdb2.md
----------------------------------------------------------------------
diff --git a/use-fuseki-tdb2.md b/use-fuseki-tdb2.md
index 4c02612..e64d99f 100644
--- a/use-fuseki-tdb2.md
+++ b/use-fuseki-tdb2.md
@@ -2,12 +2,7 @@ There is a combined Fuseki server jar:
 
 Release builds are available from the central maven repository:
 
-https://central.maven.org/maven2/org/seaborne/mantis/fuseki-tdb2/
-
-and development builds at:
-
-https://oss.sonatype.org/content/repositories/snapshots/org/seaborne/mantis/fuseki-tdb2/
-
+http://central.maven.org/maven2/org/seaborne/mantis/fuseki-tdb2-server/
 
 In addition, you will need a copy of Apache Jena Fuseki to get the web pages.
 


[43/50] [abbrv] jena git commit: JENA-1396: Merge //github/afs/mantis as subdirectory jena-db/

Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPTreeNode.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPTreeNode.java
index 0000000,0000000..978896c
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPTreeNode.java
@@@ -1,0 -1,0 +1,1507 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.bplustree;
++
++import static org.seaborne.dboe.base.record.Record.keyGT ;
++import static org.seaborne.dboe.base.record.Record.keyLT ;
++import static org.seaborne.dboe.base.record.Record.keyNE ;
++import static org.seaborne.dboe.trans.bplustree.BPT.* ;
++
++import java.util.ArrayList ;
++import java.util.Iterator ;
++import java.util.List ;
++
++import org.apache.jena.atlas.io.IndentedLineBuffer ;
++import org.apache.jena.atlas.io.IndentedWriter ;
++import org.seaborne.dboe.base.block.Block ;
++import org.seaborne.dboe.base.block.BlockMgr ;
++import org.seaborne.dboe.base.buffer.PtrBuffer ;
++import org.seaborne.dboe.base.buffer.RecordBuffer ;
++import org.seaborne.dboe.base.page.PageBlockMgr ;
++import org.seaborne.dboe.base.record.Record ;
++import org.seaborne.dboe.sys.SystemIndex ;
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++public final class BPTreeNode extends BPTreePage
++{
++    private static Logger log = LoggerFactory.getLogger(BPTreeNode.class) ;
++    @Override protected Logger getLogger() { return log ; }
++   
++    /*package*/ Block block ;
++    /** Page id.
++     *  Pages are addressed by ints (a page ref does in on-disk blocks)
++     *  although blocks are addressed in longs.
++     *  1k pages => 2Tbyte file limit.
++     */
++    /*package*/int id ;             // Or block.getId()            
++    
++    private int parent ;
++    private int count ;             // Number of records.  Number of pointers is +1
++    
++    // "Leaf" of the BPTree is the lowest level of ptr/key splits, not the data blocks.
++    // We need to know this to know which block manager the block pointers refer to.
++    private boolean isLeaf ;        
++
++    private RecordBuffer records ;
++    /*package*/ void setRecordBuffer(RecordBuffer r) { records = r ; }
++    
++    // Accessed by BPlusTreeFactory
++    /*package*/ PtrBuffer ptrs ;
++    /*package*/ void setPtrBuffer(PtrBuffer pb) { ptrs = pb ; }
++
++    /* B+Tree
++     * 
++     * Two block managers : 
++     *   one for Nodes (BPlusTreePages => BPlusTreeNode)
++     *   one for Leaves (RecordBufferPages)
++     * The split key is the held in the highest in the block  
++     * 
++     * A "leaf" node is a leaf of the B+Tree part, and points to 
++     * highest record in a RecordBuffer 
++     *
++     * Example for N = 2
++     *  2*N:      MaxRec = 4, MaxPtr = 5,
++     *  Max-1:    HighRec = 3, HighPtr = 4
++     *  N-1:      MinRec = 1, MinPtr = 2
++     *  
++     * which is why a tree of order <2 does not make sense. 
++     *  
++     *
++     * BPTreeNode:
++     * 
++     *      +------------------------+
++     *      |-| K0 | K1 | K2 | K3 |--|
++     *      +------------------------+
++     *      | P0 | P1 | P2 | P3 | P4 |
++     *      +------------------------+
++     *
++     *      +------------------------+
++     *      | | K0 | K1 | ** | ** |--|
++     *      +------------------------+
++     *      | P0 | P1 | P2 | ** | ** |
++     *      +------------------------+
++     *      
++     * BPTreeRecords -> RecordBuffer:
++     *      
++     *      +------------------------+
++     *      | K0 | K1 | K2 | ** | ** |
++     *      +------------------------+
++     *      
++     * The size of records blocks and size of tree nodes don't have to be the same.
++     * They use different page managers, and are in different files.  
++     *
++     * The minimal tree is one, leaf, root BPTreeNode and one BPTreeRecords page.
++     * 
++     * Pictures:      
++     *      /--\ \--\
++     * means a block with free space introduced between records[i] and records[i+1], ptrs[i+1]/ptrs[i+2]
++     * Lower half is a valid structure (except for overall size) 
++     *       
++     *      /--/ /--\
++     * means a block with free space introduced between records[i] and records[i+1], ptrs[i]/ptrs[i+1]
++     * Upper half is a valid structure (except for overall size) 
++     */
++
++    // Branch nodes only need create branch nodes (splitting sideways)
++    // Leaf nodes only create leaf nodes.
++    // The root is an exception.
++    
++    private BPTreeNode create(int parent, boolean isLeaf) {
++        return create(bpTree, parent, isLeaf) ;
++    }
++
++    private static BPTreeNode create(BPlusTree bpTree, int parent, boolean isLeaf) {
++        BPTreeNode n = bpTree.getNodeManager().createNode(parent) ;
++        n.isLeaf = isLeaf ;
++        return n ;
++    }
++
++    /*package*/ BPTreeNode(BPlusTree bpTree) {
++        super(bpTree) ;
++        // 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 ;
++        // reformat block (sets record and pointer buffers)
++        BPTreeNodeMgr.formatBPTreeNode(this, bpTree, block, isLeaf, parent, count) ;
++    }
++    
++    /** Get the page at slot idx - switch between B+Tree and records files */
++    /*package*/ BPTreePage get(int idx) {
++        if ( false && logging(log) ) {
++            String leafOrNode = isLeaf ? "L" : "N" ;
++            log(log, "%d[%s].get(%d)", id, leafOrNode, idx) ; 
++        }
++        int subId = ptrs.get(idx) ;
++        PageBlockMgr<? extends BPTreePage> pbm = getPageBlockMgr() ;
++        // Always get a "read" block - it must be promoted to a "write" block
++        // to update it.  Getting a write block is unhelpful as many blocks
++        // during a write operation are only read.
++        return pbm.getRead(subId, this.getId()) ;
++    }
++
++    private PageBlockMgr<? extends BPTreePage> getPageBlockMgr() {
++        if ( isLeaf )
++            return bpTree.getRecordsMgr() ;
++        else
++            return bpTree.getNodeManager() ;
++    }
++    
++    // ---------- Public calls.
++    // None of these are called recursively.
++
++    /** Find a record, using the active comparator */
++    public static Record search(BPTreeNode root, Record rec) {
++        root.internalCheckNodeDeep() ;
++        if ( ! root.isRoot() )
++            throw new BPTreeException("Search not starting from the root: " + root) ;
++        AccessPath path = new AccessPath(root) ;
++        Record r = root.internalSearch(path, rec) ;
++        return r ;
++    }
++
++    /** Insert a record - return existing value if any, else null */
++    public static Record insert(BPTreeNode root, Record record) {
++        if ( logging(log) ) {
++            log(log, "** insert(%s) / root=%d", record, root.getId()) ;
++            if ( DumpTree )
++                root.dump() ;
++        }
++
++        if ( !root.isRoot() )
++            throw new BPTreeException("Insert begins but this is not the root") ;
++        
++        if ( root.isFull() ) {
++            // Root full - root split is a special case.
++            splitRoot(root) ;
++            if ( DumpTree )
++                root.dump() ;
++        }
++
++        AccessPath path = new AccessPath(root) ;
++        // Root ready - call insert proper.
++        Record result = root.internalInsert(path, record) ;
++
++        root.internalCheckNodeDeep() ;
++
++        if ( logging(log) ) {
++            log(log, "** insert(%s) / finish", record) ;
++            if ( DumpTree )
++                root.dump() ;
++        }
++        return result ;
++    }
++
++    /** Delete a record - return the old value if there was one, else null */
++    public static Record delete(BPTreeNode root, Record rec) {
++        if ( logging(log) ) {
++            log(log, "** delete(%s) / start", rec) ;
++            if ( BPT.DumpTree )
++                root.dump() ;
++        }
++        if ( !root.isRoot() )
++            throw new BPTreeException("Delete begins but this is not the root") ;
++
++        AccessPath path = new AccessPath(root) ;
++        
++        if ( root.isLeaf && root.count == 0 ) {
++            // Special case. Just a records block. Allow that to go too small.
++            BPTreePage page = root.get(0) ;
++            if ( BPT.CheckingNode && !(page instanceof BPTreeRecords) )
++                BPT.error("Zero size leaf root but not pointing to a records block") ;
++            trackPath(path, root, 0, page) ;
++            Record r = page.internalDelete(path, rec) ;
++            page.release() ;
++            if ( r != null )
++                root.write() ;
++            if ( BPT.DumpTree )
++                root.dump() ;
++            return r ;
++        }
++
++        // Entry: checkNodeDeep() ;
++        Record v = root.internalDelete(path, rec) ;
++        // Fix the root in case it became empty in deletion process.
++        if ( !root.isLeaf && root.count == 0 ) {
++            reduceRoot(root) ;
++            root.bpTree.newRoot(root) ;
++            root.internalCheckNodeDeep() ;
++        }
++
++        if ( logging(log) ) {
++            log(log, "** delete(%s) / finish", rec) ;
++            if ( BPT.DumpTree )
++                root.dump() ;
++        }
++        return v ;
++    }
++
++    /** Iterator over the pages below that have records between minRec (inclusive) and maxRec(exclusive).
++     *  There may be other records as as well.
++     * @param minRec
++     * @param maxRec
++     * @return Iterator&lt;BPTreePage>
++     */
++    Iterator<BPTreePage> iterator(Record minRec, Record maxRec) {
++        if ( minRec != null && maxRec != null && Record.keyGE(minRec, maxRec) )
++            return null ;//throw new IllegalArgumentException("minRec >= maxRec: "+minRec+" >= "+maxRec ) ;
++        
++        int x1 = 0 ;
++        if ( minRec != null ) {
++            x1 = findSlot(minRec) ;
++            // If there is an exact match, we still need to go down that place to get the max.
++            // If there is no match, it returns -(i+1) and we need to go down the tree at i.
++            // Same effect - start at x1
++            // TODO Optimization - get max record on min side once and for all if exact match.
++            x1 = apply(x1) ;
++        }
++        
++        int x2 = this.getCount() ;    // Highest place to look. Inclusive.   
++        if ( maxRec != null ) {
++            // If there is an exact match, we need to go down that place to get the record upto max.
++            // If there is no match, it returns -(i+1) and we need to go down the tree at i to get from last key to max (exclusive).
++            // Same effect - start at x2 inclusive.
++            x2 = findSlot(maxRec) ;
++            x2 = apply(x2) ;
++        }
++        // Pages from pointer slots x1 to x2 (inc because while we exclude maxRec, 
++        // keys are only a max of the subtree they mark out.
++        
++        // XXX Just grab them now - later, keep indexes and fetch on next(). 
++        // XXX Epoch tracking 
++        
++        List<BPTreePage> x = new ArrayList<>(x2-x1+1) ;
++        for ( int i = x1 ; i <= x2 ; i++ )
++            x.add(get(i)) ;
++        
++        return x.iterator() ;
++    }
++    
++//    // OUT OF DATE WITH MVCC
++//    /**
++//     * Returns the id of the records buffer page for this record. Records Buffer
++//     * Page NOT read; record may not exist
++//     */
++//    static int recordsPageId(BPTreeNode node, Record fromRec) {
++//        // Used by BPlusTree.iterator
++//        // Walk down the B+tree part of the structure ...
++//        while (!node.isLeaf) {
++//            BPTreePage page = (fromRec == null) ? node.get(0) : node.findHere(null, fromRec) ;
++//            // Not a leaf so we can cast safely.
++//            BPTreeNode n = (BPTreeNode)page ;
++//            // Release if not root.
++//            if ( !node.isRoot() )
++//                node.release() ;
++//            node = n ;
++//        }
++//        // ... then find the id of the next step down, 
++//        // but do not touch the records buffer page.
++//        int id ;
++//        if ( fromRec == null ) {
++//            // Just get the lowest starting place.
++//            id = node.getPtrBuffer().getLow() ;
++//        } else {
++//            // Get the right id based on starting record.
++//            int idx = node.findSlot(fromRec) ;
++//            idx = apply(idx) ;
++//            id = node.getPtrBuffer().get(idx) ;
++//        }
++//        if ( !node.isRoot() )
++//            node.release() ;
++//        return id ;
++//    }
++
++    final static Record minRecord(BPTreeNode root) {
++        AccessPath path = new AccessPath(root) ; 
++        return root.internalMinRecord(path) ;
++    }
++
++    final static Record maxRecord(BPTreeNode root) {
++        AccessPath path = new AccessPath(root) ; 
++        return root.internalMaxRecord(path) ;
++    }
++    
++    @Override
++    protected Record internalMaxRecord(AccessPath path) {
++        BPTreePage page = get(count) ;
++        trackPath(path, this, count, page) ;
++        Record r = page.internalMaxRecord(path) ;
++        page.release() ;
++        return r ;
++    }
++
++    @Override
++    protected Record internalMinRecord(AccessPath path) {
++        BPTreePage page = get(0) ;
++        trackPath(path, this, 0, page) ;
++        Record r = page.internalMinRecord(path) ;
++        page.release() ;
++        return r ;
++    }
++
++    @Override
++    final Record getLowRecord() {
++        return records.getLow() ;
++    }
++
++    @Override
++    final Record getHighRecord() {
++        return records.getHigh() ;
++    }
++
++    // count is the number of pointers.
++    
++    @Override
++    public final int getMaxSize()          { return bpTree.getParams().getOrder() ; }
++    
++    @Override
++    public final int getCount()            { return count ; }
++ 
++    @Override
++    public final void setCount(int count)  { this.count = count ; }
++    
++    @Override
++  public Block getBackingBlock()           { return block ; }
++    
++    @Override
++    public BlockMgr getBlockMgr()               { return bpTree.getNodeManager().getBlockMgr() ; }
++    
++    /** Do not use without great care */
++    public final RecordBuffer getRecordBuffer()  { return records ; }
++    /** Do not use without great care */
++    public final PtrBuffer getPtrBuffer()        { return ptrs ; }
++    
++    final void setParent(int parentId)           { this.parent = parentId ; } 
++    public final int getParent()                 { return parent ; }
++
++    
++    public final void setIsLeaf(boolean isLeaf)  { this.isLeaf = isLeaf ; }
++    public final boolean isLeaf()                { return this.isLeaf ; }
++    
++    @Override
++    public final int getId()        { return id ; }
++    
++    @Override
++    public String getRefStr() {
++        return String.format("BPTNode[id=%d]", getBackingBlock().getId()) ;
++    }
++
++    @Override
++    final void write()          { bpTree.getNodeManager().write(this) ; } 
++    
++    final private static void trackPath(AccessPath path, BPTreeNode node, int idx, BPTreePage page) {
++        if ( path != null )
++            path.add(node, idx, page) ;
++    }
++    
++    final private static void resetTrackPath(AccessPath path, BPTreeNode node, int idx, BPTreePage page) {
++        if ( path != null ) {
++            path.reset(node, idx, page) ;
++        }
++    }
++    
++    @Override
++    final boolean promote() {
++        if ( bpTree.getNodeManager().isWritable(this.getId()) )
++            return false ;
++        // This calls reset is needed.
++        //   The id, records buffer and pointer buffers need resetting if the block changed.
++        boolean promoteInPlace = bpTree.state().modifiableNodeBlock(getId()) ;
++        if ( promoteInPlace ) {
++            bpTree.getNodeManager().promoteInPlace(this) ;
++            return false ;
++        } else {
++            Block oldBlock = block ;
++            boolean b = bpTree.getNodeManager().promoteDuplicate(this) ;
++            if ( b ) {
++                bpTree.getNodeManager().getBlockMgr().release(oldBlock);
++            }
++            return b ;
++        }
++    }
++    
++    @Override
++    final void release()        { bpTree.getNodeManager().release(this) ; } 
++
++    @Override
++    final void free()           { bpTree.getNodeManager().free(this) ; } 
++    
++    // ============ SEARCH
++    
++    /* 
++     * Do a (binary) search of the node to find the record.
++     *   Returns: 
++     *     +ve or 0 => the index of the record 
++     *     -ve => The insertion point : the immediate higher record or length as (-i-1)
++     *  Convert to +ve and decend to find the RecordBuffer with the record in it. 
++     */
++    
++    @Override
++    final Record internalSearch(AccessPath path, Record rec) {
++        if ( BPT.CheckingNode )
++            internalCheckNode() ;
++        BPTreePage page = findHere(path, rec) ;
++        Record r = page.internalSearch(path, rec) ;
++        page.release() ;
++        return r ;
++    }
++
++    /** Find the next page to look at as we walk down the tree */
++    private final BPTreePage findHere(AccessPath path, Record rec) {
++        int idx = findSlot(rec) ;
++        idx = apply(idx) ;
++        // Find index, or insertion point (immediate higher slot) as (-i-1)
++        // A key is the highest element of the records up to this point
++        // so we search down at slot idx (between something smaller and
++        // something larger).
++        BPTreePage page = get(idx) ;
++        trackPath(path, this, idx, page) ;
++        return page ;
++    }
++    
++    // ============ INSERT
++
++    /*
++     * Traverse this page, ensuring the node below is not full before
++     * decending. Therefore there is always space to do the actual insert.
++     */
++
++    @Override
++    final Record internalInsert(AccessPath path, Record record) {
++        if ( logging(log) )
++            log(log, "internalInsert: %s [%s]", record, this) ;
++
++        internalCheckNode() ;
++
++        int idx = findSlot(record) ;
++        
++
++        if ( logging(log) )
++            log(log, "internalInsert: idx=%d (=>%d)", idx, apply(idx)) ;
++
++        idx = apply(idx) ;
++        BPTreePage page = get(idx) ;
++        trackPath(path, this, idx, page) ;
++
++        if ( logging(log) )
++            log(log, "internalInsert: next: %s", page) ;
++
++        if ( page.isFull() ) {
++            // Need to split the page before descending.
++            split(path, idx, page) ;
++            // Did it shift the insert index?
++            // Examine the record we pulled up in the split.
++            if ( Record.keyGT(record, records.get(idx)) ) {
++                page.release() ;
++                // Yes. Get the new (upper) page
++                idx = idx + 1 ;
++                page = get(idx) ;
++                path.reset(this, idx, page);
++            }
++            internalCheckNode() ;
++        }
++        
++        Record r = page.internalInsert(path, record) ;
++        page.release() ;
++        return r ;
++    }
++
++    /*
++     * Split a non-root node y, held at slot idx in its parent,
++     * which is 'this'and is large enough for a new entry without
++     * another split because we split full blocks on the way down.  
++     * Do this by splitting the node in two (call to BPTree.split)
++     * and inserting the new key/pointer pair.
++     * WRITE(y)
++     * WRITE(z)
++     * WRITE(this)
++     */
++    private void split(AccessPath path, int idx, BPTreePage y) {
++        // logging = true ;
++        if ( logging(log) ) {
++            log(log, "split >> y=%s  this=%s idx=%d", y.getRefStr(), this.getRefStr(), idx) ;
++            log(log, "split --   %s", y) ;
++        }
++
++        internalCheckNode() ;
++        if ( BPT.CheckingNode ) {
++            if ( !y.isFull() )
++                BPT.error("Node is not full") ;
++            if ( this.ptrs.get(idx) != y.getId() ) {
++                int a = this.ptrs.get(idx) ;
++                int b = y.getId() ;
++                BPT.error("Node to be split isn't in right place [%d/%d]", a, b) ;
++            }
++        }
++        internalCheckNodeDeep() ;
++
++        // Either-Or
++//        promotePage(path, this) ;
++//        promote1(y, this, idx) ;
++        // Includes promote "this" because "this" is in the path.
++        promotePage(path, y) ;
++
++        Record splitKey = y.getSplitKey() ;
++        splitKey = keyRecord(splitKey) ;
++
++        if ( logging(log) )
++            log(log, "Split key: %s", splitKey) ;
++
++        BPTreePage z = y.split() ;
++        if ( logging(log) ) {
++            log(log, "Split: %s", y) ;
++            log(log, "Split: %s", z) ;
++        }
++
++        // Key only.
++        if ( splitKey.hasSeparateValue() )
++            splitKey = keyRecord(splitKey) ;
++
++        // Insert new node. "add" shuffle's up as well.
++        records.add(idx, splitKey) ;
++        ptrs.add(idx + 1, z.getId()) ;
++        count++ ;
++
++        if ( logging(log) ) {
++            log(log, "split <<   %s", this) ;
++            log(log, "split <<   %s", y) ;
++            log(log, "split <<   %s", z) ;
++        }
++
++        y.write() ;
++        z.write() ;
++        z.release() ;
++        // y.release() ; y release management done by caller.
++        this.write() ;
++        if ( BPT.CheckingNode ) {
++            if ( Record.keyNE(splitKey, y.maxRecord()) )
++                BPT.error("Split key %d but max subtree %s", splitKey, y.maxRecord()) ;
++            internalCheckNodeDeep() ;
++        }
++    }
++
++    @Override
++    final Record getSplitKey() {
++        int ix = params.SplitIndex ;
++        Record split = records.get(ix) ;
++        return split ;
++    }
++
++    /** Split this block - return the split record (key only needed) */
++    @Override
++    final BPTreePage split() {
++        // Median record : will go in parent.
++        int ix = params.SplitIndex ;
++
++        // New block.
++        BPTreeNode z = create(this.parent, isLeaf) ;
++
++        // Leave the low end untouched and copy, and clear the high end.
++        // z becomes the new upper node, not the lower node.
++        // 'this' is the lower block.
++
++        int maxRec = maxRecords() ;
++        // Copy from top of y into z.
++        records.copy(ix + 1, z.records, 0, maxRec - (ix + 1)) ;
++        records.clear(ix, maxRec - ix) ; // Clear copied and median slot
++        records.setSize(ix) ; // Reset size
++
++        ptrs.copy(ix + 1, z.ptrs, 0, params.MaxPtr - (ix + 1)) ;
++        ptrs.clear(ix + 1, params.MaxPtr - (ix + 1)) ;
++        ptrs.setSize(ix + 1) ;
++
++        // Set sizes of subnodes
++        setCount(ix) ; // Median is ix
++        internalCheckNode() ; // y finished
++
++        z.isLeaf = isLeaf ;
++        z.setCount(maxRec - (ix + 1)) ; // Number copied into z
++
++        // Caller puts the blocks in split(int, BTreePage)
++        z.internalCheckNode() ;
++        return z ;
++    }
++
++    /*
++     * Split the root and leave the root block as the root.
++     * This is the only point the height of the tree increases.
++     * 
++     * Allocate new blocks.
++     * Copy root low into left
++     * Copy root high into right
++     * Set counts.
++     * Create new root settings (two pointers, one key record)
++     * WRITE(left)
++     * WRITE(right)
++     * WRITE(root)
++     */
++    private static void splitRoot(BPTreeNode root) {
++        BPlusTree bpTree = root.bpTree ;
++
++        if ( BPT.CheckingNode )
++            if ( ! root.isRoot() )
++                BPT.error("Not root: %d (root is id zero)", root.getId()) ;
++        root.internalCheckNode() ;
++        promoteRoot(root) ;
++
++        // Median record
++        int splitIdx = root.params.SplitIndex ;
++        Record rec = root.records.get(splitIdx) ;
++
++        if ( logging(log) ) {
++            log(log, "** Split root %d (%s)", splitIdx, rec) ;
++            log(log, "splitRoot >>   %s", root) ;
++        }
++
++        // New blocks.
++        BPTreeNode left = create(bpTree, root.getId(), root.isLeaf) ;
++        BPTreeNode right = create(bpTree, root.getId(), root.isLeaf) ;
++
++        // int maxRecords = maxRecords() ;
++
++        // New left
++        root.records.copy(0, left.records, 0, splitIdx) ;
++        root.ptrs.copy(0, left.ptrs, 0, splitIdx + 1) ;
++        left.count = splitIdx ;
++
++        // New right
++        root.records.copy(splitIdx + 1, right.records, 0, root.maxRecords() - (splitIdx + 1)) ;
++        root.ptrs.copy(splitIdx + 1, right.ptrs, 0, root.params.MaxPtr - (splitIdx + 1)) ;
++        right.count = root.maxRecords() - (splitIdx + 1) ;
++
++        if ( logging(log) ) {
++            log(log, "splitRoot -- left:   %s", left) ;
++            log(log, "splitRoot -- right:  %s", right) ;
++        }
++
++        // So left.count+right.count = bTree.NumRec-1
++
++        // Clear root by reformatting. New root not a leaf. Has count of 1 after
++        // formatting.
++        BPTreeNodeMgr.formatForRoot(root, false) ;
++        // Make a non-leaf.
++
++        // Insert two subnodes, divided by the median record
++        root.count = 1 ;
++
++        root.records.add(0, rec) ;
++        root.ptrs.setSize(2) ;
++        root.ptrs.set(0, left.getId()) ; // slot 0
++        root.ptrs.set(1, right.getId()) ; // slot 1
++
++        if ( logging(log) ) {
++            log(log, "splitRoot <<   %s", root) ;
++            log(log, "splitRoot <<   %s", left) ;
++            log(log, "splitRoot <<   %s", right) ;
++        }
++
++        left.write() ;
++        right.write() ;
++        left.release() ;
++        right.release() ;
++        root.write() ;
++
++        if ( BPT.CheckingNode ) {
++            root.internalCheckNode() ;
++            left.internalCheckNode() ;
++            right.internalCheckNode() ;
++        }
++    }
++
++    // ============ DELETE
++
++    /*
++     * Delete
++     * Descend, making sure that the node is not minimum size at each descend.
++     * If it is, rebalenace.
++     */
++
++    @Override
++    final Record internalDelete(AccessPath path, Record rec) {
++        if ( logging(log) )
++            log(log, ">> internalDelete(%s) : %s", rec, this) ;
++        internalCheckNode() ;
++
++        int x = findSlot(rec) ;
++        int y = apply(x) ;
++        BPTreePage page = get(y) ;
++        trackPath(path, this, y, page) ;
++
++        if ( page.isMinSize() ) { 
++            // Ensure that a node is at least min+1 so a delete can happen.
++            // Can't be the root - we decended in the get().
++            rebalance(path, page, y) ;  // Ignore return - need to refind.
++            page.release(); // TODO But rebalance may have freed this?
++            // Rebalance may have moved the record due to shuffling.  
++            x = findSlot(rec) ;
++            y = apply(x) ;
++            page = get(y) ;
++            promote1(page, this, y) ;
++            resetTrackPath(path, this, y, page) ;
++            if ( BPT.CheckingNode ) {
++                internalCheckNode() ;
++                page.checkNode() ;
++            }
++            this.write() ;
++            // Needed in case the record being deleted is not in the
++            // tree, which mean there is no work done and
++            // this page is not written.
++            page.write() ;
++        }
++
++        // Go to bottom
++        // Need to return the deleted key/value.
++        Record r2 = page.internalDelete(path, rec) ;
++        if ( x >= 0 ) {
++            // And hence r2 != null.
++            // The deleted key was in the tree as well as the records.
++            // Change to the new key for the subtree. 
++            // Path is already promoted by the delete. 
++            // promote1(this, ??, ??) ;
++            Record mx = page.maxRecord() ;
++            records.set(x, keyRecord(mx)) ;
++            this.write() ;
++        }
++        if ( logging(log) )
++            log(log, "<< internalDelete(%s) : %s", rec, this) ;
++
++        page.release() ;
++        return r2 ;
++    }
++
++    /*
++     * Reduce the root when it has only one pointer and no records.
++     * WRITE(root)
++     * RELEASE(old child)
++     * This is the only point the height of the tree decreases.
++     */
++
++    private static void reduceRoot(BPTreeNode root) {
++        if ( logging(log) )
++            log(log, "reduceRoot >> %s", root) ;
++
++        if ( BPT.CheckingNode && (!root.isRoot() || root.count != 0) )
++            BPT.error("Not an empty root") ;
++
++        if ( root.isLeaf ) {
++            if ( logging(log) )
++                log(log, "reduceRoot << leaf root") ;
++            return ;
++        }
++
++        // Must have been cloned due to delete lower down
++        promoteRoot(root) ;
++        
++        BPTreePage sub = root.get(0) ;
++        promote1(sub, root, 0) ;
++        BPTreeNode n = cast(sub) ;
++        // Can pull up into the root.
++        // Leave root node in same block (rather than swap to new root).
++        BPTreeNodeMgr.formatForRoot(root, n.isLeaf) ;
++        n.records.copy(0, root.records, 0, n.count) ;
++        n.ptrs.copy(0, root.ptrs, 0, n.count + 1) ;
++        root.isLeaf = n.isLeaf ;
++        root.count = n.count ;
++        root.write() ;
++        // Free up.
++        n.free() ;
++        if ( BPT.CheckingNode )
++            root.internalCheckNodeDeep() ;
++
++        if ( logging(log) )
++            log(log, "reduceRoot << %s", root) ;
++    }
++
++    /*
++     * Rebalance "node" at slot idx in parent (this)
++     * The node will then be greater than the minimum size
++     * and one-pass delete is then possible.
++     * 
++     * try to shift right, from the left sibling (if exists)
++     * WRITE(left)
++     * WRITE(n)
++     * WRITE(this)
++     * try to shift left, from the right sibling (if exists)
++     * WRITE(right)
++     * WRITE(n)
++     * WRITE(this)
++     * else
++     * merge with left or right sibling
++     * Suboperations do all the write-back of nodes.
++     * 
++     * return the page which might be coalesced from left or right..
++     */
++    private BPTreePage rebalance(AccessPath path, BPTreePage node, int idx) {
++        if ( logging(log) ) {
++            log(log, "rebalance(id=%d, idx=%d)", node.getId(), idx) ;
++            log(log, ">> this: %s", this) ;
++            log(log, ">> node: %s", node) ;
++        }
++        internalCheckNode() ;
++//        promotePage(path, this) ;
++//        promote1(node, this, idx) ;
++        // Includes promote "this"
++        promotePage(path, node) ;
++
++        // Try left first
++        BPTreePage left = null ;
++        if ( idx > 0 )
++            left = get(idx - 1) ;
++
++        // *** SHIFTING : need to change the marker record in the parent.
++        // *** getHighRecord of lower block.
++
++        if ( left != null && !left.isMinSize() ) {
++            if ( logging(log) )
++                log(log, "rebalance/shiftRight") ;
++            // Move elements around.
++            promote1(left, this, idx-1) ;
++            
++            shiftRight(left, node, idx - 1) ;
++
++            if ( logging(log) )
++                log(log, "<< rebalance: %s", this) ;
++            if ( BPT.CheckingNode ) {
++                left.checkNode() ;
++                node.checkNode() ;
++                this.internalCheckNode() ;
++            }
++            left.release() ;
++            return node ;
++        }
++
++        BPTreePage right = null ;
++        if ( idx < count )
++            right = get(idx + 1) ;
++
++        if ( right != null && !right.isMinSize() ) {
++            if ( logging(log) )
++                log(log, "rebalance/shiftLeft") ;
++            
++            promote1(right, this, idx+1) ;
++            
++            shiftLeft(node, right, idx) ;
++
++            if ( logging(log) )
++                log(log, "<< rebalance: %s", this) ;
++            if ( BPT.CheckingNode ) {
++                right.checkNode() ;
++                node.checkNode() ;
++                this.internalCheckNode() ;
++            }
++            if ( left != null )
++                left.release() ;
++            right.release() ;
++            return node ;
++        }
++
++        // Couldn't shift. Collapse two pages.
++        if ( BPT.CheckingNode && left == null && right == null )
++            BPT.error("No siblings") ;
++
++        if ( left != null ) {
++            promote1(left, this, idx-1 ) ;
++            if ( logging(log) )
++                log(log, "rebalance/merge/left: left=%d n=%d [%d]", left.getId(), node.getId(), idx - 1) ;
++            if ( BPT.CheckingNode && left.getId() == node.getId() )
++                BPT.error("Left and n the same: %s", left) ;
++            BPTreePage page = merge(left, node, idx - 1) ;
++            if ( right != null )
++                // HACK : We didn't use it.
++                right.release() ;
++            //left release?
++            return page ;
++        } else {
++            // left == null
++            // rigth != null
++            promote1(right, this, idx+1 ) ;
++            // TODO ** HERE is it tracked correctly? Turmn tracking on, test_clear_02 and enable read checking in   Lifecycle track.free.
++            if ( logging(log) )
++                log(log, "rebalance/merge/right: n=%d right=%d [%d]", node.getId(), right.getId(), idx) ;
++            if ( BPT.CheckingNode && right.getId() == node.getId() )
++                BPT.error("N and right the same: %s", right) ;
++            BPTreePage page = merge(node, right, idx) ;
++            return page ;
++        }
++    }
++
++    /** Merge left with right ; fills left, frees right */
++    private BPTreePage merge(BPTreePage left, BPTreePage right, int dividingSlot) {
++        if ( logging(log) ) {
++            log(log, ">> merge(@%d): %s", dividingSlot, this) ;
++            log(log, ">> left:  %s", left) ;
++            log(log, ">> right: %s", right) ;
++        }
++
++        // /==\ + key + /==\ ==> /====\
++        Record splitKey = records.get(dividingSlot) ;
++        BPTreePage page = left.merge(right, splitKey) ;
++        // Must release right (not done in merge)
++        if ( logging(log) )
++            log(log, "-- merge: %s", page) ;
++
++        left.write() ;
++        left.release() ;
++        right.free() ;
++
++        if ( page == right )
++            BPT.error("Returned page is not the left") ;
++
++        if ( BPT.CheckingNode ) {
++            if ( isLeaf ) {
++                // If two data blocks, then the split key is not included
++                // (it's already there, with its value).
++                // Size is N+N and max could be odd so N+N and N+N+1 are
++                // possible.
++                if ( left.getCount() + 1 != left.getMaxSize() && left.getCount() != left.getMaxSize() )
++                    BPT.error("Inconsistent data node size: %d/%d", left.getCount(), left.getMaxSize()) ;
++            } else if ( !left.isFull() ) {
++                // If not two data blocks, the left side should now be full
++                // (N+N+split)
++                BPT.error("Inconsistent node size: %d/%d", left.getCount(), left.getMaxSize()) ;
++            }
++        }
++
++        // Remove from parent (which is "this")
++        shuffleDown(dividingSlot) ;
++        this.write() ;
++        internalCheckNodeDeep() ;
++        if ( logging(log) ) {
++            log(log, "<< merge: %s", this) ;
++            log(log, "<< left:  %s", left) ;
++        }
++        return left ;
++    }
++
++    @Override
++    BPTreePage merge(BPTreePage right, Record splitKey) {
++        return merge(this, splitKey, cast(right)) ;
++    }
++
++    private static BPTreeNode merge(BPTreeNode left, Record splitKey, BPTreeNode right) {
++        // Merge blocks - does not adjust the parent.
++        // Copy right to top of left.
++        // Caller releases 'right' (needed for testing code).
++
++        left.records.add(splitKey) ;
++
++        // Copy over right to top of left.
++        right.records.copyToTop(left.records) ;
++        right.ptrs.copyToTop(left.ptrs) ;
++
++        // Update count
++        left.count = left.count + right.count + 1 ;
++        left.internalCheckNode() ;
++
++        right.records.clear() ;
++        right.ptrs.clear() ;
++        return left ;
++    }
++
++    private void shiftRight(BPTreePage left, BPTreePage right, int i) {
++        if ( logging(log) ) {
++            log(log, ">> shiftRight: this:  %s", this) ;
++            log(log, ">> shiftRight: left:  %s", left) ;
++            log(log, ">> shiftRight: right: %s", right) ;
++        }
++        Record r1 = records.get(i) ;
++        Record r2 = left.shiftRight(right, r1) ;
++        r2 = keyRecord(r2) ;
++        this.records.set(i, r2) ;
++
++        left.write() ;
++        right.write() ;
++        // Do later -- this.put();
++        if ( logging(log) ) {
++            log(log, "<< shiftRight: this:  %s", this) ;
++            log(log, "<< shiftRight: left:  %s", left) ;
++            log(log, "<< shiftRight: right: %s", right) ;
++        }
++    }
++
++    private void shiftLeft(BPTreePage left, BPTreePage right, int i) {
++        if ( logging(log) ) {
++            log(log, ">> shiftLeft: this:  %s", this) ;
++            log(log, ">> shiftLeft: left:  %s", left) ;
++            log(log, ">> shiftLeft: right: %s", right) ;
++        }
++        Record r1 = records.get(i) ;
++        Record r2 = left.shiftLeft(right, r1) ;
++        r2 = keyRecord(r2) ;
++        this.records.set(i, r2) ;
++
++        left.write() ;
++        right.write() ;
++        // Do this later - this.put();
++        if ( logging(log) ) {
++            log(log, "<< shiftLeft: this:  %s", this) ;
++            log(log, "<< shiftLeft: left:  %s", left) ;
++            log(log, "<< shiftLeft: right: %s", right) ;
++        }
++    }
++
++    @Override
++    Record shiftRight(BPTreePage other, Record splitKey) {
++        BPTreeNode node = cast(other) ;
++        if ( BPT.CheckingNode ) {
++            if ( count == 0 )
++                BPT.error("Node is empty - can't shift a slot out") ;
++            if ( node.isFull() )
++                BPT.error("Destination node is full") ;
++        }
++        // Records: promote moving element, replace with splitKey
++        Record r = this.records.getHigh() ;
++        this.records.removeTop() ;
++        node.records.add(0, splitKey) ;
++
++        // Pointers just shift
++        this.ptrs.shiftRight(node.ptrs) ;
++
++        this.count-- ;
++        node.count++ ;
++        this.internalCheckNode() ;
++        node.internalCheckNode() ;
++        return r ;
++    }
++
++    @Override
++    Record shiftLeft(BPTreePage other, Record splitKey) {
++        BPTreeNode node = cast(other) ;
++        if ( BPT.CheckingNode ) {
++            if ( count == 0 )
++                BPT.error("Node is empty - can't shift a slot out") ;
++            if ( isFull() )
++                BPT.error("Destination node is full") ;
++        }
++        Record r = node.records.getLow() ;
++        // Records: promote moving element, replace with splitKey
++        this.records.add(splitKey) ;
++        node.records.shiftDown(0) ;
++
++        // Pointers just shift
++        this.ptrs.shiftLeft(node.ptrs) ;
++
++        this.count++ ;
++        node.count-- ;
++        return r ;
++    }
++
++    private void shuffleDown(int x) {
++        // x is the index in the parent and may be on eover the end.
++        if ( logging(log) ) {
++            log(log, "ShuffleDown: i=%d count=%d MaxRec=%d", x, count, maxRecords()) ;
++            log(log, "ShuffleDown >> %s", this) ;
++        }
++
++        if ( BPT.CheckingNode && x >= count )
++            BPT.error("shuffleDown out of bounds") ;
++
++        // Just the top to clear
++
++        if ( x == count - 1 ) {
++            records.removeTop() ;
++            ptrs.removeTop() ;
++
++            count-- ;
++            if ( logging(log) ) {
++                log(log, "shuffleDown << Clear top") ;
++                log(log, "shuffleDown << %s", this) ;
++            }
++            internalCheckNode() ;
++            return ;
++        }
++
++        // Shuffle down. Removes key and pointer just above key.
++
++        records.shiftDown(x) ;
++        ptrs.shiftDown(x + 1) ;
++        count-- ;
++        if ( logging(log) )
++            log(log, "shuffleDown << %s", this) ;
++        internalCheckNode() ;
++    }
++
++    // ---- Utilities
++
++    private static final BPTreeNode cast(BPTreePage other) {
++        try {
++            return (BPTreeNode)other ;
++        }
++        catch (ClassCastException ex) {
++            BPT.error("Wrong type: " + other) ;
++            return null ;
++        }
++    }
++
++    final int findSlot(Record rec) {
++        int x = records.find(rec) ;
++        return x ;
++    }
++
++    final boolean isRoot() {
++        // No BPT remembered root node currently
++        // if ( bpTree.root == this ) return true ;
++        return this.parent == BPlusTreeParams.RootParent ;
++    }
++
++    private Record keyRecord(Record record) {
++        return bpTree.getRecordFactory().createKeyOnly(record) ;
++    }
++
++    // Fixup/remove?
++    private final int maxRecords() {
++        return params.MaxRec ;
++    }
++
++    @Override
++    final boolean isFull() {
++        if ( BPT.CheckingNode && count > maxRecords() )
++            BPT.error("isFull: Moby block: %s", this) ;
++
++        // Count is number of records.
++        return count >= maxRecords() ;
++    }
++    
++    /** Return true if there are no keys here or below this node */
++    @Override
++    final boolean hasAnyKeys() {
++        if ( this.count > 0 )
++            return true ;
++        if ( !isRoot() )
++            return false ;
++
++        // The root can be zero size and point to a single data block.
++        BPTreePage page = get(0) ;
++        boolean b = page.hasAnyKeys() ;
++        page.release() ;
++        return b ;
++    }
++
++    @Override
++    final boolean isMinSize() {
++        int min = params.getMinRec() ;
++        if ( BPT.CheckingNode && count < min )
++            BPT.error("isMinSize: Dwarf block: %s", this) ;
++
++        return count <= min ;
++    }
++
++    // ========== Other
++
++    @Override
++    public String toString() {
++        StringBuilder b = new StringBuilder() ;
++        if ( isLeaf )
++            b.append("LEAF: ") ;
++        else
++            b.append("NODE: ") ;
++        String labelStr = "??" ;
++        if ( parent >= 0 )
++            labelStr = Integer.toString(parent) ;
++        else if ( parent == BPlusTreeParams.RootParent )
++            labelStr = "root" ;
++        if ( isLeaf )
++            labelStr = labelStr + "/leaf" ;
++
++        b.append(String.format("%d [%s] (size %d) -- ", getId(), labelStr, count)) ;
++        for ( int i = 0 ; i < maxRecords() ; i++ ) {
++            b.append(childStr(i)) ;
++            b.append(" (") ;
++            b.append(recstr(records, i)) ;
++            b.append(") ") ;
++        }
++        b.append(childStr(params.HighPtr)) ;
++        return b.toString() ;
++    }
++
++    @Override
++    protected String typeMark() {
++        String mark = isLeaf() ? "Leaf" : "Node" ;
++        if ( isRoot() )
++            mark = mark+"/Root" ;
++        return mark ; 
++    }
++
++    private final String recstr(RecordBuffer records, int idx) {
++        if ( records.isClear(idx) )
++            return "----" ;
++
++        Record r = records._get(idx) ;
++        return r.toString() ;
++    }
++
++    public void dump() {
++        boolean b = BPT.Logging ;
++        BPT.Logging = false ;
++        try { dump(IndentedWriter.stdout) ; }
++        finally { BPT.Logging = b ; }
++    }
++
++    public void dump(IndentedWriter out) {
++        output(out) ;
++        out.ensureStartOfLine() ;
++        out.flush() ;
++    }
++
++    public String dumpToString() {
++        IndentedLineBuffer buff = new IndentedLineBuffer() ;
++        output(buff) ;
++        return buff.asString() ;
++    }
++
++    @Override
++    public void output(IndentedWriter out) {
++        out.print(toString()) ;
++        out.incIndent() ;
++        for ( int i = 0 ; i < count + 1 ; i++ ) {
++            out.println() ;
++            BPTreePage page = get(i) ;
++            page.output(out) ;
++            page.release() ;
++
++        }
++        out.decIndent() ;
++    }
++
++    private String childStr(int i) {
++        if ( i >= ptrs.size() )
++            return "*" ;
++        int x = ptrs.get(i) ;
++        return Integer.toString(x) ;
++    }
++
++    // =========== Checking
++    // internal checks - only if checking
++
++    // Check node does not assume a valid tree - may be in mid-operation.
++    private final void internalCheckNode() {
++        if ( BPT.CheckingNode )
++            checkNode(null, null) ;
++    }
++
++    private final void internalCheckNodeDeep() {
++        // This distrubes trackign operations and blocks.
++        // Hooks left but switch off.
++        if ( false )
++            checkNodeDeep() ;
++    }
++
++    @Override
++    final void checkNode() {
++        checkNode(null, null) ;
++    }
++
++    @Override
++    final void checkNodeDeep() {
++        if ( isRoot() ) {
++            // if ( !isLeaf && count == 0 )
++            // error("Root is of size zero (one pointer) but not a leaf") ;
++            if ( parent != BPlusTreeParams.RootParent )
++                BPT.error("Root parent is wrong") ;
++            // if ( count == 0 )
++            // return ;
++        }
++        checkNodeDeep(null, null) ;
++    }
++
++    // Checks of a single node - no looking at children
++    // min - inclusive; max - inclusive (allows for duplicates)
++    final private void checkNode(Record min, Record max) {
++        int id = getId() ;
++        if ( count != records.size() )
++            BPT.error("Inconsistent: id=%d, count=%d, records.size()=%d : %s", id, count, records.size(), this) ;
++
++        if ( !isLeaf && count + 1 != ptrs.size() )
++            BPT.error("Inconsistent: id=%d, count+1=%d, ptrs.size()=%d ; %s", id, count + 1, ptrs.size(), this) ;
++
++        // No BPT remembered root node currently
++        // if ( bpTree.root != null && !isRoot() && count < params.MinRec)
++        if ( !isRoot() && count < params.MinRec ) {
++            // warning("Runt node: %s", this) ;
++            BPT.error("Runt node: %s", this) ;
++        }
++        if ( !isRoot() && count > maxRecords() )
++            BPT.error("Over full node: %s", this) ;
++        if ( !isLeaf && parent == id )
++            BPT.error("Parent same as id: %s", this) ;
++        Record k = min ;
++
++        // Test records in the allocated area
++        for ( int i = 0 ; i < count ; i++ ) {
++            if ( records.get(i) == null )
++                BPT.error("Node: %d : Invalid record @%d :: %s", id, i, this) ;
++            if ( k != null && keyGT(k, records.get(i)) ) {
++                Record r = records.get(i) ;
++                // keyGT(k, r) ;
++                BPT.error("Node: %d: Not sorted (%d) (%s, %s) :: %s ", id, i, k, r, this) ;
++            }
++            k = records.get(i) ;
++        }
++
++        if ( k != null && max != null && keyGT(k, max) )
++            BPT.error("Node: %d - Record is too high (max=%s):: %s", id, max, this) ;
++
++        if ( SystemIndex.getNullOut() ) {
++            // Test records in the free area
++            for ( int i = count ; i < maxRecords() ; i++ ) {
++                if ( !records.isClear(i) )
++                    BPT.error("Node: %d - not clear (idx=%d) :: %s", id, i, this) ;
++            }
++        }
++
++        // Pointer checks.
++        int i = 0 ;
++        // Check not empty at bottom.
++        for ( ; i < count + 1 ; i++ ) {
++            if ( ptrs.get(i) < 0 )
++                BPT.error("Node: %d: Invalid child pointer @%d :: %s", id, i, this) ;
++//            // This does Block IO so disturbs tracking.
++//            if ( BPT.CheckingTree && isLeaf ) {
++//                int ptr = ptrs.get(i) ;
++//                BPTreeRecords records = bpTree.getRecordsMgr().getRead(ptr) ;
++//                int rid = records.getId() ;
++//                if ( rid != ptrs.get(i) )
++//                    BPT.error("Records: Block @%d has a different id: %d :: %s", rid, i, this) ;
++//                int link = records.getLink() ;
++//                // Don't check if -1 which does not exist.
++//                if ( link != -1 && i != count ) {
++//                    BPTreeRecords page = bpTree.getRecordsMgr().getRead(ptrs.get(i)) ;
++//                    int rid2 = page.getLink() ;
++//                    if ( link != rid2 )
++//                        BPT.error("Records: Link not to next block @%d/@%d has a different id: %d :: %s", rid, rid2, i, records) ;
++//                    bpTree.getRecordsMgr().release(page) ;
++//                }
++//                records.release() ;
++//            }
++        }
++
++        // Check empty is empty
++        if ( SystemIndex.getNullOut() ) {
++            int x = params.MaxPtr ;
++            for ( ; i < x ; i++ ) {
++                if ( !ptrs.isClear(i) )
++                    BPT.error("Node: %d: Unexpected pointer @%d :: %s", id, i, this) ;
++            }
++        }
++    }
++
++    private void checkNodeDeep(Record min, Record max) {
++        checkNode(min, max) ;
++        int id = getId() ;
++        // Check pointers.
++        int limit = (count == 0) ? 0 : count + 1 ;
++
++        for ( int i = 0 ; i < limit ; i++ ) {
++            Record min1 = min ;
++            Record max1 = max ;
++            BPTreePage n = get(i) ;
++
++            if ( i != count ) {
++                Record keySubTree = n.getHighRecord() ; // high key in immediate
++                                                        // child
++                Record keyHere = records.get(i) ; // key in this
++
++                if ( keySubTree == null )
++                    BPT.error("Node: %d: Can't get high record from %d", id, n.getId()) ;
++
++                if ( keySubTree.getKey() == null )
++                    BPT.error("Node: %d: Can't get high record is missing it's key from %d", id, n.getId()) ;
++
++                if ( keyHere == null )
++                    BPT.error("Node: %d: record is null", id) ;
++
++                if ( keyHere.getKey() == null )
++                    BPT.error("Node: %d: Record key is null", id) ;
++
++                if ( keyGT(keySubTree, keyHere) )
++                    BPT.error("Node: %d: Child key %s is greater than this key %s", id, keySubTree, keyHere) ;
++
++                Record keyMax = n.maxRecord() ; // max key in subTree
++                Record keyMin = n.internalMinRecord(null) ;
++
++                if ( keyNE(keyHere, keyMax) )
++                    BPT.error("Node: %d: Key %s is not the max [%s] of the sub-tree idx=%d", id, keyHere, keyMax, i) ;
++
++                if ( min != null && keyGT(min, keyMin) )
++                    BPT.error("Node: %d: Minimun for this node should be %s but it's %s", id, min, keyMin) ;
++                if ( max != null && keyLT(max, keyMax) )
++                    BPT.error("Node: %d: Maximum for this node should be %s but it's %s", id, max, keyMax) ;
++                if ( min != null && keyGT(min, keyHere) )
++                    BPT.error("Node: %d: Key too small: %s - min should be %s", id, keyHere, min) ;
++                // keyHere == keyMax ??
++                if ( max != null && keyLT(max, keyHere) )
++                    BPT.error("Node: %d: Key too large: %s - max should be %s", id, keyHere, max) ;
++            }
++
++            // Look deeper.
++            if ( !(n instanceof BPTreeNode) ) {
++                // Records.
++                n.checkNodeDeep() ;
++                n.release() ;
++                continue ;
++            }
++
++            // Valid pointer?
++            if ( isLeaf ) {
++                if ( !bpTree.getRecordsMgr().getBlockMgr().valid(ptrs.get(i)) )
++                    BPT.error("Node: %d: Dangling ptr (records) in block @%d :: %s", id, i, this) ;
++            } else {
++                if ( !bpTree.getNodeManager().valid(ptrs.get(i)) )
++                    BPT.error("Node: %d: Dangling ptr in block @%d :: %s", id, i, this) ;
++            }
++
++            // Calc new min/max.
++            if ( i == 0 )
++                max1 = records.get(0) ;
++            else if ( i == count ) {
++                min1 = records.get(count - 1) ;
++                max1 = null ;
++            } else {
++                min1 = records.get(i - 1) ;
++                max1 = records.get(i) ;
++            }
++            // if ( n.parent != id )
++            // error("Node: %d [%d]: Parent/child mismatch :: %s", id, n.parent,
++            // this) ;
++
++            ((BPTreeNode)n).checkNodeDeep(min1, max1) ;
++            n.release() ;
++        }
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPTreeNodeMgr.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPTreeNodeMgr.java
index 0000000,0000000..e929762
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPTreeNodeMgr.java
@@@ -1,0 -1,0 +1,228 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.bplustree;
++
++import static org.seaborne.dboe.base.block.BlockType.BPTREE_BRANCH ;
++import static org.seaborne.dboe.base.block.BlockType.BPTREE_LEAF ;
++import static org.seaborne.dboe.base.block.BlockType.RECORD_BLOCK ;
++
++import java.nio.ByteBuffer ;
++
++import org.seaborne.dboe.base.block.Block ;
++import org.seaborne.dboe.base.block.BlockMgr ;
++import org.seaborne.dboe.base.block.BlockType ;
++import org.seaborne.dboe.base.buffer.PtrBuffer ;
++import org.seaborne.dboe.base.buffer.RecordBuffer ;
++import org.seaborne.dboe.base.page.BlockConverter ;
++import org.seaborne.dboe.base.page.PageBlockMgr ;
++
++/** BPlusTreePageMgr = BPlusTreeNode manager */
++public final class BPTreeNodeMgr extends PageBlockMgr<BPTreeNode>
++{
++    // Only "public" for external very low level tools in development to access this class.
++    // Assume package access.
++
++    public BPTreeNodeMgr(BPlusTree bpTree, BlockMgr blockMgr) {
++        super(new Block2BPTreeNode(bpTree), blockMgr) ;
++    }
++   
++    /** Allocate space for a fresh node. */
++    public BPTreeNode createNode(int parent) {
++        BPTreeNode n = create(BPTREE_BRANCH) ;
++        n.setIsLeaf(false) ;
++        n.setParent(parent) ;
++        return n ;
++    }
++
++    @Override
++    public BPTreeNode getWrite(int id) {
++        return super.getWrite(id, BPlusTreeParams.UnsetParent) ;
++    }
++
++    @Override
++    public BPTreeNode getRead(int id) {
++        return super.getRead(id, BPlusTreeParams.UnsetParent) ;
++    }
++
++    /** Fetch a block - fill in the parent id, which is not in the on-disk bytes */
++    @Override
++    public BPTreeNode getRead(int id, int parent) {
++        BPTreeNode n = super.getRead$(id) ;
++        n.setParent(parent);
++        return n ;
++    }
++
++    /** Fetch a block - fill in the parent id, which is not in the on-disk bytes */
++    @Override
++    public BPTreeNode getWrite(int id, int parent) {
++        BPTreeNode n = super.getWrite$(id) ;
++        n.setParent(parent);
++        return n ;
++    }
++
++    boolean isWritable(int id) {
++        //System.err.println("BPTreeNodeMgr.isWritable") ;
++        return false ;
++//      return bpTree.state.modifiableNodeBlock(id) ;
++    }
++
++    private static class Block2BPTreeNode implements BlockConverter<BPTreeNode>
++    {
++        private final BPlusTree bpTree ;
++
++        Block2BPTreeNode(BPlusTree bpTree) { this.bpTree = bpTree ; }
++        
++        @Override
++        public BPTreeNode createFromBlock(Block block, BlockType bType) {
++            return overlay(bpTree, block, bType == RECORD_BLOCK, 0) ;
++        }
++
++        @Override
++        public BPTreeNode fromBlock(Block block) {
++            // synchronized - needed for multiple reader?
++            synchronized (block) {
++                int x = block.getByteBuffer().getInt(0) ;
++                BlockType type = getType(x) ;
++
++                if ( type != BPTREE_BRANCH && type != BPTREE_LEAF )
++                    throw new BPTreeException("Wrong block type: " + type) ;
++                int count = decodeCount(x) ;
++                return overlay(bpTree, block, (type == BPTREE_LEAF), count) ;
++            }
++        }
++
++        @Override
++        public Block toBlock(BPTreeNode node) {
++            // It's manipulated in-place so no conversion needed, 
++            // Just the count needs to be fixed up. 
++//            ByteBuffer bb = node.getBackingByteBuffer() ;
++//            BlockType bType = (node.isLeaf ? BPTREE_LEAF : BPTREE_BRANCH ) ;
++
++            Block block = node.getBackingBlock() ;
++            BlockType bType = (node.isLeaf() ? BPTREE_LEAF : BPTREE_BRANCH ) ;
++
++            int c = encodeCount(bType, node.getCount()) ;
++            block.getByteBuffer().putInt(0, c) ;
++            return block ;
++        }
++    }
++    
++//    // Leaves have a count of -(count+1)
++//    // (same as the binary search encoding of "not found")
++//    private static final int encCount(int i)     { return -(i+1) ; } 
++//    private static final int decCount(int i)     { return -i-1 ; }
++
++    // ----
++    private static final BlockType getType(int x) {
++        return BlockType.extract(x >>> 24) ;
++    }
++
++    private static final int encodeCount(BlockType type, int i) {
++        return (type.id() << 24) | (i & 0x00FFFFFF) ;
++    }
++
++    private static final int decodeCount(int i) {
++        return i & 0x00FFFFFF ;
++    }
++
++    /** byte[] layout.
++     * 
++     * New:
++     *  0: Block type
++     *  1-3: Count 
++     *  Internal nodes:
++     *    4-X:        Records: b+tree.MaxRec*record length
++     *    X- :        Pointers: b+tree.MaxPtr*ptr length 
++     */
++    private static BPTreeNode overlay(BPlusTree bpTree, Block block, boolean asLeaf, int count) {
++        // if ( byteBuffer.order() != Const.NetworkOrder )
++        // throw new BTreeException("ByteBuffer in wrong order") ;
++
++        // Fix up the id later.
++        BPTreeNode n = new BPTreeNode(bpTree) ;
++        // The count is zero at the root only.
++        // When the root is zero, it's a leaf.
++        formatBPTreeNode(n, bpTree, block, asLeaf, BPlusTreeParams.NoParent, count) ;
++        return n ;
++    }
++
++    static void formatBPTreeNode(BPTreeNode n, BPlusTree bpTree, Block block, boolean leaf, int parent, int count) {
++        BPlusTreeParams params = bpTree.getParams() ;
++
++        int ptrBuffLen = params.MaxPtr * params.getPtrLength() ;
++        // Only store the key part of records in a B+Tree block
++        // OLD - Node table has real value part - what's going on?
++
++        // [Issue:FREC]
++        // Allocate space for record, key and value, despite slight over
++        // allocation.
++        int recBuffLen = params.MaxRec * params.getRecordLength() ;
++
++        // [Issue:FREC] Should be: key space only.
++        // int recBuffLen = params.MaxRec * params.getKeyLength() ;
++
++        n.id = block.getId().intValue() ;
++        n.setParent(parent) ;
++        n.setCount(count) ;
++        n.setIsLeaf(leaf) ;
++
++        int header = BPlusTreeParams.BlockHeaderSize ;
++        int rStart = header ;
++        int pStart = header + recBuffLen ;
++
++        // Find the number of pointers.
++        int numPtrs = -1 ;
++
++        // The root can have count zero - which means one pointer always.
++        // Junk when creating a new new node.
++        if ( n.getCount() < 0 ) {
++            numPtrs = 0 ;
++            n.setCount(decodeCount(n.getCount())) ;
++        } else
++            numPtrs = n.getCount() + 1 ;
++
++        // Block dependent
++        
++        n.block = block ;
++        ByteBuffer byteBuffer = block.getByteBuffer() ;
++
++        // -- Records area
++        byteBuffer.position(rStart) ;
++        byteBuffer.limit(rStart + recBuffLen) ;
++        ByteBuffer bbr = byteBuffer.slice() ;
++        // bbr.limit(recBuffLen) ;
++        n.setRecordBuffer(new RecordBuffer(bbr, n.params.keyFactory, n.getCount())) ;
++
++        // -- Pointers area
++        byteBuffer.position(pStart) ;
++        byteBuffer.limit(pStart + ptrBuffLen) ;
++
++        ByteBuffer bbi = byteBuffer.slice() ;
++        // bbi.limit(ptrBuffLen) ;
++        n.setPtrBuffer(new PtrBuffer(bbi, numPtrs)) ;
++
++        // Reset
++        byteBuffer.rewind() ;
++    }
++    
++    static final void formatForRoot(BPTreeNode n, boolean asLeaf) {
++        BPTreeNodeMgr.formatBPTreeNode(n, n.bpTree, n.getBackingBlock(), asLeaf, BPlusTreeParams.RootParent, 0) ;
++        // Tweak for the root-specials. The node is not consistent yet.
++        // Has one dangling pointer.
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPTreePage.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPTreePage.java
index 0000000,0000000..a780ad3
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPTreePage.java
@@@ -1,0 -1,0 +1,135 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.bplustree;
++
++import org.seaborne.dboe.base.block.BlockMgr ;
++import org.seaborne.dboe.base.page.Page ;
++import org.seaborne.dboe.base.record.Record ;
++import org.slf4j.Logger ;
++
++/** Abstraction of a B+Tree node - either an branch (BTreeNode) or records block (BTreeRecords) */
++abstract public class BPTreePage implements Page
++{
++    protected BPTreePage(BPlusTree bpTree) {
++        this.bpTree = bpTree ;
++        // bpTree can be null in testing.
++        this.params = ( bpTree == null ) ? null : bpTree.getParams() ;
++    }
++    
++    protected final BPlusTree bpTree ; 
++    protected final BPlusTreeParams params ;
++
++    /** Short form for logging */
++    protected String label() {
++        return String.format("%d[%s]", getId(), typeMark()) ;
++    }
++    
++    protected abstract String typeMark() ;
++    
++    protected abstract Logger getLogger() ;
++    
++    abstract BlockMgr getBlockMgr() ;
++
++    /** Split in two, return the new (upper) page.  
++     *  Split key is highest key of the old (lower) page.
++     *  Does NOT put pages back to any underlying block manager
++     */ 
++    abstract BPTreePage split() ;
++    
++    /** Move the element from the high end of this to the low end of other,
++    *  possible including the splitKey
++     * Return the new split point (highest record in left tree for records; moved element for nodes)
++     */
++    abstract Record shiftRight(BPTreePage other, Record splitKey) ;
++    
++    /** Move the element from the high end of other to the high end of this, 
++     * possible including the splitKey
++     * Return the new split point (highest record in left tree; moved element for nodes)
++     */
++    abstract Record shiftLeft(BPTreePage other, Record splitKey) ;
++    
++    /** Merge this (left) and the page imemdiately to it's right other into a single block
++     */
++    abstract BPTreePage merge(BPTreePage right, Record splitKey) ;
++    //* Return the new page (may be left or right)
++    
++    /** Test whether this page is full (has no space for a new element) - used in "insert" */
++    abstract boolean isFull() ;
++    
++    /**  Test whether this page is of minimum size (removing a record would violate the packing limits) - used in "delete" */
++    abstract boolean isMinSize() ;
++    
++    abstract int getCount() ;
++    
++    abstract void setCount(int count) ;
++  
++    abstract int getMaxSize() ;
++    
++    /**  Test whether this page has any keys */
++    abstract boolean hasAnyKeys() ;
++
++    /** Find a record; return null if not found */
++    abstract Record internalSearch(AccessPath path, Record rec) ;
++    
++    /** Insert a record - return existing value if any, else null - put back modifed blocks */
++    abstract Record internalInsert(AccessPath path, Record record) ;
++    
++    /** Delete a record - return the old value if there was one, else null - put back modifed blocks */
++    abstract Record internalDelete(AccessPath path, Record record) ;
++
++    /** Least in page */
++    abstract Record getLowRecord() ;
++
++    /** Greatest in page */
++    abstract Record getHighRecord() ;
++
++    /** Least in subtree */
++    abstract Record internalMinRecord(AccessPath path) ;
++
++    /** Greatest in subtree */
++    abstract Record internalMaxRecord(AccessPath path) ;
++
++    // For checking ...
++    
++    /** Least in subtree */
++    Record minRecord()      { return internalMinRecord(null) ; } 
++
++    /** Greatest in subtree */
++    Record maxRecord()      { return internalMaxRecord(null) ; }
++    
++    /** Write, or at least ensure will be written */
++    abstract void write() ; 
++    
++    /** Turn a read page into a write page. Return true if any changes were made. */
++    abstract boolean promote() ;
++
++    /** Mark as no longer needed */
++    abstract void release() ;
++    
++    /** Discard with this block (for ever) */
++    abstract void free() ;
++
++    /** Check - just this level.*/
++    abstract void checkNode() ;
++    
++    /** Check - here and below */
++    abstract void checkNodeDeep() ;
++
++    /** Return the split point for this record (need only be a key)*/
++    abstract Record getSplitKey() ;
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPTreeRangeIterator.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPTreeRangeIterator.java
index 0000000,0000000..dd6da35
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPTreeRangeIterator.java
@@@ -1,0 -1,0 +1,152 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.bplustree;
++
++import java.util.* ;
++
++import org.apache.jena.atlas.iterator.Iter ;
++import org.apache.jena.atlas.lib.InternalErrorException ;
++import org.seaborne.dboe.base.record.Record ;
++import org.seaborne.dboe.trans.bplustree.AccessPath.AccessStep ;
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++/** Iterator over records that does not assume records block linkage */ 
++class BPTreeRangeIterator implements Iterator<Record> {
++    static Logger log = LoggerFactory.getLogger(BPTreeRangeIterator.class) ;
++    
++    public static Iterator<Record> create(BPTreeNode node, Record minRec, Record maxRec) {
++        if ( minRec != null && maxRec != null && Record.keyGE(minRec, maxRec) )
++            return Iter.nullIter();
++        return new BPTreeRangeIterator(node, minRec, maxRec) ;
++    }
++    
++    // Convert path to a stack of iterators
++    private final Deque<Iterator<BPTreePage>> stack = new ArrayDeque<>();
++    final private Record minRecord ;
++    final private Record maxRecord ;
++    private Iterator<Record> current ;
++    private Record slot = null ;
++    private boolean finished = false ;
++    
++    BPTreeRangeIterator(BPTreeNode node, Record minRec, Record maxRec ) {
++        this.minRecord = minRec ;
++        this.maxRecord = maxRec ;
++        BPTreeRecords r = loadStack(node) ;
++        current = getRecordsIterator(r, minRecord, maxRecord) ;
++    }
++
++    @Override
++    public boolean hasNext() {
++        if ( finished ) 
++            return false ;
++        if ( slot != null )
++            return true ;
++        while(current != null && !current.hasNext()) {
++            current = moveOnCurrent() ;
++        } 
++        if ( current == null ) {
++            end() ;
++            return false ;
++        }
++        slot = current.next() ;
++        return true ;
++        
++    }
++    
++    // Move across the head of the stack until empty - then move next level. 
++    private Iterator<Record> moveOnCurrent() {
++        Iterator<BPTreePage> iter = null ;
++        while(!stack.isEmpty()) { 
++            iter = stack.peek() ;
++            if ( iter.hasNext() )
++              break ;
++            stack.pop() ;
++        } 
++        
++        if ( iter == null || ! iter.hasNext() )
++            return null ;
++        BPTreePage p = iter.next() ;
++        BPTreeRecords r = null ;
++        if (p instanceof BPTreeNode) {
++            r = loadStack((BPTreeNode)p) ;
++        }
++        else {
++            r = (BPTreeRecords)p ;
++        }
++        return getRecordsIterator(r, minRecord, maxRecord) ;
++    }
++    
++    // ---- Places we touch blocks. 
++    
++    private static Iterator<Record> getRecordsIterator(BPTreeRecords records, Record minRecord, Record maxRecord) {
++        records.bpTree.startReadBlkMgr();
++        Iterator<Record> iter = records.getRecordBuffer().iterator(minRecord, maxRecord) ;
++        records.bpTree.finishReadBlkMgr();
++        return iter ;
++    }
++    
++    private BPTreeRecords loadStack(BPTreeNode node) {
++        AccessPath path = new AccessPath(null) ;
++        node.bpTree.startReadBlkMgr();
++        
++        if ( minRecord == null )
++            node.internalMinRecord(path) ;
++        else
++            node.internalSearch(path, minRecord) ;
++        List<AccessStep> steps = path.getPath() ;
++        for ( AccessStep step : steps ) {
++            BPTreeNode n = step.node ; 
++            Iterator<BPTreePage> it = n.iterator(minRecord, maxRecord) ;
++            if ( it == null || ! it.hasNext() )
++                continue ;
++            BPTreePage p = it.next() ;
++            stack.push(it) ;
++        }
++        BPTreePage p = steps.get(steps.size()-1).page ;
++        if ( ! ( p instanceof BPTreeRecords ) )
++            throw new InternalErrorException("Last path step not to a records block") ;
++        node.bpTree.finishReadBlkMgr();
++        return (BPTreeRecords)p ;
++    }
++
++    // ---- 
++
++    private void end() {
++        finished = true ;
++        current = null ;
++    }
++    
++    // ---- 
++    
++    public void close() {
++        if ( ! finished )
++            end() ;
++    }
++
++    @Override
++    public Record next() {
++        if ( ! hasNext() )
++            throw new NoSuchElementException() ;
++        Record r = slot ;
++        if ( r == null )
++            throw new InternalErrorException("Null slot after hasNext is true") ;
++        slot = null ;
++        return r ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPTreeRecords.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPTreeRecords.java
index 0000000,0000000..e4e8e76
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPTreeRecords.java
@@@ -1,0 -1,0 +1,388 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.bplustree ;
++
++import static java.lang.String.format ;
++import static org.apache.jena.atlas.lib.Alg.decodeIndex ;
++import static org.seaborne.dboe.trans.bplustree.BPT.CheckingNode ;
++import static org.seaborne.dboe.trans.bplustree.BPT.promotePage ;
++
++import org.apache.jena.atlas.io.IndentedWriter ;
++import org.seaborne.dboe.base.StorageException ;
++import org.seaborne.dboe.base.block.Block ;
++import org.seaborne.dboe.base.block.BlockMgr ;
++import org.seaborne.dboe.base.buffer.RecordBuffer ;
++import org.seaborne.dboe.base.page.Page ;
++import org.seaborne.dboe.base.record.Record ;
++import org.seaborne.dboe.base.recordbuffer.RecordBufferPage ;
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++/**
++ * B+Tree wrapper over a block of records in a RecordBufferPage.
++ * This class adds no persistent state to a RecordBufferPage.
++ */
++public final class BPTreeRecords extends BPTreePage {
++    private static Logger log = LoggerFactory.getLogger(BPTreeRecords.class) ;
++
++    @Override
++    protected Logger getLogger() {
++        return log ;
++    }
++
++    private final RecordBufferPage rBuffPage ;
++    private final BPTreeRecordsMgr bprRecordsMgr ;
++    private RecordBuffer           rBuff ;        // Used heavily. Derived from
++                                                   // rBuffPage
++
++    BPTreeRecords(BPTreeRecordsMgr mgr, RecordBufferPage rbp) {
++        super(mgr.getBPTree()) ;
++        this.bprRecordsMgr = mgr ;
++        rBuffPage = rbp ;
++        rBuff = rBuffPage.getRecordBuffer() ;
++    }
++
++    RecordBufferPage getRecordBufferPage() {
++        return rBuffPage ;
++    }
++
++    RecordBuffer getRecordBuffer() {
++        return rBuff ;
++    }
++
++    public final Record get(int idx) {
++        return rBuff.get(idx) ;
++    }
++
++    @Override
++    public final Block getBackingBlock() {
++        return rBuffPage.getBackingBlock() ;
++    }
++
++    @Override
++    public BlockMgr getBlockMgr() {
++        return bpTree.getRecordsMgr().getBlockMgr() ;
++    }
++
++    @Override
++    public void reset(Block block) {
++        rBuffPage.reset(block) ;
++        rBuff = rBuffPage.getRecordBuffer() ;
++    }
++
++    int getLink() {
++        return rBuffPage.getLink() ;
++    }
++
++    @Override
++    public boolean isFull() {
++        return (rBuff.size() >= rBuff.maxSize()) ;
++    }
++
++    @Override
++    public boolean hasAnyKeys() {
++        return rBuff.size() > 0 ;
++    }
++
++    @Override
++    public boolean isMinSize() {
++        // 50% packing minimum.
++        // If of max length 5 (i.e. odd), min size is 2. Integer division works.
++        return (rBuff.size() <= rBuff.maxSize() / 2) ;
++    }
++
++    @Override
++    Record internalSearch(AccessPath path, Record rec) {
++        int i = rBuff.find(rec) ;
++        if ( i < 0 )
++            return null ;
++        return rBuff.get(i) ;
++    }
++
++    @Override
++    final public void write() {
++        bprRecordsMgr.write(this) ;
++    }
++
++    @Override
++    final boolean promote() {
++        if ( bprRecordsMgr.isWritable(getId()) )
++            return false ;
++        // reset() will be called if necessary.
++        boolean promoteInPlace = (bpTree == null) ? true : bpTree.state().modifiableRecordsBlock(getId()) ;
++
++        if ( promoteInPlace ) {
++            bprRecordsMgr.promoteInPlace(this) ;
++            // TODO Is this needed?
++            // Is this replicated in BPTreeNode?
++            if ( getBackingBlock().isReadOnly() )
++                bprRecordsMgr.getBlockMgr().promote(getBackingBlock()) ;
++            return false ;
++        } else {
++            Block oldBlock = getBackingBlock() ;
++            boolean b = bprRecordsMgr.promoteDuplicate(this) ;
++            if ( b )
++                bprRecordsMgr.getBlockMgr().release(oldBlock) ;
++            return b ;
++        }
++
++    }
++
++    @Override
++    final public void release() {
++        bprRecordsMgr.release(this) ;
++    }
++
++    @Override
++    final public void free() {
++        bprRecordsMgr.free(this) ;
++    }
++
++    @Override
++    Record internalInsert(AccessPath path, Record record) {
++        // Delay promotion until we know change will happen.
++        int i = rBuff.find(record) ;
++        Record r2 = null ;
++        if ( i < 0 ) {
++            i = decodeIndex(i) ;
++            if ( rBuff.size() >= rBuff.maxSize() )
++                throw new StorageException("RecordBlock.put overflow") ;
++            promotePage(path, this) ;
++            rBuff.add(i, record) ;
++        } else {
++            r2 = rBuff.get(i) ;
++            if ( Record.compareByKeyValue(record, r2) != 0 ) {
++                // Replace : return old
++                promotePage(path, this) ;
++                rBuff.set(i, record) ;
++            } else
++                // No promotion, no write
++                return r2 ;
++        }
++        write() ;
++        return r2 ;
++    }
++
++    @Override
++    Record internalDelete(AccessPath path, Record record) {
++        int i = rBuff.find(record) ;
++        if ( i < 0 )
++            return null ;
++        promotePage(path, this) ;
++        Record r2 = rBuff.get(i) ;
++        rBuff.remove(i) ;
++        write() ;
++        return r2 ;
++    }
++
++    @Override
++    public Record getSplitKey() {
++        int splitIdx = rBuff.size() / 2 - 1 ;
++        Record r = rBuff.get(splitIdx) ;
++        return r ;
++    }
++
++    /**
++     * Split: place old high half in 'other'. Return the new (upper)
++     * BPTreeRecords(BPTreePage).
++     * Split is the high end of the low page.
++     */
++    @Override
++    public BPTreePage split() {
++        BPTreeRecords other = insertNewPage() ;
++        int splitIdx = rBuff.size() / 2 - 1 ;
++        Record r = rBuff.get(splitIdx) ; // Only need key for checking later.
++        int moveLen = rBuff.size() - (splitIdx + 1) ; // Number to move.
++        // Copy high end to new.
++        rBuff.copy(splitIdx + 1, other.getRecordBufferPage().getRecordBuffer(), 0, moveLen) ;
++        rBuff.clear(splitIdx + 1, moveLen) ;
++        rBuff.setSize(splitIdx + 1) ;
++
++        if ( CheckingNode ) {
++            if ( !Record.keyEQ(r, maxRecord()) ) {
++                System.err.println(rBuff) ;
++                System.err.println(other.rBuff) ;
++                error("BPTreeRecords.split: Not returning expected record") ;
++            }
++        }
++        return other ;
++    }
++    
++    private BPTreeRecords insertNewPage() {
++        // MVCC - link can not be maintained. 
++        if ( false /* ! bpTree.isTransaction() */ ) {
++            BPTreeRecords other = create(rBuffPage.getLink()) ;
++            rBuffPage.setLink(other.getId()) ;
++            return other ;
++        } 
++        BPTreeRecords other = create(Page.NO_ID) ;
++        rBuffPage.setLink(Page.NO_ID) ;
++        return other ;
++    }
++
++    private BPTreeRecords create(int linkId) {
++        BPTreeRecords newPage = bprRecordsMgr.create() ;
++        newPage.getRecordBufferPage().setLink(linkId) ;
++        return newPage ;
++    }
++
++    @Override
++    public Record shiftRight(BPTreePage other, Record splitKey) {
++        // Error checking by RecordBuffer
++        BPTreeRecords page = cast(other) ;
++        rBuff.shiftRight(page.rBuff) ;
++        if ( rBuff.size() == 0 )
++            return null ;
++        return rBuff.getHigh() ;
++    }
++
++    @Override
++    public Record shiftLeft(BPTreePage other, Record splitKey) {
++        // Error checking by RecordBuffer
++        BPTreeRecords page = cast(other) ;
++        rBuff.shiftLeft(page.rBuff) ;
++        if ( rBuff.size() == 0 )
++            return null ;
++        return rBuff.getHigh() ;
++    }
++
++    @Override
++    BPTreePage merge(BPTreePage right, Record splitKey) {
++        // Split key ignored - it's for the B+Tree case of pushing down a key
++        // Records blocks have all the key/values in them anyway.
++        return merge(this, cast(right)) ;
++    }
++
++    private static BPTreeRecords merge(BPTreeRecords left, BPTreeRecords right) {
++        // Copy right to top of left.
++        // The other way round needs a shift as well.
++        right.rBuff.copyToTop(left.rBuff) ;
++        // Same as: right.rBuff.copy(0, left.rBuff, left.rBuff.size(),
++        // right.rBuff.size()) ;
++        right.rBuff.clear() ;
++
++        // The right page is released by the caller. left is still in use.
++        // So the test code can poke around in the right block after merge.
++        // left.bpTree.getRecordsMgr().release(left.getId()) ;
++
++        // Fix up the link chain.
++        left.rBuffPage.setLink(right.rBuffPage.getLink()) ;
++        return left ;
++    }
++
++    private static BPTreeRecords cast(BPTreePage page) {
++        try {
++            return (BPTreeRecords)page ;
++        }
++        catch (ClassCastException ex) {
++            error("Wrong type: " + page) ;
++            return null ;
++        }
++    }
++
++    @Override
++    final public Record internalMinRecord(AccessPath path) {
++        return getLowRecord() ;
++    }
++
++    @Override
++    final public Record internalMaxRecord(AccessPath path) {
++        return getHighRecord() ;
++    }
++
++    private static void error(String msg, Object... args) {
++        msg = format(msg, args) ;
++        System.out.println(msg) ;
++        System.out.flush() ;
++        throw new BPTreeException(msg) ;
++    }
++
++    @Override
++    public final Record getLowRecord() {
++        if ( rBuff.size() == 0 )
++            return null ;
++        return rBuff.getLow() ;
++    }
++
++    @Override
++    public final Record getHighRecord() {
++        if ( rBuff.size() == 0 )
++            return null ;
++        return rBuff.getHigh() ;
++    }
++
++    @Override
++    public final int getMaxSize() {
++        return rBuff.maxSize() ;
++    }
++
++    @Override
++    public final int getCount() {
++        return rBuff.size() ;
++    }
++
++    @Override
++    public final void setCount(int count) {
++        rBuff.setSize(count) ;
++    }
++
++    @Override
++    public String toString() {
++        return String.format("BPTreeRecords[id=%d, link=%d]: %s", getId(), getLink(), rBuff.toString()) ;
++    }
++
++    @Override
++    protected String typeMark() {
++        return "Data" ;
++    }
++
++    @Override
++    public final void checkNode() {
++        if ( !CheckingNode )
++            return ;
++        if ( rBuff.size() < 0 || rBuff.size() > rBuff.maxSize() )
++            error("Misized: %s", this) ;
++
++        for ( int i = 1 ; i < getCount() ; i++ ) {
++            Record r1 = rBuff.get(i - 1) ;
++            Record r2 = rBuff.get(i) ;
++            if ( Record.keyGT(r1, r2) )
++                error("Not sorted: %s", this) ;
++        }
++    }
++
++    @Override
++    public final void checkNodeDeep() {
++        checkNode() ;
++    }
++
++    @Override
++    public int getId() {
++        return rBuffPage.getId() ;
++    }
++
++    @Override
++    public String getRefStr() {
++        return String.format("BPTRecord[id=%d]", getBackingBlock().getId()) ;
++    }
++
++    @Override
++    public void output(IndentedWriter out) {
++        out.print(toString()) ;
++    }
++}


[45/50] [abbrv] jena git commit: JENA-1396: Merge //github/afs/mantis as subdirectory jena-db/

Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/AbstractTestChannel.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/AbstractTestChannel.java
index 0000000,0000000..ae14bab
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/AbstractTestChannel.java
@@@ -1,0 -1,0 +1,144 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++import java.nio.ByteBuffer ;
++
++import org.junit.Assert ;
++import org.junit.After ;
++import org.junit.Before ;
++import org.junit.Test ;
++import org.seaborne.dboe.base.file.BufferChannel ;
++
++public abstract class AbstractTestChannel extends Assert
++{
++    protected abstract BufferChannel open() ;
++    
++    private BufferChannel store ;
++    @Before public void before() { store = open() ; }
++    @After  public void after()  { store.close() ; }
++    
++    static final int blkSize = 100 ;
++    
++    protected static ByteBuffer data(int len)
++    {
++        ByteBuffer b = ByteBuffer.allocate(len) ;
++        for (int i = 0 ; i < len ; i++ )
++            b.put((byte)(i&0xFF)) ;
++        b.clear() ;
++        return b ;
++    }
++    
++    protected static boolean same(ByteBuffer bb1, ByteBuffer bb2)
++    {
++        if ( bb1.capacity() != bb2.capacity() ) return false ;
++        bb1.clear() ;
++        bb2.clear() ;
++        for ( int i = 0 ; i < bb1.capacity() ; i++ )
++            if ( bb1.get(i) != bb2.get(i) ) return false ;
++        return true ;
++    }
++
++    @Test public void storage_01() 
++    {
++        assertEquals(0, store.size()) ;
++    }
++    
++    @Test public void storage_02()
++    {
++        ByteBuffer b = data(blkSize) ;
++        store.write(b) ;
++        long x = store.size() ;
++        assertEquals(blkSize, x) ;
++    }
++
++    @Test public void storage_03()
++    {
++        ByteBuffer b1 = data(blkSize) ;
++        long posn = store.position() ; 
++        store.write(b1) ;
++        ByteBuffer b9 = ByteBuffer.allocate(blkSize) ;
++        int r = store.read(b9, posn) ;
++        assertEquals(blkSize, r) ;
++        assertTrue(same(b1, b9)) ;
++    }
++    
++    @Test public void storage_04()
++    {
++        ByteBuffer b1 = data(blkSize) ;
++        ByteBuffer b2 = data(blkSize/2) ;
++
++        store.write(b2, 0) ;
++        store.write(b1, 0) ;
++        
++        assertEquals(blkSize, store.size()) ;
++        ByteBuffer b9 = ByteBuffer.allocate(5) ;
++        int z = store.read(b9) ;
++        assertEquals(5, z) ;
++    }
++    
++    @Test public void storage_05()
++    {
++        ByteBuffer b1 = data(blkSize) ;
++        ByteBuffer b1a = ByteBuffer.allocate(blkSize) ;
++        ByteBuffer b2 = data(blkSize/2) ;
++        ByteBuffer b2a = ByteBuffer.allocate(blkSize/2) ;
++        store.write(b1) ;
++        store.write(b2) ;
++        store.position(0) ;
++        store.read(b1a) ;
++        assertTrue(same(b1, b1a)) ;
++        store.read(b2a) ;
++        assertTrue(same(b2, b2a)) ;
++    }
++    
++    @Test public void storage_06()
++    {
++        ByteBuffer b1 = data(blkSize) ;
++        store.write(b1) ;
++        store.truncate(0) ;
++        assertEquals(0, store.size()) ;
++        // Check for:
++        // http://bugs.sun.com/view_bug.do?bug_id=6191269
++        assertEquals(0, store.position()) ;
++    }
++    
++    @Test public void storage_07()
++    {
++        ByteBuffer b1 = data(blkSize) ;
++        store.write(b1) ;
++        store.position(10) ;
++        b1.rewind() ;
++        store.write(b1) ;
++        assertEquals(blkSize+10, store.size()) ;
++    }    
++
++    
++    @Test public void storage_08()
++    {
++        ByteBuffer b1 = data(blkSize) ;
++        ByteBuffer b2 = data(blkSize) ;
++        store.write(b1) ;
++        store.write(b2) ;
++        store.position(10) ;
++        b1.rewind() ;
++        store.write(b1) ;
++        assertEquals(2*blkSize, store.size()) ;
++    }    
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TS_File.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TS_File.java
index 0000000,0000000..368ffb4
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TS_File.java
@@@ -1,0 -1,0 +1,48 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++import org.junit.runner.RunWith ;
++import org.junit.runners.Suite ;
++
++@RunWith(Suite.class)
++@Suite.SuiteClasses( {
++    TestMetaFile.class
++    , TestSegmentedMemBuffer.class
++    , TestChannelMem.class
++    , TestChannelFile.class
++    
++    , TestBlockAccessMem.class
++    , TestBlockAccessByteArray.class
++    , TestBlockAccessDirect.class
++    , TestBlockAccessMapped.class
++    
++    , TestBinaryDataMem.class
++    , TestBinaryDataFileWriteBufferedMem.class
++    , TestBinaryDataRAFInitial.class
++    , TestBinaryDataRAF.class
++    , TestBinaryDataFileWriteBufferedFile.class
++    
++    , TestProcessFileLock.class
++})
++
++
++public class TS_File
++{
++    public static String FILE = "target/test-read-append-file" ;
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestBinaryDataFileWriteBufferedFile.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestBinaryDataFileWriteBufferedFile.java
index 0000000,0000000..50adda1
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestBinaryDataFileWriteBufferedFile.java
@@@ -1,0 -1,0 +1,35 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++import org.apache.jena.atlas.lib.FileOps ;
++import org.seaborne.dboe.base.file.BinaryDataFile ;
++import org.seaborne.dboe.base.file.BinaryDataFileRandomAccess ;
++import org.seaborne.dboe.base.file.BinaryDataFileWriteBuffered ;
++
++public class TestBinaryDataFileWriteBufferedFile extends AbstractTestBinaryDataFile {
++    public static String FILE = TS_File.FILE ;
++
++    @Override
++    protected BinaryDataFile createBinaryDataFile() {
++        FileOps.delete(FILE);
++        BinaryDataFileRandomAccess file = new BinaryDataFileRandomAccess(FILE) ;
++        return new BinaryDataFileWriteBuffered(file) ;
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestBinaryDataFileWriteBufferedMem.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestBinaryDataFileWriteBufferedMem.java
index 0000000,0000000..69c7f77
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestBinaryDataFileWriteBufferedMem.java
@@@ -1,0 -1,0 +1,31 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++import org.seaborne.dboe.base.file.BinaryDataFile ;
++import org.seaborne.dboe.base.file.BinaryDataFileMem ;
++import org.seaborne.dboe.base.file.BinaryDataFileWriteBuffered ;
++
++public class TestBinaryDataFileWriteBufferedMem extends AbstractTestBinaryDataFile {
++    @Override
++    protected BinaryDataFile createBinaryDataFile() {
++        BinaryDataFileMem file = new BinaryDataFileMem() ;
++        return new BinaryDataFileWriteBuffered(file) ;
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestBinaryDataMem.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestBinaryDataMem.java
index 0000000,0000000..c519029
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestBinaryDataMem.java
@@@ -1,0 -1,0 +1,31 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++import org.seaborne.dboe.base.file.BinaryDataFile ;
++import org.seaborne.dboe.base.file.BinaryDataFileMem ;
++
++public class TestBinaryDataMem extends AbstractTestBinaryDataFile {
++
++    @Override
++    protected BinaryDataFile createBinaryDataFile() {
++        return new BinaryDataFileMem() ;
++    }
++
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestBinaryDataRAF.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestBinaryDataRAF.java
index 0000000,0000000..f4ef7fc
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestBinaryDataRAF.java
@@@ -1,0 -1,0 +1,33 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++import org.apache.jena.atlas.lib.FileOps ;
++import org.seaborne.dboe.base.file.BinaryDataFile ;
++import org.seaborne.dboe.base.file.BinaryDataFileRandomAccess ;
++
++public class TestBinaryDataRAF extends AbstractTestBinaryDataFile {
++    public static String FILE = TS_File.FILE ;
++
++    @Override
++    protected BinaryDataFile createBinaryDataFile() {
++        FileOps.delete(FILE);
++        return new BinaryDataFileRandomAccess(FILE) ;
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestBinaryDataRAFInitial.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestBinaryDataRAFInitial.java
index 0000000,0000000..0a572e2
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestBinaryDataRAFInitial.java
@@@ -1,0 -1,0 +1,57 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++import org.apache.jena.atlas.RuntimeIOException ;
++import org.junit.Test ;
++import org.seaborne.dboe.base.file.BinaryDataFileRandomAccess ;
++import static org.junit.Assert.* ;
++
++// Additional tests that do not want the @Before/@After of AbstractTestBinaryDataFile
++public class TestBinaryDataRAFInitial  {
++    public static String FILE = TS_File.FILE ;
++    private BinaryDataFileRandomAccess file ;
++
++    @Test public void open_01() { 
++        file = new BinaryDataFileRandomAccess(FILE) ;
++        assertFalse(file.isOpen()) ;
++        file.open();
++        assertTrue(file.isOpen()) ;
++        file.close() ;
++        assertFalse(file.isOpen()) ;
++    }
++    
++    @Test (expected=RuntimeIOException.class)
++    public void open_02() { 
++        file = new BinaryDataFileRandomAccess(FILE) ;
++        file.open();
++        file.close() ;
++        file.sync(); 
++    }
++    
++    @Test (expected=RuntimeIOException.class)
++    public void open_03() { 
++        file = new BinaryDataFileRandomAccess(FILE) ;
++        file.open();
++        file.close() ;
++        file.truncate(0); 
++    }
++
++    
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestBlockAccessByteArray.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestBlockAccessByteArray.java
index 0000000,0000000..37881d1
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestBlockAccessByteArray.java
@@@ -1,0 -1,0 +1,32 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++import org.seaborne.dboe.base.file.BlockAccess ;
++import org.seaborne.dboe.base.file.BlockAccessByteArray ;
++
++public class TestBlockAccessByteArray extends AbstractTestBlockAccessVarSize
++{
++
++    @Override
++    protected BlockAccess make()
++    {
++        return new BlockAccessByteArray("test") ;
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestBlockAccessDirect.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestBlockAccessDirect.java
index 0000000,0000000..2b0605f
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestBlockAccessDirect.java
@@@ -1,0 -1,0 +1,44 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++import org.apache.jena.atlas.lib.FileOps ;
++import org.junit.AfterClass ;
++import org.seaborne.dboe.ConfigTestDBOE ;
++import org.seaborne.dboe.base.file.BlockAccess ;
++import org.seaborne.dboe.base.file.BlockAccessDirect ;
++
++public class TestBlockAccessDirect extends AbstractTestBlockAccessFixedSize
++{
++    static String filename = ConfigTestDBOE.getTestingDir()+"/test-file-access-direct" ;
++    
++    static final int BlockSize = 50 ;
++    public TestBlockAccessDirect()
++    {
++        super(BlockSize) ;
++    }
++
++    @AfterClass public static void cleanup() { FileOps.deleteSilent(filename) ; } 
++    
++    @Override
++    protected BlockAccess make()
++    {
++        FileOps.deleteSilent(filename) ;
++        return new BlockAccessDirect(filename, BlockSize) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestBlockAccessMapped.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestBlockAccessMapped.java
index 0000000,0000000..4223ebb
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestBlockAccessMapped.java
@@@ -1,0 -1,0 +1,48 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++import org.apache.jena.atlas.lib.FileOps ;
++import org.junit.AfterClass ;
++import org.seaborne.dboe.ConfigTestDBOE ;
++import org.seaborne.dboe.base.file.BlockAccess ;
++import org.seaborne.dboe.base.file.BlockAccessMapped ;
++
++public class TestBlockAccessMapped extends AbstractTestBlockAccessFixedSize
++{
++    static String filename = ConfigTestDBOE.getTestingDir()+"/test-file-access-mapped" ;
++    
++    static final int BlockSize = 64 ;
++    public TestBlockAccessMapped()
++    {
++        super(BlockSize) ;
++    }
++
++    @AfterClass public static void cleanup() { FileOps.deleteSilent(filename) ; } 
++    
++    static int counter = 0 ;
++    
++    @Override
++    protected BlockAccess make()
++    {
++    	String fn = filename + "-"+(counter++) ;
++    	FileOps.deleteSilent(fn) ;
++        return new BlockAccessMapped(fn, BlockSize) ;
++        
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestBlockAccessMem.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestBlockAccessMem.java
index 0000000,0000000..78a219f
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestBlockAccessMem.java
@@@ -1,0 -1,0 +1,37 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++import org.seaborne.dboe.base.file.BlockAccess ;
++import org.seaborne.dboe.base.file.BlockAccessMem ;
++
++public class TestBlockAccessMem extends AbstractTestBlockAccessFixedSize
++{
++    static final int BlockSize = 50 ;
++    public TestBlockAccessMem()
++    {
++        super(BlockSize) ;
++    }
++
++    @Override
++    protected BlockAccess make()
++    {
++        return new BlockAccessMem("test", BlockSize) ;
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestChannelFile.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestChannelFile.java
index 0000000,0000000..b99dac9
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestChannelFile.java
@@@ -1,0 -1,0 +1,38 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++import org.apache.jena.atlas.lib.FileOps ;
++import org.junit.AfterClass ;
++import org.seaborne.dboe.ConfigTestDBOE ;
++import org.seaborne.dboe.base.file.BufferChannel ;
++import org.seaborne.dboe.base.file.BufferChannelFile ;
++
++public class TestChannelFile extends AbstractTestChannel
++{
++    static String filename = ConfigTestDBOE.getTestingDir()+"/test-storage" ;
++
++    @AfterClass public static void cleanup() { FileOps.deleteSilent(filename) ; } 
++    
++    @Override
++    protected BufferChannel open()
++    {
++        FileOps.deleteSilent(filename) ;
++        return BufferChannelFile.create(filename) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestChannelMem.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestChannelMem.java
index 0000000,0000000..1fb079d
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestChannelMem.java
@@@ -1,0 -1,0 +1,34 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++import org.seaborne.dboe.base.file.BufferChannel ;
++import org.seaborne.dboe.base.file.BufferChannelMem ;
++
++
++public class TestChannelMem extends AbstractTestChannel
++{
++    static int counter = 0 ;
++    
++    @Override
++    protected BufferChannel open()
++    {
++        return BufferChannelMem.create("Test-"+(counter++)) ;
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestMetaFile.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestMetaFile.java
index 0000000,0000000..c1427d1
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestMetaFile.java
@@@ -1,0 -1,0 +1,74 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++import java.io.File ;
++
++import org.junit.Assert ;
++import org.junit.After ;
++import org.junit.Before ;
++import org.junit.Test ;
++import org.seaborne.dboe.ConfigTestDBOE ;
++import org.seaborne.dboe.base.file.MetaFile ;
++import org.seaborne.dboe.sys.Names ;
++
++public class TestMetaFile extends Assert
++{
++    String testfile = null ;
++    String testfileMeta = null ;
++    
++    @Before public void before()
++    {
++        testfile = ConfigTestDBOE.getTestingDir()+"/file" ;
++        testfileMeta = ConfigTestDBOE.getTestingDir()+"/file."+Names.extMeta ;
++        File f = new File(testfileMeta) ;
++        f.delete() ;
++    }
++    
++    @Test public void meta1()
++    {
++        clear() ;
++        MetaFile f = new MetaFile("META", testfile) ;
++        assertFalse(new File(testfileMeta).exists()) ;
++        f.setProperty("key", "value") ;
++        f.flush() ;
++        assertTrue(new File(f.getFilename()).exists()) ;
++    }
++    
++    @Test public void meta2()
++    {
++        clear() ;
++        MetaFile f = new MetaFile("META", testfile) ;
++        f.setProperty("test.value1", "1") ;
++        f.flush();
++        MetaFile f2 = new MetaFile("META", testfile) ;
++        assertEquals("1", f2.getProperty("test.value1")) ;
++        assertNull(f2.getProperty("test.value.other")) ;
++    }
++
++    // Test MetaBase
++    
++    @After public void afterClass()
++    { clear() ; }
++    
++    private void clear()
++    {
++        File f = new File(testfileMeta) ;
++        f.delete() ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestProcessFileLock.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestProcessFileLock.java
index 0000000,0000000..39b0652
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestProcessFileLock.java
@@@ -1,0 -1,0 +1,107 @@@
++/*
++ * 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.seaborne.dboe.base.file;
++
++import static org.junit.Assert.*;
++import static org.junit.Assert.assertFalse;
++import static org.junit.Assert.assertNotSame;
++import static org.junit.Assert.assertSame;
++
++import java.io.File;
++import java.io.IOException;
++
++import org.apache.jena.atlas.lib.FileOps;
++import org.junit.Before;
++import org.junit.BeforeClass;
++import org.junit.Test;
++import org.seaborne.dboe.base.file.ProcessFileLock;
++import org.seaborne.dboe.sys.Names;
++
++public class TestProcessFileLock {
++    // TestLocationLockStoreConnection
++    
++    private static final String DIR  = "target/locktest";
++    private static final String lockfile = DIR+"/"+Names.TDB_LOCK_FILE;
++    
++    
++    @BeforeClass public static void beforeClass() {
++        FileOps.ensureDir(DIR);
++    }
++    
++    @Before public void beforeTest() {
++        File f = new File(lockfile);
++        try {
++            f.delete();
++            f.createNewFile();
++        }
++        catch (IOException e) {
++            e.printStackTrace();
++        }
++    }
++
++    @Test public void process_lock_1() {
++        ProcessFileLock lock = ProcessFileLock.create(lockfile);
++        String fn = new File(lockfile).getAbsolutePath();
++        assertEquals(fn, lock.getPath().toString());
++    }
++    
++    @Test public void process_lock_2() {
++        ProcessFileLock lock1 = ProcessFileLock.create(lockfile);
++        ProcessFileLock lock2 = ProcessFileLock.create(lockfile);
++        assertSame(lock1, lock2);
++    }
++
++    @Test public void process_lock_3() {
++        ProcessFileLock lock1 = ProcessFileLock.create(lockfile);
++        ProcessFileLock.release(lock1);
++        ProcessFileLock lock2 = ProcessFileLock.create(lockfile);
++        assertNotSame(lock1, lock2);
++    }
++    
++    @Test public void process_lock_4() {
++        ProcessFileLock lock = ProcessFileLock.create(lockfile);
++        assertFalse(lock.isLockedHere());
++        lock.lockEx();
++        assertTrue(lock.isLockedHere());
++        lock.unlock();
++        assertFalse(lock.isLockedHere());
++    }
++
++    @Test(expected=AlreadyLocked.class)
++    public void process_lock_5() {
++        ProcessFileLock lock = ProcessFileLock.create(lockfile);
++        lock.lockEx();
++        lock.lockEx();
++    }
++    
++    @Test(expected=AlreadyLocked.class)
++    public void process_lock_6() {
++        ProcessFileLock lock = ProcessFileLock.create(lockfile);
++        lock.lockEx();
++        boolean b = lock.tryLock();
++        assertFalse(b);
++    }
++
++    @Test(expected=AlreadyLocked.class)
++    public void process_lock_7() {
++        ProcessFileLock lock = ProcessFileLock.create(lockfile);
++        lock.tryLock();
++        lock.tryLock();
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestSegmentedMemBuffer.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestSegmentedMemBuffer.java
index 0000000,0000000..c11cd75
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/file/TestSegmentedMemBuffer.java
@@@ -1,0 -1,0 +1,158 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.file;
++
++import java.nio.ByteBuffer ;
++import java.util.Arrays ;
++
++import org.junit.Assert ;
++import org.junit.Test ;
++
++public class TestSegmentedMemBuffer extends Assert {
++    private static byte[] data1 = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } ;  
++    private static byte[] data2 = { 10,11,12 } ;
++    
++    @Test public void membuffer_00() {
++        SegmentedMemBuffer space = new SegmentedMemBuffer() ;
++        assertTrue(space.isOpen()) ;
++        assertEquals(0, space.length()) ;
++        space.close() ;
++        assertFalse(space.isOpen()) ;
++        space.close() ;
++    }
++    
++    @Test public void membuffer_01() {
++        SegmentedMemBuffer space = new SegmentedMemBuffer() ;
++        writeread1(space) ;
++    }
++    
++    @Test public void membuffer_02() {
++        SegmentedMemBuffer space = new SegmentedMemBuffer() ;
++        writeread2(space) ;
++    }
++
++    @Test public void membuffer_03() {
++        SegmentedMemBuffer space = new SegmentedMemBuffer() ;
++        writeread1(space) ;
++        space.truncate(0); 
++        writeread2(space) ;
++    }
++
++    @Test public void membuffer_11() {
++        SegmentedMemBuffer space = new SegmentedMemBuffer(2) ;
++        writeread1(space) ;
++    }
++    
++    @Test public void membuffer_12() {
++        SegmentedMemBuffer space = new SegmentedMemBuffer(2) ;
++        writeread2(space) ;
++    }
++
++    @Test public void membuffer_13() {
++        SegmentedMemBuffer space = new SegmentedMemBuffer(2) ;
++        writeread1(space) ;
++        space.truncate(0); 
++        writeread2(space) ;
++    }
++
++    @Test public void membuffer_21() {
++        SegmentedMemBuffer space = new SegmentedMemBuffer() ;
++        writeread1a(space) ;
++    }
++    
++    @Test public void membuffer_22() {
++        SegmentedMemBuffer space = new SegmentedMemBuffer() ;
++        writeread2a(space) ;
++    }
++
++    @Test public void membuffer_23() {
++        SegmentedMemBuffer space = new SegmentedMemBuffer() ;
++        writeread1(space) ;
++        space.truncate(0); 
++        writeread2a(space) ;
++    }
++
++    @Test public void membuffer_31() {
++        SegmentedMemBuffer space = new SegmentedMemBuffer(2) ;
++        writeread1a(space) ;
++    }
++    
++    @Test public void membuffer_32() {
++        SegmentedMemBuffer space = new SegmentedMemBuffer(2) ;
++        writeread2(space) ;
++    }
++
++    @Test public void membuffer_33() {
++        SegmentedMemBuffer space = new SegmentedMemBuffer(2) ;
++        writeread1a(space) ;
++        space.truncate(0); 
++        writeread2a(space) ;
++    }
++
++    private void writeread1(SegmentedMemBuffer space) {
++        long x = space.length() ;
++        space.write(x, data1) ;
++        assertEquals(x+data1.length, space.length()) ;
++        byte[] bytes2 = new byte[data1.length+10] ;
++        int y = space.read(x, bytes2) ;
++        assertEquals(data1.length, y) ;
++        byte[] bytes3 = Arrays.copyOf(bytes2, y) ;
++        assertArrayEquals(data1, bytes3);
++    }
++    
++    private void writeread2(SegmentedMemBuffer space) {
++        // Offset.
++        space.write(0, data2) ;
++        long x = data2.length ;
++        space.write(x, data1) ;
++        assertEquals(x+data1.length, space.length()) ;
++        byte[] bytes2 = new byte[data1.length+10] ;
++        int y = space.read(x, bytes2) ;
++        assertEquals(data1.length, y) ;
++        byte[] bytes3 = Arrays.copyOf(bytes2, y) ;
++        assertArrayEquals(data1, bytes3);
++    }
++
++    private void writeread1a(SegmentedMemBuffer space) {
++        long x = space.length() ;
++        ByteBuffer bb1 = ByteBuffer.wrap(data1) ;
++        space.write(x, bb1) ;
++        assertEquals(x+data1.length, space.length()) ;
++        ByteBuffer bb2 = ByteBuffer.allocate(data1.length) ;
++        int y = space.read(x, bb2) ;
++        assertEquals(data1.length, y) ;
++        byte[] bytes3 = Arrays.copyOf(bb2.array(), y) ;
++        assertArrayEquals(data1, bytes3);
++    }
++    
++    private void writeread2a(SegmentedMemBuffer space) {
++        // Offset.
++        space.write(0,  ByteBuffer.wrap(data2)) ;
++        long x = data2.length ;
++        ByteBuffer bb1 = ByteBuffer.wrap(data1) ;
++        space.write(x, bb1) ;
++        assertEquals(x+data1.length, space.length()) ;
++        ByteBuffer bb2 = ByteBuffer.allocate(data1.length) ;
++        int y = space.read(x, bb2) ;
++        assertEquals(data1.length, y) ;
++        byte[] bytes3 = Arrays.copyOf(bb2.array(), y) ;
++        assertArrayEquals(data1, bytes3);
++    }
++
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/record/TS_Record.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/record/TS_Record.java
index 0000000,0000000..b33eca8
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/record/TS_Record.java
@@@ -1,0 -1,0 +1,34 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.record;
++
++import org.junit.runner.RunWith;
++import org.junit.runners.Suite;
++import org.seaborne.dboe.base.buffer.TestPtrBuffer ;
++import org.seaborne.dboe.base.buffer.TestRecordBuffer ;
++
++@RunWith(Suite.class)
++@Suite.SuiteClasses( {
++    TestRecord.class ,
++    TestPtrBuffer.class ,
++    TestRecordBuffer.class 
++})
++
++
++public class TS_Record
++{}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/record/TestRecord.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/record/TestRecord.java
index 0000000,0000000..a1e14bf
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/record/TestRecord.java
@@@ -1,0 -1,0 +1,77 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.record;
++
++import static org.seaborne.dboe.test.RecordLib.intToRecord ;
++import static org.seaborne.dboe.test.RecordLib.recordToInt ;
++import org.junit.Assert ;
++import org.junit.Test;
++import org.seaborne.dboe.base.record.Record ;
++
++public class TestRecord extends Assert
++{
++    static final public int RecLen = 4 ;
++    
++    @Test public void int1()
++    {
++        Record r = intToRecord(1234, 4) ;
++        int v = recordToInt(r) ;
++        assertEquals(v , 1234) ;
++    }
++    
++    @Test public void int2()
++    {
++        // Negative numbers only work for length 4.
++        Record r = intToRecord(-99, 4) ;
++        int v = recordToInt(r) ;
++        assertEquals(v , -99) ;
++    }
++    
++    @Test public void record1()
++    {
++        Record r1 = intToRecord(1, RecLen) ;
++        Record r2 = intToRecord(1, RecLen) ;
++        assertTrue(Record.keyEQ(r1,r2)) ;
++        assertTrue(Record.keyGE(r1,r2)) ;
++        assertTrue(Record.keyLE(r1,r2)) ;
++        assertFalse(Record.keyLT(r1,r2)) ;
++        assertFalse(Record.keyGT(r1,r2)) ;
++    }
++    
++    @Test public void record2()
++    {
++        Record r1 = intToRecord(1000, RecLen) ;
++        Record r2 = intToRecord(2222, RecLen) ;
++        assertFalse(Record.keyEQ(r1,r2)) ;
++        assertFalse(Record.keyGE(r1,r2)) ;
++        assertTrue(Record.keyLE(r1,r2)) ;
++        assertTrue(Record.keyLT(r1,r2)) ;
++        assertFalse(Record.keyGT(r1,r2)) ;
++    }
++
++    @Test public void record3()
++    {
++        Record r1 = intToRecord(1000, RecLen)  ;
++        Record r2 = intToRecord(0, RecLen) ;
++        assertFalse(Record.keyEQ(r1,r2)) ;
++        assertTrue(Record.keyGE(r1,r2)) ;
++        assertFalse(Record.keyLE(r1,r2)) ;
++        assertFalse(Record.keyLT(r1,r2)) ;
++        assertTrue(Record.keyGT(r1,r2)) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/recordfile/TS_RecordFile.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/recordfile/TS_RecordFile.java
index 0000000,0000000..34821f1
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/recordfile/TS_RecordFile.java
@@@ -1,0 -1,0 +1,31 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.recordfile;
++
++import org.junit.runner.RunWith;
++import org.junit.runners.Suite;
++
++@RunWith(Suite.class)
++@Suite.SuiteClasses( {
++    TestRecordBufferPage.class
++})
++
++public class TS_RecordFile
++{
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/recordfile/TestRecordBufferPage.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/recordfile/TestRecordBufferPage.java
index 0000000,0000000..d801e73
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/java/org/seaborne/dboe/base/recordfile/TestRecordBufferPage.java
@@@ -1,0 -1,0 +1,124 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.base.recordfile;
++
++import org.junit.Assert ;
++import org.junit.AfterClass ;
++import org.junit.BeforeClass ;
++import org.junit.Test ;
++import org.seaborne.dboe.base.block.BlockMgr ;
++import org.seaborne.dboe.base.block.BlockMgrFactory ;
++import org.seaborne.dboe.base.buffer.RecordBuffer ;
++import org.seaborne.dboe.base.record.Record ;
++import org.seaborne.dboe.base.record.RecordFactory ;
++import org.seaborne.dboe.base.recordbuffer.RecordBufferPage ;
++import org.seaborne.dboe.base.recordbuffer.RecordBufferPageMgr ;
++import org.seaborne.dboe.sys.SystemIndex ;
++
++public class TestRecordBufferPage extends Assert
++{
++    // Testing: records are 2 bytes, 3 records per block.  
++    
++    static final int TestRecordSize = 2 ;           // Size, in bytes.
++    static final int TestNumRecord  = 3 ;           // Size, in bytes.
++    static RecordFactory factory = new RecordFactory(2, 0) ; 
++    
++    static boolean originalNullOut ; 
++    @BeforeClass static public void beforeClass()
++    {
++        originalNullOut = SystemIndex.getNullOut() ;
++        SystemIndex.setNullOut(true) ;    
++    }
++    
++    @AfterClass static public void afterClass()
++    {
++        SystemIndex.setNullOut(originalNullOut) ;    
++    }
++
++    @Test public void recBufferPage01()
++    {
++        BlockMgr blkMgr = makeBlockMgr() ;
++        blkMgr.beginUpdate() ;
++        RecordBufferPageMgr rpm = new RecordBufferPageMgr(factory, blkMgr) ;
++        RecordBufferPage page = rpm.create() ;
++        fill(page.getRecordBuffer(), 10, 20, 30) ;
++        assertEquals(10, get(page, 0)) ;
++        assertEquals(20, get(page, 1)) ;
++        assertEquals(30, get(page, 2)) ;
++        rpm.release(page) ;
++        blkMgr.endUpdate() ;
++    }
++    
++    @Test public void recBufferPage02()
++    {
++        BlockMgr blkMgr = makeBlockMgr() ;
++        blkMgr.beginUpdate() ;
++        RecordBufferPageMgr rpm = new RecordBufferPageMgr(factory, blkMgr) ;
++        int x = -99 ;
++        {
++            RecordBufferPage page1 = rpm.create() ;
++            fill(page1.getRecordBuffer(), 10, 20, 30) ;
++            x = page1.getId() ;
++            rpm.put(page1) ;
++            page1 = null ;
++        }
++        blkMgr.endUpdate() ;
++        blkMgr.beginRead() ;
++        {
++            RecordBufferPage page2 = rpm.getRead(x) ;
++            assertEquals(10, get(page2, 0)) ;
++            assertEquals(20, get(page2, 1)) ;
++            assertEquals(30, get(page2, 2)) ;
++            rpm.release(page2) ;
++        }
++        blkMgr.endRead() ;
++    }
++
++    
++    private static void fill(RecordBuffer rb, int ... nums)
++    {
++        for ( int num : nums )
++        {
++            Record rec = record( num );
++            rb.add( rec );
++        }
++    }
++    
++    private static int get(RecordBufferPage rbp, int idx) { return get(rbp.getRecordBuffer(), idx) ; } 
++    
++    private static int get(RecordBuffer rb, int idx) 
++    {
++        Record r = rb.get(idx) ;
++        int v = (r.getKey()[0])<<8 | ((r.getKey()[1])&0xFF) ;
++        return v ;
++    }
++    
++    private static Record record(int i)
++    {
++        byte b[] = new byte[]{ 
++            (byte)((i>>8)&0xFF),
++            (byte)(i&0xFF)} ; 
++        Record r = factory.create(b) ;
++        return r ;
++    }
++
++    private static BlockMgr makeBlockMgr()
++    {
++        return BlockMgrFactory.createMem("RecordBuffer", RecordBufferPage.calcBlockSize(factory, TestNumRecord)) ; 
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-base/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --cc jena-db/dboe-base/src/test/resources/log4j.properties
index 0000000,0000000..d6b299a
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-base/src/test/resources/log4j.properties
@@@ -1,0 -1,0 +1,6 @@@
++log4j.rootLogger=INFO, stdlog
++
++log4j.appender.stdlog=org.apache.log4j.ConsoleAppender
++## log4j.appender.stdlog.target=System.err
++log4j.appender.stdlog.layout=org.apache.log4j.PatternLayout
++log4j.appender.stdlog.layout.ConversionPattern=%d{HH:mm:ss} %-5p %-20c{1} :: %m%n

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-index-test/.gitignore
----------------------------------------------------------------------
diff --cc jena-db/dboe-index-test/.gitignore
index 0000000,0000000..b83d222
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-index-test/.gitignore
@@@ -1,0 -1,0 +1,1 @@@
++/target/

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-index-test/README.md
----------------------------------------------------------------------
diff --cc jena-db/dboe-index-test/README.md
index 0000000,0000000..bae93dd
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-index-test/README.md
@@@ -1,0 -1,0 +1,1 @@@
++Test support for dboe-index code.

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-index-test/pom.xml
----------------------------------------------------------------------
diff --cc jena-db/dboe-index-test/pom.xml
index 0000000,0000000..b06a773
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-index-test/pom.xml
@@@ -1,0 -1,0 +1,54 @@@
++<?xml version="1.0" encoding="utf-8"?>
++<!--
++   Licensed 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.
++ 
++   See the NOTICE file distributed with this work for additional
++   information regarding copyright ownership.
++-->
++
++<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
++  <modelVersion>4.0.0</modelVersion>
++
++  <artifactId>dboe-index-test</artifactId>
++  <packaging>jar</packaging>
++  <name>Mantis (${project.artifactId})</name>
++
++  <parent>
++    <groupId>org.seaborne.mantis</groupId>
++    <artifactId>mantis</artifactId>
++    <version>0.3.1-SNAPSHOT</version>
++  </parent> 
++
++  <dependencies>
++
++    <dependency>
++      <groupId>org.seaborne.mantis</groupId>
++      <artifactId>dboe-index</artifactId>
++      <version>0.3.1-SNAPSHOT</version>
++    </dependency>
++
++    <dependency>
++      <groupId>org.apache.jena</groupId>
++      <artifactId>jena-cmds</artifactId>
++    </dependency>
++
++    <dependency>
++      <!--  Bring junit into scope-compile -->
++      <groupId>junit</groupId>
++      <artifactId>junit</artifactId>
++      <scope>compile</scope>
++    </dependency>
++
++  </dependencies>
++
++</project>

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-index-test/src/main/java/org/seaborne/dboe/index/TS_Index.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-index-test/src/main/java/org/seaborne/dboe/index/TS_Index.java
index 0000000,0000000..0fe386d
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-index-test/src/main/java/org/seaborne/dboe/index/TS_Index.java
@@@ -1,0 -1,0 +1,29 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.index;
++
++import org.junit.runner.RunWith ;
++import org.junit.runners.Suite ;
++
++@RunWith(Suite.class)
++@Suite.SuiteClasses( {
++    TestIndexMem.class
++} )
++
++public class TS_Index
++{ }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-index-test/src/main/java/org/seaborne/dboe/index/TestIndexMem.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-index-test/src/main/java/org/seaborne/dboe/index/TestIndexMem.java
index 0000000,0000000..78f4009
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-index-test/src/main/java/org/seaborne/dboe/index/TestIndexMem.java
@@@ -1,0 -1,0 +1,34 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.index;
++
++import org.seaborne.dboe.base.record.RecordFactory ;
++import org.seaborne.dboe.index.Index ;
++import org.seaborne.dboe.index.IndexMap ;
++import org.seaborne.dboe.index.test.AbstractTestIndex ;
++
++public class TestIndexMem extends AbstractTestIndex
++{
++    @Override
++    protected Index makeIndex(int kLen, int vLen)
++    {
++        RecordFactory rf = new RecordFactory(kLen, vLen) ;
++        return new IndexMap(rf) ;
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-index-test/src/main/java/org/seaborne/dboe/index/test/AbstractTestIndex.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-index-test/src/main/java/org/seaborne/dboe/index/test/AbstractTestIndex.java
index 0000000,0000000..75efad7
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-index-test/src/main/java/org/seaborne/dboe/index/test/AbstractTestIndex.java
@@@ -1,0 -1,0 +1,159 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.index.test;
++
++import static org.seaborne.dboe.index.test.IndexTestLib.* ;
++import static org.seaborne.dboe.test.RecordLib.intToRecord ;
++import org.junit.Assert ;
++import org.junit.After;
++import org.junit.Test;
++import org.seaborne.dboe.base.record.Record ;
++import org.seaborne.dboe.index.Index ;
++import org.seaborne.dboe.test.RecordLib ;
++
++//import org.apache.jena.tdb.base.record.RecordLib;
++
++public abstract class AbstractTestIndex extends Assert 
++{
++    Index index = null ;
++    
++    @After public void afterTest()
++    { 
++        if ( index != null )
++            index.close();
++        index = null ;
++    }
++    
++    // ---- Overridable maker
++    protected abstract Index makeIndex(int kLen, int vLen) ;
++    
++    @Test public void __basic() {
++        makeIndex(4,0) ;
++    }
++    
++    @Test public void index_ins_0()
++    {
++        // Empty tree
++        int[] keys = {};
++        test(keys) ;
++    }
++    
++    @Test public void index_ins_1()
++    {
++        int[] keys = {1};
++        test(keys) ;
++    }
++    
++    @Test public void index_ins_2()
++    {
++        int[] keys = new int[20] ; 
++        for ( int i = 0 ; i < keys.length ; i++ )
++            keys[i] = i ;
++        test(keys) ;
++    }
++    
++    @Test public void index_ins_3()
++    {
++        int[] keys = new int[20] ; 
++        for ( int i = keys.length-1 ; i >= 0 ; i-- )
++            keys[i] = i ;
++        test(keys) ;
++    }
++
++    @Test public void index_ins_4()
++    {
++        int[] keys = new int[10] ; 
++        for ( int i = 0 ; i < keys.length ; i++ )
++            keys[i] = 1<<i ;
++        test(keys) ;
++    }
++    
++    @Test public void index_ins_5()
++    {
++        int[] keys = new int[10] ; 
++        for ( int i = keys.length-1 ; i >= 0 ; i-- )
++            keys[i] = 1<<i ;
++        test(keys) ;
++    }
++    
++    @Test public void index_find_1()
++    {
++        int[] keys = {1};
++        Index index = test(keys) ;
++        Record r = intToRecord(1, RecordLib.TestRecordLength) ;
++        r = index.find(r) ;
++        assertNotNull(r) ;
++    }
++    
++    @Test public void index_find_2()
++    {
++        int[] keys = {1,2,3,4,5,6,7,8,9};
++        Index index = test(keys) ;
++        Record r = intToRecord(20, RecordLib.TestRecordLength) ;
++        r = index.find(r) ;
++        assertNull(r) ;
++    }
++    
++    @Test public void index_del_1()
++    {
++        int[] keys1 = {0, 1, 2};
++        int[] keys2 = {0, 1, 2};
++        int[] keys3 = {} ;
++        test(keys1, keys2, keys3) ;
++    }
++
++    @Test public void index_del_2()
++    {
++        int[] keys1 = {0, 1, 2};
++        int[] keys2 = {0, 1};
++        int[] keys3 = {2} ;
++        test(keys1, keys2, keys3) ;
++    }
++
++    @Test public void index_del_3()
++    {
++        int[] keys1 = {0, 1, 2};
++        int[] keys2 = {0, 99};
++        int[] keys3 = {2, 1} ;
++        test(keys1, keys2, keys3) ;
++    }
++
++    private Index test(int[] insKeys, int[] delKeys, int[] expected)
++    {
++        index = makeIndex(4,0) ;
++        testInsert(index, insKeys) ;
++        long x = index.size() ;
++        if ( x >= 0 )
++            assertEquals(insKeys.length, x) ;
++        
++        if ( delKeys != null )
++        {
++            testDelete(index, delKeys) ;
++            
++        }
++        
++        if ( expected != null ) 
++            testIndexContents(index, expected) ;
++        return index ;
++    }
++
++    private Index test(int[] keys)
++    {
++        return test(keys, null, keys) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-index-test/src/main/java/org/seaborne/dboe/index/test/AbstractTestRangeIndex.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-index-test/src/main/java/org/seaborne/dboe/index/test/AbstractTestRangeIndex.java
index 0000000,0000000..ab3f82c
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-index-test/src/main/java/org/seaborne/dboe/index/test/AbstractTestRangeIndex.java
@@@ -1,0 -1,0 +1,434 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.index.test ;
++
++import static org.seaborne.dboe.index.test.IndexTestLib.add ;
++import static org.seaborne.dboe.index.test.IndexTestLib.randTest ;
++import static org.seaborne.dboe.index.test.IndexTestLib.testInsert ;
++import static org.seaborne.dboe.index.test.IndexTestLib.testInsertDelete ;
++import static org.seaborne.dboe.test.RecordLib.intToRecord ;
++import static org.seaborne.dboe.test.RecordLib.r ;
++import static org.seaborne.dboe.test.RecordLib.toIntList ;
++
++import java.util.Iterator ;
++import java.util.List ;
++
++import org.junit.Assert ;
++import org.junit.Test ;
++import org.seaborne.dboe.base.record.Record ;
++import org.seaborne.dboe.index.RangeIndex ;
++
++public abstract class AbstractTestRangeIndex extends Assert {
++    // ---- Overridable maker
++    protected RangeIndex makeRangeIndex(int order) {
++        return makeRangeIndex(order, order) ;
++    }
++
++    protected abstract RangeIndex makeRangeIndex(int order, int minRecords) ;
++
++    @Test public void __basic() {
++         makeRangeIndex(4,0) ;
++    }
++    
++    @Test
++    public void tree_ins_0_0() {
++        // Empty tree
++        int[] keys = {} ;
++        RangeIndex rIndex = makeRangeIndex(2) ;
++        testInsert(rIndex, keys) ;
++        assertTrue(rIndex.isEmpty()) ;
++        assertNull(rIndex.minKey()) ;
++        assertNull(rIndex.maxKey()) ;
++    }
++
++    @Test
++    public void tree_ins_0_1() {
++        int[] keys = {0, 1, 2} ;
++        RangeIndex rIndex = makeRangeIndex(2) ;
++        testInsert(rIndex, keys) ;
++        assertFalse(rIndex.isEmpty()) ;
++        assertEquals(0, r(rIndex.minKey())) ;
++        assertEquals(2, r(rIndex.maxKey())) ;
++    }
++
++    @Test
++    public void tree_ins_2_01() {
++        int[] keys = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} ;
++        RangeIndex rIndex = makeRangeIndex(2) ;
++        testInsert(rIndex, keys) ;
++        assertEquals(0, r(rIndex.minKey())) ;
++        assertEquals(9, r(rIndex.maxKey())) ;
++    }
++
++    @Test
++    public void tree_ins_2_02() {
++        int[] keys = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0} ;
++        RangeIndex rIndex = makeRangeIndex(2) ;
++        testInsert(rIndex, keys) ;
++        assertEquals(0, r(rIndex.minKey())) ;
++        assertEquals(9, r(rIndex.maxKey())) ;
++    }
++
++    @Test
++    public void tree_ins_2_03() {
++        int[] keys = {0, 2, 4, 6, 8, 1, 3, 5, 7, 9} ;
++        RangeIndex rIndex = makeRangeIndex(2) ;
++        testInsert(rIndex, keys) ;
++        assertEquals(0, r(rIndex.minKey())) ;
++        assertEquals(9, r(rIndex.maxKey())) ;
++    }
++
++    @Test
++    public void tree_ins_2_04() {
++        int[] keys = {0, 9, 2, 7, 4, 5, 6, 3, 8, 1} ;
++        RangeIndex rIndex = makeRangeIndex(2) ;
++        testInsert(rIndex, keys) ;
++        assertEquals(0, r(rIndex.minKey())) ;
++        assertEquals(9, r(rIndex.maxKey())) ;
++    }
++
++    @Test
++    public void tree_ins_2_05() {
++        int[] keys = {0, 18, 4, 14, 8, 10, 12, 6, 16, 2} ;
++        RangeIndex rIndex = makeRangeIndex(2) ;
++        testInsert(rIndex, keys) ;
++        assertFalse(rIndex.contains(r(1))) ;
++        assertFalse(rIndex.contains(r(999))) ;
++        assertFalse(rIndex.contains(r(-9))) ;
++        assertFalse(rIndex.contains(r(7))) ;
++        assertEquals(0, r(rIndex.minKey())) ;
++        assertEquals(18, r(rIndex.maxKey())) ;
++    }
++
++    @Test
++    public void tree_del_0_1() {
++        int[] keys1 = {0, 1, 2} ;
++        int[] keys2 = {0, 1, 2} ;
++        RangeIndex rIndex = makeRangeIndex(2) ;
++        testInsertDelete(rIndex, keys1, keys2) ;
++    }
++
++    @Test
++    public void tree_del_0_2() {
++        int[] keys1 = {0, 1, 2} ;
++        int[] keys2 = {2, 1, 0} ;
++        RangeIndex rIndex = makeRangeIndex(2) ;
++        testInsertDelete(rIndex, keys1, keys2) ;
++    }
++
++    @Test
++    public void tree_del_0_3() {
++        int[] keys1 = {0, 1, 2} ;
++        int[] keys2 = {1, 0, 2} ;
++        RangeIndex rIndex = makeRangeIndex(2) ;
++        testInsertDelete(rIndex, keys1, keys2) ;
++    }
++
++    @Test
++    public void tree_del_2_01() {
++        int[] keys1 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} ;
++        int[] keys2 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} ;
++        RangeIndex rIndex = makeRangeIndex(2) ;
++        testInsertDelete(rIndex, keys1, keys2) ;
++    }
++
++    @Test
++    public void tree_del_2_02() {
++        int[] keys1 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} ;
++        int[] keys2 = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0} ;
++        RangeIndex rIndex = makeRangeIndex(2) ;
++        testInsertDelete(rIndex, keys1, keys2) ;
++    }
++
++    @Test
++    public void tree_del_2_03() {
++        int[] keys1 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} ;
++        int[] keys2 = {0, 2, 4, 6, 8, 1, 3, 5, 7, 9} ;
++        RangeIndex rIndex = makeRangeIndex(2) ;
++        testInsertDelete(rIndex, keys1, keys2) ;
++    }
++
++    @Test
++    public void tree_del_2_04() {
++        int[] keys1 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} ;
++        int[] keys2 = {0, 9, 2, 7, 4, 5, 6, 3, 8, 1} ;
++        RangeIndex rIndex = makeRangeIndex(2) ;
++        testInsertDelete(rIndex, keys1, keys2) ;
++    }
++
++    @Test
++    public void tree_del_2_05() {
++        int[] keys1 = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0} ;
++        int[] keys2 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} ;
++        RangeIndex rIndex = makeRangeIndex(2) ;
++        testInsertDelete(rIndex, keys1, keys2) ;
++    }
++
++    @Test
++    public void tree_del_2_06() {
++        int[] keys1 = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0} ;
++        int[] keys2 = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0} ;
++        RangeIndex rIndex = makeRangeIndex(2) ;
++        testInsertDelete(rIndex, keys1, keys2) ;
++    }
++
++    @Test
++    public void tree_del_2_07() {
++        int[] keys1 = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0} ;
++        int[] keys2 = {0, 2, 4, 6, 8, 1, 3, 5, 7, 9} ;
++        RangeIndex rIndex = makeRangeIndex(2) ;
++        testInsertDelete(rIndex, keys1, keys2) ;
++    }
++
++    @Test
++    public void tree_del_2_08() {
++        int[] keys1 = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0} ;
++        int[] keys2 = {0, 9, 2, 7, 4, 5, 6, 3, 8, 1} ;
++        RangeIndex rIndex = makeRangeIndex(2) ;
++        testInsertDelete(rIndex, keys1, keys2) ;
++    }
++
++    @Test
++    public void tree_del_2_09() {
++        int[] keys1 = {0, 2, 4, 6, 8, 1, 3, 5, 7, 9} ;
++        int[] keys2 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} ;
++        RangeIndex rIndex = makeRangeIndex(2) ;
++        testInsertDelete(rIndex, keys1, keys2) ;
++    }
++
++    @Test
++    public void tree_del_2_10() {
++        int[] keys1 = {0, 2, 4, 6, 8, 1, 3, 5, 7, 9} ;
++        int[] keys2 = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0} ;
++        RangeIndex rIndex = makeRangeIndex(2) ;
++        testInsertDelete(rIndex, keys1, keys2) ;
++    }
++
++    @Test
++    public void tree_del_2_11() {
++        int[] keys1 = {0, 2, 4, 6, 8, 1, 3, 5, 7, 9} ;
++        int[] keys2 = {0, 2, 4, 6, 8, 1, 3, 5, 7, 9} ;
++        RangeIndex rIndex = makeRangeIndex(2) ;
++        testInsertDelete(rIndex, keys1, keys2) ;
++    }
++
++    @Test
++    public void tree_del_2_12() {
++        int[] keys1 = {0, 2, 4, 6, 8, 1, 3, 5, 7, 9} ;
++        int[] keys2 = {0, 9, 2, 7, 4, 5, 6, 3, 8, 1} ;
++        RangeIndex rIndex = makeRangeIndex(2) ;
++        testInsertDelete(rIndex, keys1, keys2) ;
++    }
++
++    @Test
++    public void tree_iter_2_01() {
++        int[] keys = {0, 2, 4, 6, 8, 1, 3, 5, 7, 9} ;
++        RangeIndex rIndex = makeRangeIndex(2) ;
++        add(rIndex, keys) ;
++        List<Integer> x = toIntList(rIndex.iterator(r(4), r(6))) ;
++        List<Integer> expected = toIntList(4, 5) ;
++        assertEquals(expected, x) ;
++    }
++
++    @Test
++    public void tree_iter_2_02() {
++        int[] keys = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} ;
++        RangeIndex rIndex = makeRangeIndex(2) ;
++        add(rIndex, keys) ;
++        List<Integer> x = toIntList(rIndex.iterator(r(4), r(7))) ;
++        List<Integer> expected = toIntList(4, 5, 6) ;
++        assertEquals(expected, x) ;
++    }
++
++    @Test
++    public void tree_iter_2_03() {
++        int[] keys = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} ;
++        RangeIndex rIndex = makeRangeIndex(2) ;
++        add(rIndex, keys) ;
++        List<Integer> x = toIntList(rIndex.iterator(r(4), null)) ;
++        List<Integer> expected = toIntList(4, 5, 6, 7, 8, 9) ;
++        assertEquals(expected, x) ;
++    }
++
++    @Test
++    public void tree_iter_2_04() {
++        int[] keys = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} ;
++        RangeIndex rIndex = makeRangeIndex(2) ;
++        add(rIndex, keys) ;
++        List<Integer> x = toIntList(rIndex.iterator(null, null)) ;
++        List<Integer> expected = toIntList(keys) ;
++        assertEquals(expected, x) ;
++    }
++
++    @Test
++    public void tree_iter_2_05() {
++        int[] keys = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} ;
++        RangeIndex rIndex = makeRangeIndex(2) ;
++        add(rIndex, keys) ;
++        List<Integer> x = toIntList(rIndex.iterator(null, r(4))) ;
++        List<Integer> expected = toIntList(0, 1, 2, 3) ;
++        assertEquals(expected, x) ;
++    }
++
++    @Test
++    public void tree_iter_2_07() {
++        int[] keys = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9} ;
++        RangeIndex rIndex = makeRangeIndex(2) ;
++        add(rIndex, keys) ;
++        List<Integer> x = toIntList(rIndex.iterator(null, r(99))) ;
++        List<Integer> expected = toIntList(keys) ;
++        assertEquals(expected, x) ;
++    }
++
++    @Test
++    public void tree_iter_2_08() {
++        int[] keys = {1, 2, 3, 4, 5, 6, 7, 8, 9} ;
++        RangeIndex rIndex = makeRangeIndex(2) ;
++        add(rIndex, keys) ;
++        List<Integer> x = toIntList(rIndex.iterator(r(0), r(99))) ;
++        List<Integer> expected = toIntList(keys) ;
++        assertEquals(expected, x) ;
++    }
++
++    @Test
++    public void tree_iter_2_09() {
++        int[] keys = {1, 2, 3, 4, /* 5, 6, */7, 8, 9, 10, 11} ;
++        RangeIndex rIndex = makeRangeIndex(2) ;
++        add(rIndex, keys) ;
++        List<Integer> x = toIntList(rIndex.iterator(r(5), r(7))) ;
++        List<Integer> expected = toIntList() ;
++        assertEquals(expected, x) ;
++    }
++
++    // Root
++    @Test
++    public void tree_iter_0_01() {
++        int[] keys = {1, 2, 3, 4, 5} ;
++        RangeIndex rIndex = makeRangeIndex(5) ;
++        add(rIndex, keys) ;
++        Iterator<Record> iter = rIndex.iterator(r(2), r(4)) ;
++        while ( iter.hasNext() ) 
++            iter.next() ;
++        
++        List<Integer> x = toIntList(rIndex.iterator(r(2), r(4))) ;
++        List<Integer> expected = toIntList(2, 3) ;
++        assertEquals(expected, x) ;
++    }
++
++    @Test
++    public void tree_iter_0_02() {
++        int[] keys = {1, 2, 3, 4, 5} ;
++        RangeIndex rIndex = makeRangeIndex(5) ;
++        add(rIndex, keys) ;
++        List<Integer> x = toIntList(rIndex.iterator(null, null)) ;
++        List<Integer> expected = toIntList(keys) ;
++        assertEquals(expected, x) ;
++    }
++
++    @Test
++    public void tree_iter_0_03() {
++        int[] keys = {1, 2, 3, 4, 5} ;
++        RangeIndex rIndex = makeRangeIndex(5) ;
++        add(rIndex, keys) ;
++        List<Integer> x = toIntList(rIndex.iterator(r(5), null)) ;
++        List<Integer> expected = toIntList(5) ;
++        assertEquals(expected, x) ;
++    }
++
++    @Test
++    public void tree_iter_0_04() {
++        int[] keys = {1, 2, 3, 4, 5} ;
++        RangeIndex rIndex = makeRangeIndex(5) ;
++        add(rIndex, keys) ;
++        List<Integer> x = toIntList(rIndex.iterator(r(0), r(0))) ;
++        List<Integer> expected = toIntList() ;
++        assertEquals(expected, x) ;
++    }
++
++    @Test
++    public void tree_iter_0_05() {
++        int[] keys = {1, 2, 3, 4, 5} ;
++        RangeIndex rIndex = makeRangeIndex(5) ;
++        add(rIndex, keys) ;
++        List<Integer> x = toIntList(rIndex.iterator(r(1), r(0))) ;
++        List<Integer> expected = toIntList() ;
++        assertEquals(expected, x) ;
++    }
++
++    @Test
++    public void tree_ret_1() {
++        int[] keys = {1, 2, 3, 4, 5} ;
++        RangeIndex rIndex = makeRangeIndex(2) ;
++        add(rIndex, keys) ;
++        boolean b = rIndex.insert(intToRecord(3)) ;
++        assertFalse(b) ;
++        b = rIndex.insert(intToRecord(9)) ;
++        assertTrue(b) ;
++    }
++
++    @Test
++    public void tree_ret_2() {
++        int[] keys = {1, 2, 3, 4, 5} ;
++        RangeIndex rIndex = makeRangeIndex(2) ;
++        add(rIndex, keys) ;
++        boolean b = rIndex.delete(intToRecord(9)) ;
++        assertFalse(b) ;
++        b = rIndex.insert(intToRecord(1)) ;
++        assertFalse(b) ;
++    }
++
++    @Test
++    public void tree_2_N() {
++        for ( int i = 0 ; i < 10 ; i++ )
++            randTest(makeRangeIndex(2), 999, 20) ;
++    }
++
++    @Test
++    public void tree_3_N() {
++        for ( int i = 0 ; i < 10 ; i++ )
++            randTest(makeRangeIndex(3), 9999, 100) ;
++    }
++
++    @Test public void tree_clear_00()   { testClear(0) ; }
++
++    @Test public void tree_clear_01()   { testClear(5) ; }
++
++    @Test public void tree_clear_02()   { testClear(15) ; }
++
++    @Test public void tree_clear_03()   { testClear(998) ; }
++
++    @Test public void tree_clear_04()   { testClear(999) ; }
++
++    @Test public void tree_clear_05()   { testClear(1000) ; }
++    
++    @Test public void tree_clear_06()   { testClear(1001) ; }
++    
++    @Test public void tree_clear_07()   { testClear(5500) ; }
++
++    protected void testClear(int N) {
++        int[] keys = new int[N] ; // Slice is 1000.
++        for ( int i = 0 ; i < keys.length ; i++ )
++            keys[i] = i ;
++        RangeIndex rIndex = makeRangeIndex(3) ;
++        add(rIndex, keys) ;
++        if ( N > 0 )
++            assertFalse(rIndex.isEmpty()) ;
++        rIndex.clear() ;
++        assertTrue(rIndex.isEmpty()) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-index-test/src/main/java/org/seaborne/dboe/index/test/BaseSoakTest.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-index-test/src/main/java/org/seaborne/dboe/index/test/BaseSoakTest.java
index 0000000,0000000..bd5ddf6
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-index-test/src/main/java/org/seaborne/dboe/index/test/BaseSoakTest.java
@@@ -1,0 -1,0 +1,140 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.index.test;
++
++import java.util.List ;
++
++import jena.cmd.CmdException ;
++import jena.cmd.CmdGeneral ;
++
++import org.apache.jena.atlas.lib.Lib ;
++import org.apache.jena.atlas.lib.RandomLib ;
++import org.apache.jena.atlas.logging.LogCtl ;
++
++public abstract class BaseSoakTest extends CmdGeneral {
++    static { LogCtl.setLog4j() ; }
++
++    protected final int MinOrder = 2 ;
++    protected final int MinSize  = 1 ;
++    protected int       MaxOrder = -1 ;
++    protected int       MaxSize  = -1 ;
++    protected int       NumTest  = -1 ;
++    
++    protected BaseSoakTest(String[] argv) {
++        super(argv) ;
++    }
++
++    protected abstract void before() ;
++    protected abstract void after() ;
++
++    @Override
++    protected String getSummary() {
++        return "Usage: "+Lib.className(this)+" maxOrder maxSize NumTests" ;
++    }
++
++    @Override
++    protected void processModulesAndArgs() {
++        List<String> args = super.getPositional() ;
++        if ( args.size() != 3 )
++            throw new CmdException("Usage: maxOrder maxSize NumTests") ;
++        
++        try { MaxOrder = Integer.parseInt(args.get(0)) ; }
++        catch (NumberFormatException ex)
++        { throw new CmdException("Bad number for MaxOrder") ; }
++
++        try { MaxSize = Integer.parseInt(args.get(1)) ; }
++        catch (NumberFormatException ex)
++        { throw new CmdException("Bad number for MaxSize") ; }
++
++        try { NumTest = Integer.parseInt(args.get(2)) ; }
++        catch (NumberFormatException ex)
++        { throw new CmdException("Bad number for NumTest") ; }
++    }
++
++    @Override
++    protected void exec() {
++        int successes   = 0 ;
++        int failures    = 0 ;
++
++        // Number of dots.
++        int numOnLine = 50 ;
++        int testsPerTick ;
++        if ( NumTest < 20 )
++            testsPerTick = 5 ;
++        else if ( NumTest < 200 )
++            testsPerTick = 50 ;
++        else 
++            testsPerTick = 500 ;
++        
++        
++        // ---- Format for line counter.
++        int numLines = (int)Math.ceil( ((double)NumTest) / (testsPerTick * numOnLine) ) ;
++        // Start of last line.
++        int z = (numLines-1)*(testsPerTick * numOnLine) ;
++        int digits = 1 ;
++        if ( z > 0 )
++            digits = 1+(int)Math.floor(Math.log10(z));
++        String format = "[%"+digits+"d] " ;
++
++        System.out.printf("TEST : %,d tests : Max Order=%d  Max Items=%,d [tests per tick=%d]\n", NumTest, MaxOrder, MaxSize, testsPerTick) ;
++        
++        before() ;
++        
++        int testCount = 1 ;
++        
++        for ( testCount = 1 ; testCount <= NumTest ; testCount++ ) {
++            if ( testCount % testsPerTick == 0 )
++                System.out.print(".") ;
++            if ( testCount % (testsPerTick * numOnLine) == 0 )
++                System.out.println("") ;
++            if ( testCount % (testsPerTick * numOnLine) == 1 )
++                System.out.printf(format, testCount-1) ;
++
++            int idx = testCount - 1 ;
++            int order = ( MinOrder == MaxOrder ) ? MinOrder : MinOrder + RandomLib.random.nextInt(MaxOrder-MinOrder) ;
++            int size =  ( MinSize  == MaxSize  ) ? MinSize :  MinSize  + RandomLib.random.nextInt(MaxSize-MinSize) ;            try {
++                //System.out.printf("TEST : %,d : Order=%-2d : Size=%d\n", testCount, order, size) ;
++                runOneTest(testCount, order, size) ;
++                successes++ ;
++            }
++            catch (AssertionError | RuntimeException ex) {
++                System.err.printf("-- Fail: (order=%d, size=%d)\n", order, size) ;
++                ex.printStackTrace(System.err) ;
++                System.err.printf("--------------------------\n") ;
++                failures++ ;
++            }
++        }
++        
++        // Did the last loop print a new line?
++        if ( (testCount-1) % (testsPerTick*numOnLine) != 0 )
++            System.out.println();
++            
++        after() ;
++        System.err.flush() ;
++        System.out.flush() ;
++        System.out.printf("DONE : %,d tests : Success=%,d ; Failures=%,d\n", NumTest, successes, failures);
++    }
++
++    protected abstract void runOneTest(int testCount, int order, int size, boolean debug) ;
++
++    protected abstract void runOneTest(int testCount, int order, int size) ;
++
++    @Override
++    protected String getCommandName() { return Lib.className(this) ; } 
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-index-test/src/main/java/org/seaborne/dboe/index/test/IndexMaker.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-index-test/src/main/java/org/seaborne/dboe/index/test/IndexMaker.java
index 0000000,0000000..317639a
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-index-test/src/main/java/org/seaborne/dboe/index/test/IndexMaker.java
@@@ -1,0 -1,0 +1,26 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.index.test;
++
++import org.seaborne.dboe.index.Index ;
++
++public interface IndexMaker
++{
++    Index makeIndex() ;
++    String getLabel() ;
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-index-test/src/main/java/org/seaborne/dboe/index/test/IndexTestGenerator.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-index-test/src/main/java/org/seaborne/dboe/index/test/IndexTestGenerator.java
index 0000000,0000000..65b3412
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-index-test/src/main/java/org/seaborne/dboe/index/test/IndexTestGenerator.java
@@@ -1,0 -1,0 +1,44 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.index.test;
++
++import org.apache.jena.atlas.lib.RandomLib ;
++import org.apache.jena.atlas.test.ExecGenerator ;
++
++public class IndexTestGenerator implements ExecGenerator
++{
++    int maxNumKeys ;
++    int maxValue ;
++    IndexMaker maker ;
++    
++    public IndexTestGenerator(IndexMaker maker, int maxValue, int maxNumKeys)
++    {
++        if ( maxValue <= maxNumKeys )
++            throw new IllegalArgumentException("RangeIndexTestGenerator: Max value less than number of keys") ;
++        this.maker = maker ;
++        this.maxValue = maxValue ; 
++        this.maxNumKeys = maxNumKeys ;
++    }
++    
++    @Override
++    public void executeOneTest()
++    {
++        int numKeys = RandomLib.random.nextInt(maxNumKeys)+1 ;
++        IndexTestLib.randTest(maker.makeIndex(), maxValue, numKeys) ;
++    }
++}


[44/50] [abbrv] jena git commit: JENA-1396: Merge //github/afs/mantis as subdirectory jena-db/

Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-index-test/src/main/java/org/seaborne/dboe/index/test/IndexTestLib.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-index-test/src/main/java/org/seaborne/dboe/index/test/IndexTestLib.java
index 0000000,0000000..f4dd9a8
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-index-test/src/main/java/org/seaborne/dboe/index/test/IndexTestLib.java
@@@ -1,0 -1,0 +1,218 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.index.test ;
++
++import static java.lang.String.format ;
++import static org.apache.jena.atlas.lib.ListUtils.asList ;
++import static org.apache.jena.atlas.lib.ListUtils.unique ;
++import static org.apache.jena.atlas.test.Gen.rand ;
++import static org.apache.jena.atlas.test.Gen.strings ;
++import static org.junit.Assert.assertEquals ;
++import static org.junit.Assert.assertNotNull ;
++import static org.junit.Assert.fail ;
++import static org.seaborne.dboe.test.RecordLib.intToRecord ;
++import static org.seaborne.dboe.test.RecordLib.r ;
++import static org.seaborne.dboe.test.RecordLib.toIntList ;
++
++import java.util.* ;
++
++import org.apache.jena.atlas.lib.RandomLib ;
++import org.apache.jena.ext.com.google.common.primitives.Ints;
++import org.junit.Assert ;
++import org.seaborne.dboe.base.record.Record ;
++import org.seaborne.dboe.index.Index ;
++import org.seaborne.dboe.index.RangeIndex ;
++import org.seaborne.dboe.test.RecordLib ;
++
++public class IndexTestLib {
++
++    // ---------- Utilities
++
++    // public static RangeIndex buildRangeIndex(RangeIndexMaker maker, int[]
++    // keys)
++    // {
++    // RangeIndex index = maker.make() ;
++    // IndexTestLib.add(index, keys) ;
++    // return index ;
++    // }
++
++    public static Index buildIndex(IndexMaker maker, int[] keys) {
++        Index index = maker.makeIndex() ;
++        IndexTestLib.add(index, keys) ;
++        return index ;
++    }
++    
++    private static Random random = RandomLib.qrandom ;
++
++    public static void testIteration(RangeIndex index, int[] keys, int numIterations) {
++        // Shared across test-lets
++        SortedSet<Integer> x = new TreeSet<>() ;
++        for ( int v : keys )
++            x.add(v) ;
++
++        for ( int i = 0 ; i < numIterations ; i++ ) {
++            int lo = random.nextInt(keys.length) ;
++            int hi = random.nextInt(keys.length) ;
++            if ( lo > hi ) {
++                int t = lo ;
++                lo = hi ;
++                hi = t ;
++            }
++            // Does not consider nulls for min, max)
++            // That is assumed to be part of functional testing.
++            // Tweak lo and hi
++            if ( lo != 0 && random.nextFloat() < 0.5 )
++                lo-- ; // Negatives confuse the int/record code.
++            if ( random.nextFloat() < 0.5 )
++                hi++ ;
++
++            List<Integer> slice = r(index.iterator(r(lo), r(hi))) ;
++            List<Integer> expected = new ArrayList<>(keys.length) ;
++            for ( Integer ii : x.subSet(lo, hi) )
++                expected.add(ii) ;
++            if ( ! expected.equals(slice) )
++                assertEquals(format("(%d,%d)", lo, hi), expected, slice) ;
++        }
++    }
++
++    /* One random test : print the keys if there was a problem */
++
++    public static void randTest(Index index, int maxValue, int numKeys) {
++        randTest(index, maxValue, numKeys, true) ; 
++    }
++    public static void randTest(Index index, int maxValue, int numKeys, boolean includeIteratorCheck) {
++        if ( numKeys >= 5000 )
++            System.err.printf("Warning: too many keys\n") ;
++
++        int[] keys1 = rand(numKeys, 0, maxValue) ;
++        int[] keys2 = permute2(keys1) ;
++        //int[] keys2 = permute(keys1, 4 * numKeys) ;
++        //System.err.printf("int[] keys1 = {%s} ;\n", strings(keys1)) ;
++        //System.err.printf("int[] keys2 = {%s}; \n", strings(keys2)) ;
++        try {
++            testInsert(index, keys1) ;
++            if ( true ) {
++                // Checking tests.
++                testIndexContents(index, keys2) ;
++                // Test iteration - quite expensive.
++                if ( includeIteratorCheck && index instanceof RangeIndex )
++                    testIteration((RangeIndex)index, keys1, 10) ;
++            }
++            testDelete(index, keys2) ;
++        }
++        catch (AssertionError | RuntimeException ex) {
++            System.err.printf("Index : %s\n", index.getClass().getName()) ;
++            System.err.printf("int[] keys1 = {%s} ;\n", strings(keys1)) ;
++            System.err.printf("int[] keys2 = {%s}; \n", strings(keys2)) ;
++            throw ex ;
++        }
++    }
++    
++    /** Pull items out of the list in a random order */ 
++    private static int[] permute2(int[] x) {
++        int[] ints = Arrays.copyOf(x, x.length);
++        List<Integer> list = Ints.asList(ints);
++        Collections.shuffle(list, random);
++        return ints; 
++    }
++    
++    private static Random randInit()
++    {
++        // Cheap random numbers, well seeded.
++        int seed = random.nextInt() ;
++        return new Random(seed) ;   
++    }
++
++    // ---- Test utils
++
++    public static void testInsert(Index index, int[] keys) {
++        IndexTestLib.add(index, keys) ;
++        testIndexContents(index, keys) ;
++    }
++
++    public static Index testInsert(IndexMaker maker, int[] keys) {
++        Index index = maker.makeIndex() ;
++        testInsert(index, keys) ;
++        return index ;
++    }
++
++    public static void testInsertDelete(Index index, int[] buildKeys, int[] deleteKeys) {
++        testInsert(index, buildKeys) ;
++        testDelete(index, deleteKeys) ;
++    }
++
++    public static void testDelete(Index index, int[] vals) {
++        long size1 = index.size() ;
++
++        int count = 0 ;
++        count = delete(index, vals) ;
++
++        List<Record> x = intToRecord(vals, RecordLib.TestRecordLength) ;
++        for ( Record r : x )
++            Assert.assertFalse(index.contains(r)) ;
++        long size2 = index.size() ;
++
++        assertEquals(size1 - count, size2) ;
++    }
++
++    public static int delete(Index index, int... vals) {
++        int count = 0 ;
++        for ( int v : vals ) {
++            boolean b = index.delete(r(v)) ;
++            if ( b )
++                count++ ;
++        }
++        return count ;
++    }
++
++    public static void add(Index index, int... vals) {
++        // System.out.println("Add: "+Arrays.toString(vals)) ;
++        List<Record> x = intToRecord(vals, RecordLib.TestRecordLength) ;
++        for ( Record r : x ) {
++            // System.out.println("  Add: "+r) ;
++            index.insert(r) ;
++        }
++    }
++
++    public static void testIndexContents(Index index, int... records) {
++        List<Integer> x = toIntList(index.iterator()) ;
++
++        // Make a unique list of expected records. Remove duplicates
++        List<Integer> y = unique(asList(records)) ;
++
++        assertEquals("Expected records size and tree size different", y.size(), index.size()) ;
++        assertEquals("Expected records size and iteration over all keys are of different sizes", y.size(), x.size()) ;
++
++        if ( index instanceof RangeIndex ) {
++            // Check sorted order
++            for ( int i = 0 ; i < x.size() - 2 ; i++ ) {
++                if ( x.get(i) > x.get(i + 1) ) {
++                    fail("check failed: " + strings(records)) ;
++                    return ;
++                }
++            }
++        }
++
++        // Check each expected record is in the tree
++        for ( int k : y ) {
++            Record rec = intToRecord(k) ;
++            Record r2 = index.find(rec) ;
++            assertNotNull(r2) ;
++        }
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-index-test/src/main/java/org/seaborne/dboe/index/test/RangeIndexMaker.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-index-test/src/main/java/org/seaborne/dboe/index/test/RangeIndexMaker.java
index 0000000,0000000..10c7b00
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-index-test/src/main/java/org/seaborne/dboe/index/test/RangeIndexMaker.java
@@@ -1,0 -1,0 +1,27 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.index.test;
++
++import org.seaborne.dboe.index.RangeIndex ;
++
++public interface RangeIndexMaker extends IndexMaker
++{
++    RangeIndex makeRangeIndex() ;
++    @Override
++    String getLabel() ;
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-index-test/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --cc jena-db/dboe-index-test/src/test/resources/log4j.properties
index 0000000,0000000..d6b299a
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-index-test/src/test/resources/log4j.properties
@@@ -1,0 -1,0 +1,6 @@@
++log4j.rootLogger=INFO, stdlog
++
++log4j.appender.stdlog=org.apache.log4j.ConsoleAppender
++## log4j.appender.stdlog.target=System.err
++log4j.appender.stdlog.layout=org.apache.log4j.PatternLayout
++log4j.appender.stdlog.layout.ConversionPattern=%d{HH:mm:ss} %-5p %-20c{1} :: %m%n

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-index/pom.xml
----------------------------------------------------------------------
diff --cc jena-db/dboe-index/pom.xml
index 0000000,0000000..28943de
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-index/pom.xml
@@@ -1,0 -1,0 +1,42 @@@
++<?xml version="1.0" encoding="utf-8"?>
++<!--
++   Licensed 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.
++ 
++   See the NOTICE file distributed with this work for additional
++   information regarding copyright ownership.
++-->
++
++<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
++  <modelVersion>4.0.0</modelVersion>
++
++  <artifactId>dboe-index</artifactId>
++  <packaging>jar</packaging>
++  <name>Mantis (${project.artifactId})</name>
++
++  <parent>
++    <groupId>org.seaborne.mantis</groupId>
++    <artifactId>mantis</artifactId>
++    <version>0.3.1-SNAPSHOT</version>
++  </parent> 
++
++  <dependencies>
++
++    <dependency>
++      <groupId>org.seaborne.mantis</groupId>
++      <artifactId>dboe-base</artifactId>
++      <version>0.3.1-SNAPSHOT</version>
++    </dependency>
++
++  </dependencies>
++
++</project>

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-index/src/main/java/org/seaborne/dboe/index/Index.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-index/src/main/java/org/seaborne/dboe/index/Index.java
index 0000000,0000000..ddf4800
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-index/src/main/java/org/seaborne/dboe/index/Index.java
@@@ -1,0 -1,0 +1,91 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.index;
++
++import java.util.Iterator ;
++import java.util.stream.Stream ;
++
++import org.apache.jena.atlas.lib.Closeable ;
++import org.apache.jena.atlas.lib.Sync ;
++import org.seaborne.dboe.base.record.Record ;
++import org.seaborne.dboe.base.record.RecordFactory ;
++
++public interface Index extends Iterable<Record>, Sync, Closeable
++{
++    /** Find one record - and return the record actually in the index (may have a value part) */
++    public Record find(Record record) ;
++    
++    /** Return whether the index contains the record or not. */
++    public boolean contains(Record record) ;
++    
++    /** Insert a record - return true if an insertion was actually needed */
++    public boolean insert(Record record) ;
++    
++    /** Delete a record - Return true if a record was actually removed */
++    public boolean delete(Record record) ;
++    
++//    public default void bulkInsert(Collection<Record> inserts) {
++//        for ( Record r : inserts )
++//            insert(r) ;
++//    }
++//    
++//    public default void bulkDelete(Collection<Record> inserts) {
++//        for ( Record r : inserts )
++//            delete(r) ;
++//    }
++    
++    /** Bulk changes as given by a stream of changes.
++     * This is operations consumes the stream.
++     * @param changes
++     */
++    public default void bulkChanges(Stream<IndexChange> changes) {
++        changes.forEach(chg -> {
++            switch (chg.action) {
++                case ADD :
++                    insert(chg.record) ;
++                    break ;
++                case DELETE :
++                    delete(chg.record) ;
++                    break ;
++            }
++        }) ;
++    }
++    
++    /** Iterate over the whole index */ 
++    @Override
++    public Iterator<Record> iterator() ;
++    
++    /** Get the Record factory associated with this index */
++    public RecordFactory getRecordFactory() ;
++    
++    /** Close the index - can not be used again through this object */
++    @Override
++    public void close() ;
++    
++    /** Answer whether the index is empty or not.  May return false for unknown or meaningless */
++    public boolean isEmpty() ;
++    
++    /** Clear the index */
++    public void clear() ;
++    
++    /** Perform checks on this index */
++    public void check() ;
++    
++    /** Return size if known else return -1 : does not count the peristent storage */
++    public long size() ;
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-index/src/main/java/org/seaborne/dboe/index/IndexChange.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-index/src/main/java/org/seaborne/dboe/index/IndexChange.java
index 0000000,0000000..b296ae8
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-index/src/main/java/org/seaborne/dboe/index/IndexChange.java
@@@ -1,0 -1,0 +1,40 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.index;
++
++import java.util.Objects ;
++
++import org.seaborne.dboe.base.record.Record ;
++
++import org.apache.jena.sparql.core.DatasetChanges ;
++
++/** Temporary class to refine what to put into Jena itself.
++ * @see DatasetChanges
++ */
++public class IndexChange {
++    enum Action { ADD, DELETE }
++    final Action action ;
++    final Record record ;
++    public IndexChange(Action action, Record record) {
++        Objects.requireNonNull(action, "Action can not be null") ; 
++        Objects.requireNonNull(record, "Record can not be null") ;
++        this.action = action ;
++        this.record = record ;
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-index/src/main/java/org/seaborne/dboe/index/IndexConst.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-index/src/main/java/org/seaborne/dboe/index/IndexConst.java
index 0000000,0000000..08797f4
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-index/src/main/java/org/seaborne/dboe/index/IndexConst.java
@@@ -1,0 -1,0 +1,52 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.index;
++
++import org.seaborne.dboe.base.block.FileMode ;
++import org.seaborne.dboe.sys.SystemIndex ;
++
++public class IndexConst {
++
++    private static IndexParams stdParams = new IndexParamsStd() ;
++    public static IndexParams getDftParams() { return stdParams ; }
++    
++    private static class IndexParamsStd implements IndexParams {
++
++        @Override
++        public FileMode getFileMode() {
++            return SystemIndex.fileMode() ;
++        }
++
++        @Override
++        public Integer getBlockSize() {
++            return SystemIndex.BlockSize ;
++        }
++
++        @Override
++        public Integer getBlockReadCacheSize() {
++            return SystemIndex.BlockReadCacheSize ;
++        }
++
++        @Override
++        public Integer getBlockWriteCacheSize() {
++            return SystemIndex.BlockWriteCacheSize ;
++        }
++
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-index/src/main/java/org/seaborne/dboe/index/IndexLogger.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-index/src/main/java/org/seaborne/dboe/index/IndexLogger.java
index 0000000,0000000..ab2acf9
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-index/src/main/java/org/seaborne/dboe/index/IndexLogger.java
@@@ -1,0 -1,0 +1,64 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.index;
++
++import java.util.Iterator ;
++
++import org.seaborne.dboe.base.record.Record ;
++import org.slf4j.Logger ;
++
++public final class IndexLogger extends IndexWrapper
++{
++    private final Logger log ;
++
++    public IndexLogger(RangeIndex rIdx, Logger log)
++    {
++        super(rIdx) ;
++        this.log = log ;
++    }
++
++    @Override
++    public boolean insert(Record record)
++    { 
++        log.info("Add: {}", record) ;
++        return super.insert(record) ; 
++    }
++
++    @Override
++    public boolean delete(Record record)
++    { 
++        log.info("Delete: {}", record) ;
++        return super.delete(record) ; 
++    }
++
++    @Override
++    public Record find(Record record)
++    {
++        log.info("Find: {}", record) ;
++        Record r2 = super.find(record) ;
++        log.info("Find: "+record+" ==> "+r2) ;
++        return r2 ;
++    }
++
++    @Override
++    public Iterator<Record> iterator()
++    {
++        log.info("iterator()") ;
++        return super.iterator() ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-index/src/main/java/org/seaborne/dboe/index/IndexMap.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-index/src/main/java/org/seaborne/dboe/index/IndexMap.java
index 0000000,0000000..b176a84
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-index/src/main/java/org/seaborne/dboe/index/IndexMap.java
@@@ -1,0 -1,0 +1,171 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.index;
++
++import java.util.Arrays ;
++import java.util.HashMap ;
++import java.util.Iterator ;
++import java.util.Map ;
++
++import org.apache.jena.atlas.lib.Bytes ;
++import org.seaborne.dboe.base.record.Record ;
++import org.seaborne.dboe.base.record.RecordFactory ;
++
++/** A simple implementation of {@link Index} for testing */
++public class IndexMap implements Index
++{
++    private final Map<ByteArray, ByteArray> index = new HashMap<>() ;
++    private final RecordFactory recordFactory ;
++    
++    public IndexMap(RecordFactory recordFactory)
++    {
++        this.recordFactory = recordFactory ;
++    }
++    
++    @Override
++    public Record find(Record record)
++    {
++        ByteArray k = wrap(record.getKey()) ;
++        ByteArray v = index.get(k) ;
++        if ( v == null )
++            return null ;
++        return record(k, v) ;
++    }
++
++    @Override
++    public boolean contains(Record record)
++    {
++        Record r = find(record) ;
++        if ( r == null )
++            return false ;
++        if ( ! recordFactory.hasValue() )
++            return true ;
++        return Bytes.compare(record.getValue(), r.getValue()) == 0 ;
++    }
++
++    @Override
++    public boolean insert(Record record)
++    {
++        Record r = find(record) ;
++        if ( r != null && r.equals(record) )
++            return false ;
++        index.put(wrap(record.getKey()), wrap(record.getValue())) ;
++        return true ;
++    }
++
++    @Override
++    public boolean delete(Record record)
++    {
++        ByteArray x = index.remove(wrap(record.getKey())) ;
++        if ( x == null )
++            return false ;
++        return true ;
++    }
++
++    @Override
++    public Iterator<Record> iterator()
++    {
++        return new Iterator<Record>() {
++            
++            Iterator<Map.Entry<ByteArray, ByteArray>> iter = index.entrySet().iterator() ;
++            
++            @Override
++            public boolean hasNext()
++            {
++                return iter.hasNext() ;
++            }
++
++            @Override
++            public Record next()
++            {
++                Map.Entry<ByteArray, ByteArray> e = iter.next() ;
++                return record(e.getKey(), e.getValue()) ; 
++            }
++
++            @Override
++            public void remove()
++            { throw new UnsupportedOperationException() ;}
++        } ; 
++        
++    }
++
++    @Override
++    public RecordFactory getRecordFactory()
++    {
++        return recordFactory ;
++    }
++
++    @Override
++    public boolean isEmpty()
++    {
++        return index.isEmpty() ;
++    }
++
++    @Override
++    public void clear()
++    {
++        index.clear() ;
++    }
++
++    @Override
++    public void check()
++    {}
++
++    @Override
++    public long size()
++    {
++        return index.size() ;
++    }
++
++    @Override
++    public void sync()
++    {}
++
++    @Override
++    public void close()
++    {}
++
++    private static ByteArray wrap(byte[] b) { return new ByteArray(b) ; }
++    private Record record(ByteArray k, ByteArray v) { return recordFactory.create(k.bytes, v.bytes) ; }
++    
++    private static class ByteArray
++    {
++        byte[] bytes ;
++        ByteArray(byte[] bytes) { this.bytes = bytes ; }
++        
++        @Override
++        public int hashCode()
++        {
++            final int prime = 31 ;
++            int result = 1 ;
++            result = prime * result + Arrays.hashCode(bytes) ;
++            return result ;
++        }
++        @Override
++        public boolean equals(Object obj)
++        {
++            if (this == obj) return true ;
++            if (obj == null) return false ;
++            if (getClass() != obj.getClass()) return false ;
++            ByteArray other = (ByteArray)obj ;
++            if (!Arrays.equals(bytes, other.bytes)) return false ;
++            return true ;
++        }
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-index/src/main/java/org/seaborne/dboe/index/IndexParams.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-index/src/main/java/org/seaborne/dboe/index/IndexParams.java
index 0000000,0000000..f3f25c9
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-index/src/main/java/org/seaborne/dboe/index/IndexParams.java
@@@ -1,0 -1,0 +1,37 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.index;
++
++import org.seaborne.dboe.base.block.BlockParams ;
++import org.seaborne.dboe.base.block.FileMode ;
++
++public interface IndexParams extends BlockParams {
++    /** File Mode */
++    @Override public FileMode getFileMode() ;
++
++    /** Block size - this is only configurable when the on-disk are created.
++     * After that, the same value as at creation must be used each time.
++     */
++    @Override public Integer getBlockSize() ;
++    
++    /** Block read cache size (mmap'ed files do not have a block cache)*/
++    @Override public Integer getBlockReadCacheSize() ;
++    
++    /** Block write cache size (mmap'ed files do not have a block cache)*/
++    @Override public Integer getBlockWriteCacheSize() ;
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-index/src/main/java/org/seaborne/dboe/index/IndexWrapper.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-index/src/main/java/org/seaborne/dboe/index/IndexWrapper.java
index 0000000,0000000..95de7f2
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-index/src/main/java/org/seaborne/dboe/index/IndexWrapper.java
@@@ -1,0 -1,0 +1,91 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.index ;
++
++import java.util.Iterator ;
++
++import org.seaborne.dboe.base.record.Record ;
++import org.seaborne.dboe.base.record.RecordFactory ;
++
++public class IndexWrapper implements Index {
++    protected final Index index ;
++
++    public IndexWrapper(Index idx) {
++        this.index = idx ;
++    }
++
++    @Override
++    public Record find(Record record) {
++        return index.find(record) ;
++    }
++
++    @Override
++    public boolean contains(Record record) {
++        return index.contains(record) ;
++    }
++
++    @Override
++    public boolean insert(Record record) {
++        return index.insert(record) ;
++    }
++
++    @Override
++    public boolean delete(Record record) {
++        return index.delete(record) ;
++    }
++
++    @Override
++    public Iterator<Record> iterator() {
++        return index.iterator() ;
++    }
++
++    @Override
++    public boolean isEmpty() {
++        return index.isEmpty() ;
++    }
++
++    @Override
++    public void clear() {
++        index.clear() ;
++    }
++
++    @Override
++    public void sync() {
++        index.sync() ;
++    }
++
++    @Override
++    public void close() {
++        index.close() ;
++    }
++
++    @Override
++    public RecordFactory getRecordFactory() {
++        return index.getRecordFactory() ;
++    }
++
++    @Override
++    public void check() {
++        index.check() ;
++    }
++
++    @Override
++    public long size() {
++        return index.size() ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-index/src/main/java/org/seaborne/dboe/index/RangeIndex.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-index/src/main/java/org/seaborne/dboe/index/RangeIndex.java
index 0000000,0000000..b53c7f5
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-index/src/main/java/org/seaborne/dboe/index/RangeIndex.java
@@@ -1,0 -1,0 +1,38 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.index;
++
++import java.util.Iterator ;
++
++import org.seaborne.dboe.base.record.Record ;
++import org.seaborne.dboe.base.record.RecordMapper ;
++
++
++public interface RangeIndex extends Index
++{
++    /** Return records between min (inclusive) and max (exclusive), based on the record keys */
++    public Iterator<Record> iterator(Record recordMin, Record recordMax) ;
++    
++    public <X> Iterator<X> iterator(Record recordMin, Record recordMax, RecordMapper<X> mapper) ;
++    
++    /** Return the record containing the least key - may or may not have the associated value */
++    public Record minKey() ;
++
++    /** Return the record containing the greatest key - may or may not have the associated value */
++    public Record maxKey() ;
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-index/src/main/java/org/seaborne/dboe/index/RangeIndexLogger.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-index/src/main/java/org/seaborne/dboe/index/RangeIndexLogger.java
index 0000000,0000000..7a7d70d
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-index/src/main/java/org/seaborne/dboe/index/RangeIndexLogger.java
@@@ -1,0 -1,0 +1,71 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.index;
++
++import java.util.Iterator ;
++
++import org.seaborne.dboe.base.record.Record ;
++import org.slf4j.Logger ;
++
++public final class RangeIndexLogger extends RangeIndexWrapper
++{
++    private final Logger log ;
++
++    public RangeIndexLogger(RangeIndex rIdx, Logger log)
++    {
++        super(rIdx) ;
++        this.log = log ;
++    }
++
++    @Override
++    public boolean insert(Record record)
++    { 
++        log.info("Add: "+record) ;
++        return super.insert(record) ; 
++    }
++
++    @Override
++    public boolean delete(Record record)
++    { 
++        log.info("Delete: "+record) ;
++        return super.delete(record) ; 
++    }
++
++    @Override
++    public Record find(Record record)
++    {
++        log.info("Find: "+record) ;
++        Record r2 = super.find(record) ;
++        log.info("Find: "+record+" ==> "+r2) ;
++        return r2 ;
++    }
++
++    @Override
++    public Iterator<Record> iterator()
++    {
++        log.info("iterator()") ;
++        return super.iterator() ;
++    }
++
++    @Override
++    public Iterator<Record> iterator(Record minRec, Record maxRec)
++    {
++        log.info("iterator("+minRec+", "+maxRec+")") ;
++        return super.iterator(minRec, maxRec) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-index/src/main/java/org/seaborne/dboe/index/RangeIndexWrapper.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-index/src/main/java/org/seaborne/dboe/index/RangeIndexWrapper.java
index 0000000,0000000..f5510e5
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-index/src/main/java/org/seaborne/dboe/index/RangeIndexWrapper.java
@@@ -1,0 -1,0 +1,105 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.index;
++
++import java.util.Iterator ;
++
++import org.seaborne.dboe.base.record.Record ;
++import org.seaborne.dboe.base.record.RecordFactory ;
++import org.seaborne.dboe.base.record.RecordMapper ;
++
++public class RangeIndexWrapper implements RangeIndex
++{
++    // Could extend IndexWrapper but it's nice to have one place to look and set breakpoints.
++    protected final RangeIndex rIndex ;
++    
++    public RangeIndexWrapper(RangeIndex rIdx) { this.rIndex = rIdx ; }
++    
++    @Override
++    public Record find(Record record)
++    { return rIndex.find(record) ; }
++    
++    @Override
++    public boolean contains(Record record)
++    { return rIndex.contains(record) ; }
++    
++    @Override
++    public Record minKey()
++    { return rIndex.minKey() ; }
++    
++    @Override
++    public Record maxKey()
++    { return rIndex.maxKey() ; }
++    
++    @Override
++    public boolean insert(Record record)
++    { return rIndex.insert(record) ; }
++    
++    @Override
++    public boolean delete(Record record)
++    { return rIndex.delete(record) ; }
++    
++//  public Record addAndReturnOld(Record record)
++//  { return bpt.addAndReturnOld(record) ; }
++  
++//    public Record deleteAndReturnOld(Record record)
++//    { return bpt.deleteAndReturnOld(record) ; }
++    
++    @Override
++    public Iterator<Record> iterator()
++    { return rIndex.iterator() ; }
++    
++    @Override
++    public Iterator<Record> iterator(Record minRec, Record maxRec)
++    { return rIndex.iterator(minRec, maxRec) ; }
++    
++    @Override
++    public <X> Iterator<X> iterator(Record minRec, Record maxRec, RecordMapper<X> mapper)
++    { return rIndex.iterator(minRec, maxRec, mapper) ; }
++
++    @Override
++    public boolean isEmpty()
++    { return rIndex.isEmpty() ; }
++    
++    @Override
++    public void clear()
++    { rIndex.clear() ; }
++    
++    @Override
++    public void sync()
++    { rIndex.sync() ; }
++    
++    @Override
++    public void close()
++    { rIndex.close() ; }
++
++    public RangeIndex getWrapped()
++    { return rIndex ; }
++    
++    @Override
++    public RecordFactory getRecordFactory()
++    { return rIndex.getRecordFactory() ; }
++
++    @Override
++    public void check()
++    { rIndex.check() ; }
++
++    @Override
++    public long size()
++    { return rIndex.size() ; }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-index/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --cc jena-db/dboe-index/src/test/resources/log4j.properties
index 0000000,0000000..d6b299a
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-index/src/test/resources/log4j.properties
@@@ -1,0 -1,0 +1,6 @@@
++log4j.rootLogger=INFO, stdlog
++
++log4j.appender.stdlog=org.apache.log4j.ConsoleAppender
++## log4j.appender.stdlog.target=System.err
++log4j.appender.stdlog.layout=org.apache.log4j.PatternLayout
++log4j.appender.stdlog.layout.ConversionPattern=%d{HH:mm:ss} %-5p %-20c{1} :: %m%n

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/pom.xml
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/pom.xml
index 0000000,0000000..50756aa
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/pom.xml
@@@ -1,0 -1,0 +1,63 @@@
++<?xml version="1.0" encoding="utf-8"?>
++<!--
++   Licensed 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.
++ 
++   See the NOTICE file distributed with this work for additional
++   information regarding copyright ownership.
++-->
++
++<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
++  <modelVersion>4.0.0</modelVersion>
++  <artifactId>dboe-trans-data</artifactId>
++  <packaging>jar</packaging>
++  <name>Mantis (${project.artifactId})</name>
++
++  <parent>
++    <groupId>org.seaborne.mantis</groupId>
++    <artifactId>mantis</artifactId>
++    <version>0.3.1-SNAPSHOT</version>
++  </parent> 
++
++  <dependencies>
++
++    <dependency>
++      <groupId>org.seaborne.mantis</groupId>
++      <artifactId>dboe-transaction</artifactId>
++      <version>0.3.1-SNAPSHOT</version>
++    </dependency>
++
++    <dependency>
++      <groupId>org.seaborne.mantis</groupId>
++      <artifactId>dboe-index</artifactId>
++      <version>0.3.1-SNAPSHOT</version>
++    </dependency>
++
++    <dependency>
++      <groupId>org.seaborne.mantis</groupId>
++      <artifactId>dboe-index-test</artifactId>
++      <version>0.3.1-SNAPSHOT</version>
++      <scope>test</scope>
++    </dependency>
++
++    <dependency>
++      <groupId>org.seaborne.mantis</groupId>
++      <artifactId>dboe-base</artifactId>
++      <version>0.3.1-SNAPSHOT</version>
++      <classifier>tests</classifier>
++      <scope>test</scope>
++    </dependency>
++
++    
++  </dependencies>
++
++</project>

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/randtest
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/randtest
index 0000000,0000000..1e716dd
new file mode 100755
--- /dev/null
+++ b/jena-db/dboe-trans-data/randtest
@@@ -1,0 -1,0 +1,57 @@@
++#!/bin/bash
++
++M2="$HOME/.m2/repo"
++
++CPX="
++${M2}/com/fasterxml/jackson/core/jackson-annotations/2.3.0/jackson-annotations-2.3.0.jar
++${M2}/com/fasterxml/jackson/core/jackson-core/2.3.3/jackson-core-2.3.3.jar
++${M2}/com/fasterxml/jackson/core/jackson-databind/2.3.3/jackson-databind-2.3.3.jar
++${M2}/com/github/jsonld-java/jsonld-java/0.5.1/jsonld-java-0.5.1.jar
++${M2}/commons-codec/commons-codec/1.6/commons-codec-1.6.jar
++${M2}/junit/junit/4.12-beta-3/junit-4.12-beta-3.jar
++${M2}/log4j/log4j/1.2.17/log4j-1.2.17.jar
++${M2}/org/apache/commons/commons-csv/1.0/commons-csv-1.0.jar
++${M2}/org/apache/commons/commons-lang3/3.3.2/commons-lang3-3.3.2.jar
++${M2}/org/apache/httpcomponents/httpclient-cache/4.2.6/httpclient-cache-4.2.6.jar
++${M2}/org/apache/httpcomponents/httpclient/4.2.6/httpclient-4.2.6.jar
++${M2}/org/apache/httpcomponents/httpcore/4.2.5/httpcore-4.2.5.jar
++${M2}/org/apache/jena/jena-arq/2.13.0-SNAPSHOT/jena-arq-2.13.0-SNAPSHOT.jar
++${M2}/org/apache/jena/jena-core/2.13.0-SNAPSHOT/jena-core-2.13.0-SNAPSHOT.jar
++${M2}/org/apache/jena/jena-iri/1.1.2-SNAPSHOT/jena-iri-1.1.2-SNAPSHOT.jar
++${M2}/org/apache/thrift/libthrift/0.9.2/libthrift-0.9.2.jar
++${M2}/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar
++${M2}/org/slf4j/jcl-over-slf4j/1.7.6/jcl-over-slf4j-1.7.6.jar
++${M2}/org/slf4j/slf4j-api/1.7.6/slf4j-api-1.7.6.jar
++${M2}/org/slf4j/slf4j-log4j12/1.7.6/slf4j-log4j12-1.7.6.jar
++${M2}/xerces/xercesImpl/2.11.0/xercesImpl-2.11.0.jar
++${M2}/xml-apis/xml-apis/1.4.01/xml-apis-1.4.01.jar
++"
++
++CP=""
++for x in $CPX
++do
++    if [[ $CP == "" ]]
++    then
++	CP="$x"
++    else
++	CP="$CP:$x"
++    fi
++done
++
++MANTIS="$HOME/Mantis"
++for x in dboe-base dboe-index dboe-index-test dboe-transaction
++do
++    CP="$MANTIS/$x/target/classes:$CP"
++    CP="$MANTIS/$x/target/test-classes:$CP"
++done    
++
++ARGS="" 
++#ARGS="-Xmx1G" 
++ARGS="$ARGS -Dbpt:checking=true"
++ARGS="$ARGS -Dbpt:duplication=true"
++
++
++CP="target/classes:target/test-classes:$CP"
++#echo $CP
++#java -cp "$CP" org.seaborne.dboe.trans.bplustree.rewriter.CmdTestBPlusTreeRewriter "$@"
++java $ARGS -cp "$CP" org.seaborne.dboe.trans.bplustree.soak.CmdTestBPlusTree "$@"

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/AccessPath.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/AccessPath.java
index 0000000,0000000..af62e04
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/AccessPath.java
@@@ -1,0 -1,0 +1,69 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.bplustree;
++
++import java.util.ArrayList ;
++import java.util.List ;
++import java.util.stream.Collectors ;
++
++import org.apache.jena.atlas.lib.InternalErrorException ;
++
++public class AccessPath {
++    static class AccessStep {
++        final BPTreeNode node ;
++        final int idx ;
++        final BPTreePage page ;
++        AccessStep(BPTreeNode node, int idx, BPTreePage page) {
++            this.node = node ;
++            this.idx = idx ;
++            this.page = page ;
++        }
++        
++        @Override 
++        public String toString() {
++            return "("+node.label()+", "+idx+")->"+page.getId() ;
++        }
++    }
++    
++    private final BPTreeNode root ;
++    private List<AccessStep> traversed = new ArrayList<>() ;
++    
++    public AccessPath(BPTreeNode root) {
++        this.root = root ;
++    }
++    
++    public void add(BPTreeNode node, int idx, BPTreePage page) {
++        traversed.add(new AccessStep(node, idx, page)) ;
++    }
++    
++    public void reset(BPTreeNode node, int idx, BPTreePage page) {
++        AccessStep s = traversed.remove(traversed.size()-1) ;
++        AccessStep s2 = new AccessStep(node, idx, page) ;
++        if ( s.node != s2.node )
++            throw new InternalErrorException("Bad attempt to reset: "+this+" with "+s2) ; 
++        traversed.add(new AccessStep(node, idx, page)) ;
++    }
++
++    public List<AccessStep> getPath() { return traversed ; }
++    
++    @Override
++    public String toString() {
++        return traversed.stream().map(x-> x.toString()).collect(Collectors.toList()).toString() ;
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPT.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPT.java
index 0000000,0000000..f849367
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPT.java
@@@ -1,0 -1,0 +1,248 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.bplustree;
++
++import static java.lang.String.format ;
++import static org.apache.jena.atlas.lib.Alg.decodeIndex ;
++
++import java.util.List ;
++import java.util.Optional ;
++
++import org.apache.jena.atlas.lib.InternalErrorException ;
++import org.apache.jena.atlas.logging.FmtLog ;
++import org.seaborne.dboe.base.block.BlockMgr ;
++import org.seaborne.dboe.base.file.BufferChannel ;
++import org.seaborne.dboe.sys.SystemIndex ;
++import org.seaborne.dboe.trans.bplustree.AccessPath.AccessStep ;
++import org.slf4j.Logger ;
++
++/** B+Tree assist functions */
++public final class BPT {
++    public static boolean Logging = false ;                  // Turn on/off logging
++    
++    public static boolean forcePromoteModes         = false ;
++    public static boolean promoteDuplicateRecords   = false ;
++    public static boolean promoteDuplicateNodes     = false ;
++
++    // Check within BPTreeNode
++    public static boolean CheckingNode = false ;                      
++    // Check on exit of B+Tree modifiying operations
++    // Not done?
++    public static boolean CheckingConcurrency = SystemIndex.Checking ;
++
++    /** Enable detailed internal consistency checking */
++    public static void checking(boolean onOrOff) {
++        CheckingNode = onOrOff ;
++    }
++
++    /** Dump before and after top level update operations **/
++    public static boolean DumpTree = false ;
++
++    /** Output a lot of detailed information. */
++    public static void infoAll(boolean onOrOff) { 
++        DumpTree = true ;
++        Logging = true ;
++    }
++
++    static boolean logging(Logger log) {
++        return Logging && log.isDebugEnabled() ;
++    }
++
++    static void log(Logger log, String fmt, Object... args) {
++        if ( logging(log) ) {
++            FmtLog.debug(log, fmt, args);
++        }
++    }
++
++    static void warning(String msg, Object... args) {
++        msg = format(msg, args) ;
++        System.out.println("Warning: " + msg) ;
++        System.out.flush() ;
++    }
++
++    static void error(String msg, Object... args) {
++        msg = format(msg, args) ;
++        System.out.println() ;
++        System.out.println(msg) ;
++        System.out.flush() ;
++        throw new BPTreeException(msg) ;
++    }
++
++    /** Convert a find index return to the insert location in the array */ 
++    static int apply(int idx) {
++        if ( idx >= 0 )
++            return idx ;
++        return decodeIndex(idx) ;
++    }
++    
++    // ---- Promotion of pages.
++
++    /** Promote a single page. Assumes the path to this page has been handled in some way elsewhere */  
++    static boolean promote1(BPTreePage page, BPTreeNode node, int idx) {
++        //System.out.println("promote1: "+page.getBlockMgr().getLabel()+" "+page.getBackingBlock().getId()) ;
++        boolean changed = page.promote() ;
++        node.ptrs.set(idx, page.getId()) ;
++        return changed ;
++    }
++
++    /** Promote a B+Tree root */ 
++    static boolean promoteRoot(BPTreeNode root) {
++        if ( ! root.isRoot() ) 
++            throw new InternalErrorException("Not a root") ;
++        boolean changed = root.promote() ;
++        root.bpTree.newRoot(root) ;
++        return changed ;
++    }
++    
++    /** Promote a B+Tree page */ 
++    static void promotePage(AccessPath path, BPTreePage page) {
++        Logger pageLog = page.getLogger() ;
++        boolean loggingCall = logging(pageLog) ; 
++        if ( loggingCall )
++            log(pageLog, "Promote :: Path=%s  Page=%s", path, page) ;
++        // ---- Checking if the access path is consistent.
++        if ( BPT.CheckingNode && path != null ) {
++            if ( path.getPath().size() > 2) {
++                // Check every one except the last is not a leaf node.
++                List<AccessStep> y = path.getPath().subList(0, path.getPath().size()-2) ;
++                Optional<AccessStep> z = y.stream().filter(e -> e.node.isLeaf() ).findFirst() ;
++                if ( z.isPresent() )
++                    error("promote: Leaf %s found in path but not at the tail: %s") ;
++            }
++            // Check the page/index pointers
++            Optional<AccessStep> z2 = path.getPath().stream().filter(e -> e.node.ptrs.get(e.idx) != e.page.getId()).findFirst() ;
++            if ( z2.isPresent() )
++                error("promote: path error: %s in %s", z2.get(), path) ;
++        }
++        
++        // ---- Clone the access path nodes.
++        // Path is the route to this page - it does not include this page. 
++        // Work from the bottom to the top, the reverse order of AccessPath
++        if ( loggingCall )
++            log(pageLog, "   page>> %s", page.label()) ;
++        boolean changed = page.promote();
++        if ( loggingCall ) {
++            if ( changed )
++                log(pageLog, "   page<< %s", page.label()) ;
++            else
++                log(pageLog, "    .. no change") ;
++        }
++        
++        if ( changed )
++            page.write() ;  // Being careful.
++
++        // Even if the page did not change, make sure the chain is dealt with.
++        // e.g. promote in place policies. 
++        //if ( changed ) {
++        if ( path != null ) {
++            // Sequence of promote1 calls? + root.
++
++            List<AccessStep> steps = path.getPath() ;
++
++            int newPtr = page.getId() ;
++            BPTreePage newPage = null ;
++            boolean previousChanged = changed ; 
++            BPTreeNode newRoot = null ; 
++
++            if ( logging(pageLog) )
++                log(pageLog, "Path: %s", path) ;
++            // Duplicate from bottom to top.
++            for ( int i = steps.size() - 1 ; i >= 0 ; i--  ) {
++                AccessStep s = steps.get(i) ;
++                // duplicate
++                BPTreeNode n = s.node ;
++                if ( logging(pageLog) )
++                    log(pageLog, "    >> %s", n) ;
++
++                changed = n.promote() ;
++                
++                if ( previousChanged ) {
++                    // Even if n did not change, if it's sub changed, need to update s.idx. 
++                    n.ptrs.set(s.idx, newPtr) ;
++                } else {
++                    if ( ! changed ) {
++                        if ( logging(pageLog) )
++                            log(pageLog, "    .. no change") ;
++                        continue ;
++                    }
++                }
++                
++                previousChanged = changed ;
++
++                if ( logging(pageLog) )
++                    log(pageLog, "    << %s", n) ;
++
++                if ( n.isRoot() ) {
++                    if ( newRoot != null)
++                        throw new InternalErrorException("New root already found") ;
++                    newRoot = n ;
++                }
++                newPtr = n.getId() ;
++                n.write() ;
++
++            }
++            if ( newRoot != null ) {
++                if ( loggingCall )
++                    log(pageLog, "  new root %s", newRoot) ;
++                page.bpTree.newRoot(newRoot) ;
++            }
++        } // end of "if ( path != null )"
++    }
++    
++    /**
++     * The initial tree is a single root node with no records block below it. It
++     * is an illegal tree. We make it by creating a real tree, deleting and
++     * freeing the records block from the root, then resetting the records block
++     * manager. This is to avoid having specialized creating code in
++     * BPlusTreeFactory solely for the rewriter.
++     */
++    public static BPlusTree createRootOnlyBPTree(BPlusTreeParams bptParams, 
++                                                 BufferChannel bptState, 
++                                                 BlockMgr blkMgrNodes, 
++                                                 BlockMgr blkMgrRecords) {
++        BPlusTree bpt = BPlusTreeFactory.createNonTxn(bptParams, bptState, blkMgrNodes, blkMgrRecords) ;
++        
++        BPTreeRecordsMgr recordsMgr = bpt.getRecordsMgr() ;
++        
++        BPTreeRecords recordsPage = recordsMgr.getWrite(0) ;
++//        recordsPage.getRecordBuffer().clear();
++//        recordsMgr.write(recordsPage) ;
++        recordsMgr.free(recordsPage);
++//        recordsMgr.release(recordsPage);
++        recordsMgr.resetAlloc(0);
++        
++        BPTreeNodeMgr nodeMgr = bpt.getNodeManager() ;
++        
++        // Alter the root node.
++        BPTreeNode root = nodeMgr.getWrite(BPlusTreeParams.RootId, BPlusTreeParams.RootParent) ;
++        int rootId = root.getId() ;
++        if ( rootId != 0 )
++            throw new BPTreeException("**** Not the root: " + rootId) ;
++
++        // Undo the records block.
++        root.getPtrBuffer().clear() ;
++        root.getRecordBuffer().clear() ;
++        //root.setCount(-1);
++        nodeMgr.write(root);
++        nodeMgr.release(root);
++
++        // Now a broken tree of one root block and no records.
++        return bpt ;
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPTStateMgr.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPTStateMgr.java
index 0000000,0000000..cb7049c
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPTStateMgr.java
@@@ -1,0 -1,0 +1,89 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.bplustree;
++
++import org.apache.jena.atlas.logging.FmtLog ;
++import org.seaborne.dboe.base.file.BufferChannel ;
++import org.seaborne.dboe.transaction.txn.StateMgrData ;
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++/** Manage the persistent state of the tree
++ * 
++ * This consists of last commited root, and the limits on the blocks for both
++ * nodes and records. 
++ * 
++ * (rootId/int-as-long, nodeAllocLimit/long, recordsAllocLimit/long) 
++ */
++public class BPTStateMgr extends StateMgrData {
++    private static Logger log = LoggerFactory.getLogger(BPTStateMgr.class) ;
++    
++    private static int idxRoot                  = 0 ;
++    private static int idxNodeBlocksLimit       = 1 ;
++    private static int idxRecordsBlocksLimit    = 2 ;
++    
++    private int currentRoot()                   { return (int)super.get(idxRoot) ; }
++    private long nodeBlocksLimit()              { return super.get(idxNodeBlocksLimit) ; }
++    private long recordsBlocksLimit()           { return super.get(idxRecordsBlocksLimit) ; }
++    
++    private void currentRoot(int x)             { super.set(idxRoot, x) ; }
++    private void nodeBlocksLimit(long x)        { super.set(idxNodeBlocksLimit, x) ; }
++    private void recordsBlocksLimit(long x)     { super.set(idxRecordsBlocksLimit, x) ; }
++
++    private boolean LOGGING = BPT.Logging ;
++
++    public BPTStateMgr(BufferChannel storage) {
++        // These values are the values for a null tree (no blocks).
++        super(storage, 0L, 0L, 0L) ;
++    }
++
++    /*package*/ void setState(int rootIdx, long nodeBlkLimit, long recordsBlkLimit) {
++        currentRoot(rootIdx) ;
++        nodeBlocksLimit(nodeBlkLimit) ;
++        recordsBlocksLimit(recordsBlkLimit) ;
++        log("Set") ;
++        setDirtyFlag() ;
++        // But don't write it.
++    }
++    @Override
++    protected void writeStateEvent() {
++        log("Write") ;
++    }
++
++    @Override
++    protected void readStateEvent() {
++        log("Read") ;
++    }
++
++    private void log(String operation) {
++        if ( LOGGING )
++            FmtLog.info(log, "%s state:  root=%d // node block limit = %d // records block limit %d", operation, currentRoot(), nodeBlocksLimit(), recordsBlocksLimit()) ;
++    }
++    
++    public int getRoot() {
++        return currentRoot() ;
++    }
++
++    public long getNodeBlocksLimit() {
++        return nodeBlocksLimit() ;
++    }
++
++    public long getRecordsBlocksLimit() {
++        return recordsBlocksLimit() ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPTreeException.java
----------------------------------------------------------------------
diff --cc jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPTreeException.java
index 0000000,0000000..423785a
new file mode 100644
--- /dev/null
+++ b/jena-db/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/bplustree/BPTreeException.java
@@@ -1,0 -1,0 +1,26 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.dboe.trans.bplustree;
++
++import org.seaborne.dboe.DBOpEnvException ;
++
++public class BPTreeException extends DBOpEnvException
++{
++    public BPTreeException() {}
++    public BPTreeException(String msg) { super(msg) ; }
++}


[33/50] [abbrv] jena git commit: JENA-1396: Merge //github/afs/mantis as subdirectory jena-db/

Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/setup/StoreParamsConst.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/setup/StoreParamsConst.java
index 0000000,0000000..b3a352a
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/setup/StoreParamsConst.java
@@@ -1,0 -1,0 +1,104 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.setup;
++
++import org.seaborne.dboe.base.block.FileMode ;
++import org.seaborne.dboe.sys.Names ;
++import org.seaborne.tdb2.sys.SystemTDB ;
++
++public class StoreParamsConst {
++    // SystemParams are built with a SystemParamsBuilder
++    // Initial values are the system defaults.
++    
++    /** Database and query configuration */ 
++    // Key names are the base name -  encode/decode may add a prefix.
++    
++    public static final String   fFileMode             = "file_mode" ;
++    public static final FileMode fileMode              = SystemTDB.fileMode() ;
++    
++    public static final String   fBlockReadCacheSize   = "block_read_cache_size" ;
++    public static final int      blockReadCacheSize    = SystemTDB.BlockReadCacheSize ;
++    
++    public static final String   fBlockWriteCacheSize  = "block_write_cache_size" ;
++    public static final int      blockWriteCacheSize   = SystemTDB.BlockWriteCacheSize ;
++    
++    public static final String   fNode2NodeIdCacheSize = "node2nodeid_cache_size" ;
++    public static final int      Node2NodeIdCacheSize  = SystemTDB.Node2NodeIdCacheSize ;
++    
++    public static final String   fNodeId2NodeCacheSize = "nodeid2node_cache_size" ;
++    public static final int      NodeId2NodeCacheSize  = SystemTDB.NodeId2NodeCacheSize ;
++    
++    public static final String   fNodeMissCacheSize    = "node_miss_cache_size" ;
++    public static final int      NodeMissCacheSize     = SystemTDB.NodeMissCacheSize ;
++    
++    /** Database layout - ignored after a database is created */
++    public static final String   fBlockSize            = "block_size" ;
++    public static final int      blockSize             = SystemTDB.BlockSize ;
++    
++    public static final String   fNodeTableBaseName    = "nodetable" ;
++    public static final String   nodeTableBaseName     = Names.nodeTableBaseName ;
++    
++    public static final String   fPrimaryIndexTriples  = "triple_index_primary" ;
++    public static final String   primaryIndexTriples   = Names.primaryIndexTriples ;
++    
++    public static final String   fTripleIndexes        = "triple_indexes" ;
++    public static final String[] tripleIndexes         = Names.tripleIndexes ;
++    
++    public static final String   fPrimaryIndexQuads    = "quad_index_primary" ;
++    public static final String   primaryIndexQuads     = Names.primaryIndexQuads ;
++    
++    public static final String   fQuadIndexes          = "quad_indexes" ;
++    public static final String[] quadIndexes           = Names.quadIndexes ;
++    
++    public static final String   fPrefixTableBaseName  = "prefixtable" ;
++    public static final String   prefixTableBaseName   = Names.prefixTableBaseName ;
++   
++    public static final String   fPrimaryIndexPrefix   = "prefix_index_primary" ;
++    public static final String   primaryIndexPrefix    = Names.primaryIndexPrefix ;
++    
++    public static final String   fPrefixIndexes        = "prefix_indexes" ;
++    public static final String[] prefixIndexes         = Names.prefixIndexes ;
++
++    // Must be after the constants above to get initialization order right
++    // because StoreParamsBuilder uses these constants.
++     
++    /** The system default parameters for on-disk databases. */
++    static StoreParams dftStoreParams = StoreParams.builder().build() ;
++
++    /** The system default parameters for in-memory databases. */
++    static StoreParams dftMemStoreParams = StoreParams.builder()
++        .fileMode(FileMode.direct)
++        // Small block caches, mainly so it behaves like a direct on-disk database.  
++        .blockReadCacheSize(10)
++        .blockWriteCacheSize(10)
++        .node2NodeIdCacheSize(10000)
++        .nodeId2NodeCacheSize(10000)
++        .nodeMissCacheSize(100)
++        .build() ;
++    
++    /** The "small store" parameters. */
++    static StoreParams smallStoreParams = StoreParams.builder()
++        .fileMode(FileMode.direct)
++        .blockReadCacheSize(100)
++        .blockWriteCacheSize(100)
++        .node2NodeIdCacheSize(10000)
++        .nodeId2NodeCacheSize(10000)
++        .nodeMissCacheSize(100)
++        .build() ;
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/setup/StoreParamsDynamic.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/setup/StoreParamsDynamic.java
index 0000000,0000000..3d21185
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/setup/StoreParamsDynamic.java
@@@ -1,0 -1,0 +1,53 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.setup;
++
++import org.seaborne.dboe.base.block.FileMode ;
++
++/** Store parameters that can be adjusted after a store has been created,
++ *  and given different values when the JVM attachs to a store area. 
++ *  (They are still fixed for any given database once created in a JVM.) 
++ */
++
++public interface StoreParamsDynamic {
++    
++    /** Store-wide file access mode */ 
++    public FileMode getFileMode() ;
++    public boolean isSetFileMode() ;
++    
++    /** Block read cache (note: mapped files do not have a block cache) */
++    public Integer getBlockReadCacheSize() ;
++    public boolean isSetBlockReadCacheSize() ;
++
++    /** Block write cache (note: mapped files do not have a block cache) */
++    public Integer getBlockWriteCacheSize() ;
++    public boolean isSetBlockWriteCacheSize() ;
++    
++    /** Node cache for Node->NodeId. */
++    public Integer getNode2NodeIdCacheSize() ;
++    public boolean isSetNode2NodeIdCacheSize() ;
++    
++    /** Node cache for NodeId->Node. Important for SPARQL results. */
++    public Integer getNodeId2NodeCacheSize() ;
++    public boolean isSetNodeId2NodeCacheSize() ;
++
++    /** Node cache for recording known misses */
++    public Integer getNodeMissCacheSize() ;
++    public boolean isSetNodeMissCacheSize() ;
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/setup/StoreParamsFactory.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/setup/StoreParamsFactory.java
index 0000000,0000000..5f5aa4b
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/setup/StoreParamsFactory.java
@@@ -1,0 -1,0 +1,93 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.setup;
++
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.dboe.sys.Names;
++import org.seaborne.tdb2.TDB2Factory ;
++
++public class StoreParamsFactory {
++    /** Choose the StoreParams.  This is the policy applied when creating or reattaching to a database.
++     *  (extracted and put here to keep the size of DatasetBuildStd  
++     * <p>
++     * If the location has parameters in a <tt>tdb.cfg</tt> file, use them, as modified by any
++     * application-supplied internal parameters.
++     * <p>
++     * Otherwise, if this is a new database, use the application provided
++     * parameters or if there are no application provided 
++     * parameters, use the system default parameters.
++     * Write the parameters used to the location in <tt>tdb.cfg</tt>
++     * <p>If this is an existing database and there are no location recorded parameters,
++     * use system default parameters, modified by application parameters.   
++     * <p>
++     * Notes:
++     * <ul>
++     * <li><i>Modification</i> involves setting any of the parameters than can vary from run to run. 
++     * These are the cache sizes and the file mode. 
++     * <li><i>Block size</i>: it is critical that this set correctly. Silent corruption
++     * of a database may occur if this is changed.  At the moment, it is not possible to provide
++     * a complete check of block size.
++     * <ul>  
++     * <p>
++     * Do not edit store parameters recorded at a location after the database has been created.
++     * Only the dynamic parameters cna be safely changed. That is better done though the application
++     * providing some parameters in the {@link TDB2Factory} call.
++     * <p>
++     * This includes changing filenames,  indexing choices and block size. 
++     * Otherwise, the database may be permanetly and irrecovably corrupted.
++     * You have been warned. 
++     * 
++     * @param location The place where the database is or will be.
++     * @param isNew  Whether the database is being created or whether there is an existing database.
++     * @param pApp   Application-provide store parameters.
++     * @param pLoc   Store parameters foud at the location.
++     * @param pDft   System default store parameters.
++     * @return       StoreParams
++     * 
++     * @see StoreParams
++     * @see StoreParamsDynamic
++     */
++    public static StoreParams decideStoreParams(Location location, boolean isNew, StoreParams pApp, StoreParams pLoc, StoreParams pDft) {
++        StoreParams p = null ;
++        if ( pLoc != null ) {
++            // pLoc so use it, modify by pApp.
++            // Covers new and reconnect cases.
++            p = pLoc ;
++            if ( pApp != null )
++                p = StoreParamsBuilder.modify(pLoc, pApp) ;
++            return p ;
++        }
++        // No pLoc.
++        // Use pApp if available.  Write to location if new.
++        if ( pApp != null ) {
++            if ( isNew ) {
++                if ( ! location.isMem() ) {
++                    String filename = location.getPath(Names.TDB_CONFIG_FILE) ;
++                    StoreParamsCodec.write(filename, pApp) ;
++                }
++                return pApp ;
++            }
++            // Not new : pLoc is implicitly pDft.
++            return StoreParamsBuilder.modify(pDft, pApp) ;
++        }
++        // no pLoc, no pApp
++        return pDft ;
++    }
++
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/setup/TDBBuilder.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/setup/TDBBuilder.java
index 0000000,0000000..88e18fc
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/setup/TDBBuilder.java
@@@ -1,0 -1,0 +1,290 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.setup;
++
++import java.io.File ;
++import java.io.FileFilter ;
++import java.util.UUID ;
++
++import org.apache.jena.atlas.lib.tuple.TupleMap ;
++import org.apache.jena.sparql.engine.main.QC ;
++import org.apache.jena.sparql.engine.optimizer.reorder.ReorderLib ;
++import org.seaborne.dboe.DBOpEnvException ;
++import org.seaborne.dboe.base.file.* ;
++import org.seaborne.dboe.base.record.RecordFactory ;
++import org.seaborne.dboe.index.Index ;
++import org.seaborne.dboe.index.RangeIndex ;
++import org.seaborne.dboe.sys.Names ;
++import org.seaborne.dboe.trans.bplustree.BPlusTree ;
++import org.seaborne.dboe.trans.bplustree.BPlusTreeFactory ;
++import org.seaborne.dboe.trans.data.TransBinaryDataFile ;
++import org.seaborne.dboe.transaction.txn.ComponentId ;
++import org.seaborne.dboe.transaction.txn.TransactionCoordinator ;
++import org.seaborne.dboe.transaction.txn.TransactionalBase ;
++import org.seaborne.dboe.transaction.txn.TransactionalSystem ;
++import org.seaborne.dboe.transaction.txn.journal.Journal ;
++import org.seaborne.tdb2.TDBException ;
++import org.seaborne.tdb2.solver.OpExecutorTDB1 ;
++import org.seaborne.tdb2.store.* ;
++import org.seaborne.tdb2.store.nodetable.NodeTable ;
++import org.seaborne.tdb2.store.nodetable.NodeTableCache ;
++import org.seaborne.tdb2.store.nodetable.NodeTableInline ;
++import org.seaborne.tdb2.store.nodetable.NodeTableTRDF ;
++import org.seaborne.tdb2.store.nodetupletable.NodeTupleTable ;
++import org.seaborne.tdb2.store.nodetupletable.NodeTupleTableConcrete ;
++import org.seaborne.tdb2.store.tupletable.TupleIndex ;
++import org.seaborne.tdb2.store.tupletable.TupleIndexRecord ;
++import org.seaborne.tdb2.sys.SystemTDB ;
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++// Takes from TDB2Builder
++// Converted to statics.
++
++/** Build TDB2 databases.
++ * <p>
++ * <b>Do not call these operations directly - use StoreConnection.</b>
++ */
++public class TDBBuilder {
++    private Logger log = LoggerFactory.getLogger(TDBBuilder.class) ;
++    
++    public static DatasetGraphTxn build(Location location) {
++        return build(location, StoreParams.getDftStoreParams()) ;
++    }
++
++    public static DatasetGraphTxn build(Location location, StoreParams appParams) {
++        StoreParams locParams = StoreParamsCodec.read(location) ;
++        StoreParams dftParams = StoreParams.getDftStoreParams() ;
++        // This can write the chosen parameters if necessary (new database, appParams != null, locParams == null)
++        boolean newArea = isNewDatabaseArea(location) ;
++        if ( newArea ) {
++        }
++        StoreParams params = StoreParamsFactory.decideStoreParams(location, newArea, appParams, locParams, dftParams) ;
++        return create(location, params).build$() ; 
++    }
++
++    private DatasetGraphTxn build$() {
++        NodeTable nodeTable = buildNodeTable(params.getNodeTableBaseName()) ;
++        
++        TripleTable tripleTable = buildTripleTable(nodeTable) ;
++        QuadTable quadTable = buildQuadTable(nodeTable) ;
++        
++        NodeTable nodeTablePrefixes = buildNodeTable(params.getPrefixTableBaseName()) ;
++        DatasetPrefixesTDB prefixes = buildPrefixTable(nodeTablePrefixes) ;
++        
++        TransactionalSystem trans = new TransactionalBase(txnCoord) ;
++        DatasetGraphTxn dsg = new DatasetGraphTDB(trans, 
++                                                  tripleTable, quadTable, prefixes, 
++                                                  ReorderLib.fixed(), location, params) ;
++        QC.setFactory(dsg.getContext(), OpExecutorTDB1.OpExecFactoryTDB) ;
++        txnCoord.start() ;
++        return dsg ;
++    }
++
++    public static TransactionCoordinator buildTransactionCoordinator(Location location) {
++        Journal journal = Journal.create(location) ;
++        TransactionCoordinator txnCoord = new TransactionCoordinator(journal) ;
++        return txnCoord ;
++    }
++
++    public static String choosePrimaryForIndex(StoreParams params, String index) {
++        String primary3 = params.getPrimaryIndexTriples() ;
++        String primary4 = params.getPrimaryIndexQuads() ;
++        
++        if ( index.length() == primary3.length() )
++            return primary3 ;
++        if ( index.length() == primary4.length() )
++            return primary4 ;
++        throw new DBOpEnvException("Can't find primary for '"+index+"'") ;
++    }
++
++    // ---- Object starts
++    final Location location ;
++    final StoreParams params ;
++    final ComponentIdMgr componentIdMgr ;
++    final TransactionCoordinator txnCoord ;
++
++    private TDBBuilder(TransactionCoordinator txnCoord, Location location, StoreParams params, ComponentIdMgr componentIdMgr) {
++        this.txnCoord = txnCoord ;
++        this.location = location ;
++        this.params = params ;
++        this.componentIdMgr = componentIdMgr ;
++    }
++
++    public Location getLocation()               { return location ; }
++    public StoreParams getParams()              { return params ; }
++    public TransactionCoordinator getTxnCoord() { return txnCoord ; }
++
++    public static TDBBuilder create(Location location) {
++        return create(location, StoreParams.getDftStoreParams()) ; 
++    }
++    
++    public static TDBBuilder create(Location location, StoreParams params) {
++        TransactionCoordinator txnCoord = buildTransactionCoordinator(location) ;
++        return new TDBBuilder(txnCoord, location, params, new ComponentIdMgr(UUID.randomUUID())) ;
++    }
++
++    public static TDBBuilder create(TransactionCoordinator txnCoord, Location location, StoreParams params) {
++        return new TDBBuilder(txnCoord, location, params, new ComponentIdMgr(UUID.randomUUID())) ;
++    }
++
++    /** Look at a directory and see if it is a new area */
++    private static boolean isNewDatabaseArea(Location location) {
++        if ( location.isMem() )
++            return true ;
++        File d = new File(location.getDirectoryPath()) ;
++        if ( !d.exists() )
++            return true ;
++        FileFilter ff = fileFilterNewDB ;
++        File[] entries = d.listFiles(ff) ;
++        return entries.length == 0 ;
++    }
++    
++    /** FileFilter
++     * Skips "..", ".", "tdb.lock" and "tdb.cfg"
++     * 
++     */
++  private static  FileFilter fileFilterNewDB  = (pathname)->{
++        String fn = pathname.getName() ;
++        if ( fn.equals(".") || fn.equals("..") )
++            return false ;
++        if ( pathname.isDirectory() )
++            return true ;
++        if ( fn.equals(Names.TDB_LOCK_FILE) )
++            return false ;
++        if ( fn.equals(Names.TDB_CONFIG_FILE) )
++            return false ;
++        
++        return true ;
++    } ;
++    
++    public TripleTable buildTripleTable(NodeTable nodeTable) {    
++        String primary = params.getPrimaryIndexTriples() ;
++        String[] indexes = params.getTripleIndexes() ;
++
++        // Validation checks - common index forms.  
++        if ( indexes.length != 3 && indexes.length != 2 )
++            error(log, "Wrong number of triple table indexes: "+String.join(",", indexes)) ;
++        log.debug("Triple table: "+primary+" :: "+String.join(",", indexes)) ;
++
++        TupleIndex tripleIndexes[] = makeTupleIndexes(primary, indexes) ;
++
++        if ( tripleIndexes.length != indexes.length )
++            error(log, "Wrong number of triple table tuples indexes: "+tripleIndexes.length) ;
++        TripleTable tripleTable = new TripleTable(tripleIndexes, nodeTable) ;
++        return tripleTable ;
++    }
++
++    public QuadTable buildQuadTable(NodeTable nodeTable) {    
++        String primary = params.getPrimaryIndexQuads() ;
++        String[] indexes = params.getQuadIndexes() ;
++
++        // Validation checks - common index forms.  
++        if ( indexes.length != 6 && indexes.length != 4 )
++            error(log, "Wrong number of quad table indexes: "+String.join(",", indexes)) ;
++        log.debug("Quad table: "+primary+" :: "+String.join(",", indexes)) ;
++
++        TupleIndex tripleIndexes[] = makeTupleIndexes(primary, indexes) ;
++
++        if ( tripleIndexes.length != indexes.length )
++            error(log, "Wrong number of triple table tuples indexes: "+tripleIndexes.length) ;
++        QuadTable tripleTable = new QuadTable(tripleIndexes, nodeTable) ;
++        return tripleTable ;
++    }
++
++    public DatasetPrefixesTDB buildPrefixTable(NodeTable prefixNodes) {
++        String primary = params.getPrimaryIndexPrefix() ;
++        String[] indexes = params.getPrefixIndexes() ;
++
++        TupleIndex prefixIndexes[] = makeTupleIndexes(primary, indexes) ;
++        if ( prefixIndexes.length != 1 )
++            error(log, "Wrong number of triple table tuples indexes: "+prefixIndexes.length) ;
++
++        // No cache - the prefix mapping is a cache
++        //NodeTable prefixNodes = makeNodeTable(location, pnNode2Id, pnId2Node, -1, -1, -1)  ;
++        NodeTupleTable prefixTable = new NodeTupleTableConcrete(primary.length(),
++                                                                prefixIndexes,
++                                                                prefixNodes) ;
++        DatasetPrefixesTDB prefixes = new DatasetPrefixesTDB(prefixTable) ; 
++        log.debug("Prefixes: "+primary+" :: "+String.join(",", indexes)) ;
++        return prefixes ;
++    }
++
++    // ---- Build structures
++
++    public TupleIndex[] makeTupleIndexes(String primary, String[] indexNames) {
++        int indexRecordLen = primary.length()*SystemTDB.SizeOfNodeId ;
++        TupleIndex indexes[] = new TupleIndex[indexNames.length] ;
++        for (int i = 0 ; i < indexes.length ; i++) {
++            String indexName = indexNames[i] ;
++            String indexLabel = indexNames[i] ;
++            indexes[i] = buildTupleIndex(primary, indexName, indexLabel) ;
++        }
++        return indexes ;
++    }
++
++    public TupleIndex buildTupleIndex(String primary, String index, String name) {
++        TupleMap cmap = TupleMap.create(primary, index) ;
++        RecordFactory rf = new RecordFactory(SystemTDB.SizeOfNodeId * cmap.length(), 0) ;
++        RangeIndex rIdx = buildRangeIndex(rf, index) ;
++        TupleIndex tIdx = new TupleIndexRecord(primary.length(), cmap, index, rf, rIdx) ;
++        return tIdx ;
++    }
++    
++    public RangeIndex buildRangeIndex(RecordFactory recordFactory, String name) {
++        ComponentId cid = componentIdMgr.getComponentId(name) ;
++        FileSet fs = new FileSet(location, name) ;
++        BPlusTree bpt = BPlusTreeFactory.createBPTree(cid, fs, recordFactory) ;
++        txnCoord.add(bpt) ;
++        return bpt ;
++    }
++    
++    public NodeTable buildNodeTable(String name) {
++        NodeTable nodeTable = buildBaseNodeTable(name) ;
++        nodeTable = NodeTableCache.create(nodeTable, params) ;
++        nodeTable = NodeTableInline.create(nodeTable) ;
++        return nodeTable ;
++    }
++
++    public NodeTable buildBaseNodeTable(String name) {
++        RecordFactory recordFactory = new RecordFactory(SystemTDB.LenNodeHash, SystemTDB.SizeOfNodeId) ;
++        Index index = buildRangeIndex(recordFactory, name) ;
++        
++        String dataname = name+"-data" ; 
++        TransBinaryDataFile transBinFile = buildBinaryDataFile(dataname) ;
++        txnCoord.add(transBinFile) ;
++        return new NodeTableTRDF(index, transBinFile) ;
++    }
++    
++    public TransBinaryDataFile buildBinaryDataFile(String name) {
++        ComponentId cid = componentIdMgr.getComponentId(name) ;
++        FileSet fs = new FileSet(location, name) ; 
++        BinaryDataFile binFile = FileFactory.createBinaryDataFile(fs, Names.extObjNodeData) ;
++        BufferChannel pState = FileFactory.createBufferChannel(fs, Names.extBdfState) ;
++        // ComponentId mgt.
++        TransBinaryDataFile transBinFile = new TransBinaryDataFile(binFile, cid, pState) ;
++        return transBinFile ;
++    }
++    
++    private void error(Logger log, String msg)
++    {
++        if ( log != null )
++            log.error(msg) ;
++        throw new TDBException(msg) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/setup/TDBDatasetDetails.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/setup/TDBDatasetDetails.java
index 0000000,0000000..4d4797a
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/setup/TDBDatasetDetails.java
@@@ -1,0 -1,0 +1,75 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.setup;
++
++import org.apache.jena.atlas.lib.ArrayUtils ;
++import org.apache.jena.atlas.logging.Log ;
++import org.seaborne.tdb2.store.DatasetGraphTDB ;
++import org.seaborne.tdb2.store.nodetable.NodeTable ;
++import org.seaborne.tdb2.store.nodetable.NodeTableCache ;
++import org.seaborne.tdb2.store.nodetable.NodeTableInline ;
++import org.seaborne.tdb2.store.tupletable.TupleIndex ;
++
++public class TDBDatasetDetails {
++    public final TupleIndex[] tripleIndexes ;
++    public final TupleIndex[] quadIndexes ;
++    public final NodeTable ntTop ;
++    public NodeTableInline ntInline ;
++    public NodeTableCache ntCache ;
++    public NodeTable ntBase ;
++    
++    
++    public TDBDatasetDetails(DatasetGraphTDB dsg) {
++        ntTop = dsg.getTripleTable().getNodeTupleTable().getNodeTable() ;
++        tripleIndexes = ArrayUtils.copy(dsg.getTripleTable().getNodeTupleTable().getTupleTable().getIndexes()) ;
++        quadIndexes = ArrayUtils.copy(dsg.getQuadTable().getNodeTupleTable().getTupleTable().getIndexes()) ;
++        
++        fillInNodeTableDetails() ;
++        fillInIndexDetails() ;
++    }
++
++    private void fillInNodeTableDetails() {
++        // Nodetable.
++        NodeTable ntx = ntTop ;
++        while(ntx.wrapped() != null ) {
++            if ( ntx instanceof NodeTableInline ) {
++                if ( ntInline != null )
++                    Log.warn(this, "Multiple NodeTableInline") ;
++                ntInline = (NodeTableInline)ntx ;
++            }
++            else if ( ntx instanceof NodeTableCache ) {
++                if ( ntCache != null )
++                    Log.warn(this, "Multiple NodeTableCache") ;
++                ntCache = (NodeTableCache)ntx ;
++            }
++            ntx = ntx.wrapped() ;
++        } 
++        
++        ntBase = ntx ;
++        
++        if ( ntInline == null )
++            Log.warn(this, "No NodeTableInline") ;
++        if ( ntCache == null )
++            Log.warn(this, "No NodeTableCache") ;
++        if ( ntBase == null )
++            Log.warn(this, "No base NodeTable") ;
++    }
++    
++    private void fillInIndexDetails() {
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/Abortable.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/Abortable.java
index 0000000,0000000..14dfa58
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/Abortable.java
@@@ -1,0 -1,0 +1,20 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.solver;
++
++interface Abortable { public void abort() ; }

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/BindingNodeId.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/BindingNodeId.java
index 0000000,0000000..57b73a9
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/BindingNodeId.java
@@@ -1,0 -1,0 +1,112 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.solver;
++
++import java.util.HashMap;
++import java.util.Iterator ;
++import java.util.Map;
++
++import org.apache.jena.atlas.lib.Map2 ;
++
++
++
++import org.apache.jena.sparql.core.Var ;
++import org.apache.jena.sparql.engine.binding.Binding ;
++import org.seaborne.tdb2.store.NodeId ;
++
++/** Class for a Binding-like structure except it works on NodeIds, not on Nodes */  
++public class BindingNodeId extends Map2<Var, NodeId>
++{
++    // This is the parent binding - which may be several steps up the chain. 
++    // This just carried around for later use when we go BindingNodeId back to Binding.
++    private final Binding parentBinding ;
++
++    // Possible optimization: there are at most 3 possible values so HashMap is overkill.
++    // Use a chain of small objects.
++    
++    private BindingNodeId(Map<Var, NodeId> map1, Map2<Var, NodeId> map2, Binding parentBinding)
++    {
++        super(map1, map2) ;
++        this.parentBinding = parentBinding ;
++    }
++
++    // Make from an existing BindingNodeId 
++    public BindingNodeId(BindingNodeId other)
++    {
++        this(new HashMap<Var, NodeId>(), other, other.getParentBinding()) ;
++    }
++    
++    // Make from an existing Binding 
++    public BindingNodeId(Binding binding)
++    {
++        this(new HashMap<Var, NodeId>(), null, binding) ;
++    }
++
++    public BindingNodeId()
++    {
++        this(new HashMap<Var, NodeId>(), null, null) ;
++    }
++    
++    public Binding getParentBinding()    { return parentBinding ; } 
++    
++    //@Override public NodeId get(Var v)    { return super.get(v) ; } 
++    
++    @Override public void put(Var v, NodeId n)
++    {
++        if ( v == null || n == null )
++            throw new IllegalArgumentException("("+v+","+n+")") ;
++        super.put(v, n) ;
++    }
++    
++    public void putAll(BindingNodeId other)
++    {
++        Iterator<Var> vIter = other.iterator() ;
++        
++        for ( ; vIter.hasNext() ; )
++        {
++            Var v = vIter.next() ;
++            if ( v == null )
++                throw new IllegalArgumentException("Null key") ;
++            NodeId n = other.get(v) ;
++            if ( n == null )
++                throw new IllegalArgumentException("("+v+","+n+")") ;
++            super.put(v, n) ;
++        }
++    }
++    
++    @Override
++    public String toString()
++    {
++        StringBuilder sb = new StringBuilder() ;
++        
++        boolean first = true ;
++        for ( Var v : this )
++        {
++            if ( ! first )
++                sb.append(" ") ;
++            first = false ;
++            NodeId x = get(v) ;
++            sb.append(v) ;
++            sb.append(" = ") ;
++            sb.append(x) ;
++        }
++            
++        return sb.toString() ;
++        
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/BindingTDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/BindingTDB.java
index 0000000,0000000..3eed2d4
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/BindingTDB.java
@@@ -1,0 -1,0 +1,154 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.solver;
++
++import java.util.* ;
++
++import org.apache.jena.atlas.logging.Log ;
++import org.apache.jena.graph.Node ;
++import org.apache.jena.riot.out.NodeFmtLib ;
++import org.apache.jena.sparql.core.Var ;
++import org.apache.jena.sparql.engine.binding.Binding ;
++import org.apache.jena.sparql.engine.binding.BindingBase ;
++import org.seaborne.tdb2.store.NodeId ;
++import org.seaborne.tdb2.store.nodetable.NodeTable ;
++
++/** Bind that delays turning a NodeId into a Node until explicitly needed by get() */
++
++public class BindingTDB extends BindingBase
++{
++    private final NodeTable nodeTable ;
++    private final BindingNodeId idBinding ;
++    
++    private static final boolean caching = false ;
++    // Whether the cache is worthwhile is unclear - the NodeTable keeps a cache. 
++    private final Map<Var,Node> cache = ( caching ? new HashMap<>() : null ) ;
++
++    public BindingTDB(BindingNodeId idBinding, NodeTable nodeTable)
++    {
++        // BindingNodeId contains the bindings actually used  copied down when created. 
++        super(idBinding.getParentBinding()) ;
++        this.idBinding = idBinding ;
++        this.nodeTable = nodeTable ;
++    }
++
++    @Override
++    protected int size1() { return idBinding.size(); }
++    
++    private List<Var> vars = null ;
++    
++    /** Iterate over all the names of variables. */
++    @Override
++    protected Iterator<Var> vars1() 
++    {
++        if ( vars == null )
++            vars = calcVars() ;
++        return vars.iterator() ;
++    }
++
++    private List<Var> calcVars()
++    {
++        List<Var> vars = new ArrayList<>(4) ;
++        // Only if not in parent.
++        // A (var/value) binding may have been copied down to record it's NodeId.  
++        
++        Binding b = idBinding.getParentBinding() ;
++        
++        Iterator<Var> iter = idBinding.iterator() ;
++        for ( Var v : idBinding )
++        {
++            if ( b == null || ! b.contains(v) )
++                vars.add(v) ;
++        }
++        return vars ;
++    }
++    
++    @Override
++    protected boolean isEmpty1()
++    {
++        return size1() == 0 ;
++    }
++
++    @Override
++    public boolean contains1(Var var)
++    {
++        return idBinding.containsKey(var) ;
++    }
++    
++    public BindingNodeId getBindingId() { return idBinding ; }
++    
++    public NodeId getNodeId(Var var)
++    {
++        NodeId id = idBinding.get(var) ;
++        if ( id != null )
++            return id ;
++        
++        if ( parent == null )
++            return null ; 
++
++        // Maybe in the parent.
++        if ( parent instanceof BindingTDB )
++            return ((BindingTDB)parent).getNodeId(var) ;
++        return null ;
++    }
++    
++    @Override
++    public Node get1(Var var)
++    {
++        try {
++            Node n = cacheGet(var) ;
++            if ( n != null )
++                return n ;
++            
++            NodeId id = idBinding.get(var) ;
++            if ( id == null )
++                return null ; 
++            n = nodeTable.getNodeForNodeId(id) ;
++            // Update cache.
++            cachePut(var, n) ;
++            return n ;
++        } catch (Exception ex)
++        {
++            Log.error(this, String.format("get1(%s)", var), ex) ;
++            return null ;
++        }
++    }
++
++    private void cachePut(Var var, Node n)
++    {
++        if ( cache != null ) cache.put(var, n) ; 
++    }
++
++    private Node cacheGet(Var var)
++    { 
++        if ( cache == null ) return null ;
++        return cache.get(var) ;
++    }
++    
++    @Override
++    protected void format(StringBuffer sbuff, Var var)
++    {
++        NodeId id = idBinding.get(var) ;
++        String extra = "" ;
++        if ( id != null )
++            extra = "/"+id ;
++        Node node = get(var) ;
++        String tmp = NodeFmtLib.displayStr(node) ;
++        sbuff.append("( ?"+var.getVarName()+extra+" = "+tmp+" )") ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/OpExecutorTDB1.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/OpExecutorTDB1.java
index 0000000,0000000..f103917
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/OpExecutorTDB1.java
@@@ -1,0 -1,0 +1,427 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.solver;
++
++import java.util.function.Predicate;
++
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.atlas.logging.Log ;
++import org.apache.jena.graph.Graph ;
++import org.apache.jena.graph.Node ;
++import org.apache.jena.sparql.ARQInternalErrorException ;
++import org.apache.jena.sparql.algebra.Op ;
++import org.apache.jena.sparql.algebra.op.* ;
++import org.apache.jena.sparql.algebra.optimize.TransformFilterPlacement ;
++import org.apache.jena.sparql.core.BasicPattern ;
++import org.apache.jena.sparql.core.Quad ;
++import org.apache.jena.sparql.core.Substitute ;
++import org.apache.jena.sparql.core.Var ;
++import org.apache.jena.sparql.engine.ExecutionContext ;
++import org.apache.jena.sparql.engine.QueryIterator ;
++import org.apache.jena.sparql.engine.iterator.QueryIterPeek ;
++import org.apache.jena.sparql.engine.main.OpExecutor ;
++import org.apache.jena.sparql.engine.main.OpExecutorFactory ;
++import org.apache.jena.sparql.engine.main.QC ;
++import org.apache.jena.sparql.engine.main.iterator.QueryIterGraph ;
++import org.apache.jena.sparql.engine.optimizer.reorder.ReorderProc ;
++import org.apache.jena.sparql.engine.optimizer.reorder.ReorderTransformation ;
++import org.apache.jena.sparql.expr.ExprList ;
++import org.apache.jena.sparql.mgt.Explain ;
++import org.seaborne.tdb2.store.DatasetGraphTDB ;
++import org.seaborne.tdb2.store.GraphTDB ;
++import org.seaborne.tdb2.store.NodeId ;
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++/** TDB executor for algebra expressions.  It is the standard ARQ executor
++ *  except for basic graph patterns and filtered basic graph patterns (currently).  
++ * 
++ * See also: StageGeneratorDirectTDB, a non-reordering 
++ */
++public class OpExecutorTDB1 extends OpExecutor
++{
++    private static final Logger log = LoggerFactory.getLogger(OpExecutorTDB1.class) ;
++    
++    public final static OpExecutorFactory OpExecFactoryTDB = new OpExecutorFactory()
++    {
++        @Override
++        public OpExecutor create(ExecutionContext execCxt)
++        { return new OpExecutorTDB1(execCxt) ; }
++    } ;
++    
++    private final boolean isForTDB ;
++    
++    // A new compile object is created for each op compilation.
++    // So the execCxt is changing as we go through the query-compile-execute process  
++    public OpExecutorTDB1(ExecutionContext execCxt)
++    {
++        super(execCxt) ;
++        // NB. The dataset may be a TDB one, or a general one.
++        // Any merged union graph magic (for a TDB dataset was handled
++        // in QueryEngineTDB).
++        
++        isForTDB = (execCxt.getActiveGraph() instanceof GraphTDB) ;
++    }
++
++    @Override
++    protected QueryIterator exec(Op op, QueryIterator input) {
++        if ( level < 0 )
++            // Print only at top level (and we're called before level++) 
++            Explain.explain("TDB", op, super.execCxt.getContext()) ;
++        return super.exec(op, input) ;
++    } 
++    
++    // Retrieving nodes isn't so bad because they will be needed anyway.
++    // And if their duplicates, likely to be cached.
++    // Need to work with SolverLib which wraps the NodeId bindgins with a converter. 
++    
++    @Override
++    protected QueryIterator execute(OpDistinct opDistinct, QueryIterator input)
++    {
++        return super.execute(opDistinct, input) ;
++    }
++    
++    @Override
++    protected QueryIterator execute(OpReduced opReduced, QueryIterator input)
++    {
++        return super.execute(opReduced, input) ;
++    }
++    
++    @Override
++    protected QueryIterator execute(OpFilter opFilter, QueryIterator input)
++    {
++        if ( ! isForTDB )
++            return super.execute(opFilter, input) ;
++        
++        // If the filter does not apply to the input??
++        // Where does ARQ catch this?
++        
++        // (filter (bgp ...))
++        if ( OpBGP.isBGP(opFilter.getSubOp()) )
++        {
++            // Still may be a TDB graph in a non-TDB dataset (e.g. a named model)
++            GraphTDB graph = (GraphTDB)execCxt.getActiveGraph() ;
++            OpBGP opBGP = (OpBGP)opFilter.getSubOp() ;
++            return executeBGP(graph, opBGP, input, opFilter.getExprs(), execCxt) ;
++        }
++        
++        // (filter (quadpattern ...))
++        if ( opFilter.getSubOp() instanceof OpQuadPattern )
++        {
++            OpQuadPattern quadPattern = (OpQuadPattern)opFilter.getSubOp() ;
++            DatasetGraphTDB ds = (DatasetGraphTDB)execCxt.getDataset() ;
++            return optimizeExecuteQuads(ds, input,
++                                        quadPattern.getGraphNode(), quadPattern.getBasicPattern(),
++                                        opFilter.getExprs(), execCxt) ;
++        }
++    
++        // (filter (anything else))
++        return super.execute(opFilter, input) ;
++        }
++
++    // ---- Triple patterns
++    
++    @Override
++    protected QueryIterator execute(OpBGP opBGP, QueryIterator input)
++    {
++        if ( ! isForTDB )
++            return super.execute(opBGP, input) ;
++        
++        GraphTDB graph = (GraphTDB)execCxt.getActiveGraph() ;
++        return executeBGP(graph, opBGP, input, null, execCxt) ;
++       
++    }
++
++    @Override
++    protected QueryIterator execute(OpQuadPattern quadPattern, QueryIterator input)
++    {
++        if ( ! isForTDB )
++            return super.execute(quadPattern, input) ;
++            
++    //        DatasetGraph dg = execCxt.getDataset() ;
++    //        if ( ! ( dg instanceof DatasetGraphTDB ) )
++    //            throw new InternalErrorException("Not a TDB backed dataset in quad pattern execution") ;
++        
++        DatasetGraphTDB ds = (DatasetGraphTDB)execCxt.getDataset() ;
++        BasicPattern bgp = quadPattern.getBasicPattern() ;
++        Node gn = quadPattern.getGraphNode() ;
++        return optimizeExecuteQuads(ds, input, gn, bgp, null, execCxt) ;
++    }
++
++    @Override
++    protected QueryIterator execute(OpGraph opGraph, QueryIterator input)
++    {
++        // Path evaluation or dataset sets which do not go straight to the DatasetGraphTDB  
++        return new QueryIterGraph(input, opGraph, execCxt) ;
++    }
++
++    /** Execute a BGP (and filters) on a TDB graph, which may be in default storage or it may be a named graph */ 
++    private static QueryIterator executeBGP(GraphTDB graph, OpBGP opBGP, QueryIterator input, ExprList exprs, 
++                                            ExecutionContext execCxt)
++    {
++        // Is it the real default graph (normal route or explicitly named)?
++        if ( ! isDefaultGraphStorage(graph.getGraphName()))
++        {
++            // Not default storage - it's a named graph in storage. 
++            DatasetGraphTDB ds = graph.getDSG() ;
++            return optimizeExecuteQuads(ds, input, graph.getGraphName(), opBGP.getPattern(), exprs, execCxt) ;
++        }
++        
++        // Execute a BGP on the real default graph
++        return optimizeExecuteTriples(graph, input, opBGP.getPattern(), exprs, execCxt) ;
++    }
++
++    /** Execute, with optimization, a basic graph pattern on the default graph storage */
++    private static QueryIterator optimizeExecuteTriples(GraphTDB graph, QueryIterator input,
++                                                        BasicPattern pattern, ExprList exprs,
++                                                        ExecutionContext execCxt)
++    {
++        if ( ! input.hasNext() )
++            return input ;
++    
++        // -- Input
++        // Must pass this iterator into the next stage.
++        if ( pattern.size() >= 2 )
++        {
++            // Must be 2 or triples to reorder. 
++            ReorderTransformation transform = graph.getDSG().getReorderTransform() ;
++            if ( transform != null )
++            {
++                QueryIterPeek peek = QueryIterPeek.create(input, execCxt) ;
++                input = peek ; // Must pass on
++                pattern = reorder(pattern, peek, transform) ;
++            }
++        }
++        // -- Filter placement
++            
++        Op op = null ;
++        if ( exprs != null )
++            op = TransformFilterPlacement.transform(exprs, pattern) ;
++        else
++            op = new OpBGP(pattern) ;
++        
++        return plainExecute(op, input, execCxt) ;
++    }
++
++    /** Execute, with optimization, a quad pattern */
++    private static QueryIterator optimizeExecuteQuads(DatasetGraphTDB ds, 
++                                                      QueryIterator input, 
++                                                      Node gn, BasicPattern bgp,
++                                                      ExprList exprs, ExecutionContext execCxt)
++    {
++        if ( ! input.hasNext() )
++            return input ;
++
++        // ---- Graph names with special meaning. 
++
++        gn = decideGraphNode(gn, execCxt) ;
++        if ( gn == null )
++            return optimizeExecuteTriples(ds.getDefaultGraphTDB(), input, bgp, exprs, execCxt) ;
++        
++        // ---- Execute quads+filters
++        if ( bgp.size() >= 2 )
++        {
++            ReorderTransformation transform = ds.getReorderTransform() ;
++    
++            if ( transform != null )
++            {
++                QueryIterPeek peek = QueryIterPeek.create(input, execCxt) ;
++                input = peek ; // Original input now invalid.
++                bgp = reorder(bgp, peek, transform) ;
++            }
++        }
++        // -- Filter placement
++        Op op = null ;
++        if ( exprs != null )
++            op = TransformFilterPlacement.transform(exprs, gn, bgp) ;
++        else
++            op = new OpQuadPattern(gn, bgp) ;
++
++        return plainExecute(op, input, execCxt) ;
++    }
++
++    /** Execute without modification of the op - does <b>not</b> apply special graph name translations */ 
++    private static QueryIterator plainExecute(Op op, QueryIterator input, ExecutionContext execCxt)
++    {
++        // -- Execute
++        // Switch to a non-reordering executor
++        // The Op may be a sequence due to TransformFilterPlacement
++        // so we need to do a full execution step, not go straight to the SolverLib.
++        
++        ExecutionContext ec2 = new ExecutionContext(execCxt) ;
++        ec2.setExecutor(plainFactory) ;
++
++        // Solve without going through this executor again.
++        // There would be issues of nested patterns but this is only a
++        // (filter (bgp...)) or (filter (quadpattern ...)) or sequences of these.
++        // so there are no nested patterns to reorder.
++        return QC.execute(op, input, ec2) ;
++    }
++
++    private static BasicPattern reorder(BasicPattern pattern, QueryIterPeek peek, ReorderTransformation transform)
++    {
++        if ( transform != null )
++        {
++            // This works by getting one result from the peek iterator,
++            // and creating the more gounded BGP. The tranform is used to
++            // determine the best order and the transformation is returned. This
++            // transform is applied to the unsubstituted pattern (which will be
++            // substituted as part of evaluation.
++            
++            if ( ! peek.hasNext() )
++                throw new ARQInternalErrorException("Peek iterator is already empty") ;
++ 
++            BasicPattern pattern2 = Substitute.substitute(pattern, peek.peek() ) ;
++            // Calculate the reordering based on the substituted pattern.
++            ReorderProc proc = transform.reorderIndexes(pattern2) ;
++            // Then reorder original patten
++            pattern = proc.reorder(pattern) ;
++        }
++        return pattern ;
++    }
++    
++    /** Handle special graph node names.  
++     * Returns null for default graph in storage (use the triple table).
++     * Returns Node.ANY for the union graph
++     */
++    public static Node decideGraphNode(Node gn, ExecutionContext execCxt)
++    {
++     // ---- Graph names with special meaning. 
++    
++        // Graph names with special meaning:
++        //   Quad.defaultGraphIRI -- the IRI used in GRAPH <> to mean the default graph.
++        //   Quad.defaultGraphNodeGenerated -- the internal marker node used for the quad form of queries.
++        //   Quad.unionGraph -- the IRI used in GRAPH <> to mean the union of named graphs
++    
++        if ( isDefaultGraphStorage(gn) ) 
++        {
++            // Storage concrete, default graph. 
++            // Either outside GRAPH (no implicit union)
++            // or using the "name" of the default graph
++            return null ;
++        }
++
++        // Not default storage graph.
++        // ---- Union (RDF Merge) of named graphs
++
++        if ( Quad.isUnionGraph(gn) ) 
++            return Node.ANY ;
++        boolean doingUnion = false ;
++        
++        return gn ;
++    }
++
++    // Is this a query against the real default graph in the storage (in a 3-tuple table). 
++    private static boolean isDefaultGraphStorage(Node gn)
++    {
++        if ( gn == null )
++            return true ;
++        
++        // Is it the implicit name for default graph.
++        if ( Quad.isDefaultGraph(gn) )
++            // Not accessing the union of named graphs as the default graph
++            // and pattern is directed to the default graph.
++            return true ;
++    
++        return false ;
++    }
++    
++    @Override
++    protected QueryIterator execute(OpDatasetNames dsNames, QueryIterator input)
++    { 
++        DatasetGraphTDB ds = (DatasetGraphTDB)execCxt.getDataset() ;
++        Predicate<Tuple<NodeId>> filter = QC2.getFilter(execCxt.getContext()) ;
++        Node gn = dsNames.getGraphNode() ;
++        if ( Var.isVar(gn) )
++            return SolverLib.graphNames(ds, dsNames.getGraphNode(), input, filter, execCxt) ;
++        else
++            return SolverLib.testForGraphName(ds, dsNames.getGraphNode(), input, filter, execCxt) ;
++    }
++
++    // ---- OpExecute factories and plain executor.
++    
++    private static OpExecutorFactory plainFactory = new OpExecutorPlainFactoryTDB() ;
++    private static class OpExecutorPlainFactoryTDB implements OpExecutorFactory
++    {
++        @Override
++        public OpExecutor create(ExecutionContext execCxt)
++        {
++            return new OpExecutorPlainTDB(execCxt) ;
++        }
++    }
++
++    /** An op executor that simply executes a BGP or QuadPattern without any reordering */ 
++    private static class OpExecutorPlainTDB extends OpExecutor
++    {
++        Predicate<Tuple<NodeId>> filter = null ;
++        
++        public OpExecutorPlainTDB(ExecutionContext execCxt)
++        {
++            super(execCxt) ;
++            filter = QC2.getFilter(execCxt.getContext()) ;
++        }
++        
++        @Override
++        public QueryIterator execute(OpBGP opBGP, QueryIterator input)
++        {
++            Graph g = execCxt.getActiveGraph() ;
++            
++            if ( g instanceof GraphTDB )
++            {
++                BasicPattern bgp = opBGP.getPattern() ;
++                Explain.explain("Execute", bgp, execCxt.getContext()) ;
++                // Triple-backed (but may be named as explicit default graph).
++                //return SolverLib.execute((GraphTDB)g, bgp, input, filter, execCxt) ;
++                GraphTDB gtdb = (GraphTDB)g ;
++                Node gn = decideGraphNode(gtdb.getGraphName(), execCxt) ;
++                return SolverLib.execute(gtdb.getDSG(), gn, bgp, input, filter, execCxt) ;
++            }
++            Log.warn(this, "Non-GraphTDB passed to OpExecutorPlainTDB") ;
++            return super.execute(opBGP, input) ;
++        }
++        
++        @Override
++        public QueryIterator execute(OpQuadPattern opQuadPattern, QueryIterator input)
++        {
++            Node gn = opQuadPattern.getGraphNode() ;
++            gn = decideGraphNode(gn, execCxt) ;
++            
++            if ( execCxt.getDataset() instanceof DatasetGraphTDB )
++            {
++                DatasetGraphTDB ds = (DatasetGraphTDB)execCxt.getDataset() ;
++                Explain.explain("Execute", opQuadPattern.getPattern(), execCxt.getContext()) ;
++                BasicPattern bgp = opQuadPattern.getBasicPattern() ;
++                return SolverLib.execute(ds, gn, bgp, input, filter, execCxt) ;
++            }
++            // Maybe a TDB named graph inside a non-TDB dataset.
++            Graph g = execCxt.getActiveGraph() ;
++            if ( g instanceof GraphTDB )
++            {
++                // Triples graph from TDB (which is the default graph of the dataset),
++                // used a named graph in a composite dataset.
++                BasicPattern bgp = opQuadPattern.getBasicPattern() ;
++                Explain.explain("Execute", bgp, execCxt.getContext()) ;
++                // Don't pass in G -- gn may be different.
++                return SolverLib.execute(((GraphTDB)g).getDSG(), gn, bgp, input, filter, execCxt) ;
++            }
++            Log.warn(this, "Non-DatasetGraphTDB passed to OpExecutorPlainTDB") ;
++            return super.execute(opQuadPattern, input) ;
++        }
++
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/QC2.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/QC2.java
index 0000000,0000000..b96ece2
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/QC2.java
@@@ -1,0 -1,0 +1,48 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.solver;
++
++import java.util.function.Predicate;
++
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.sparql.util.Context ;
++import org.seaborne.tdb2.TDBException ;
++import org.seaborne.tdb2.store.NodeId ;
++import org.seaborne.tdb2.sys.SystemTDB ;
++
++public class QC2
++{
++    public static Predicate<Tuple<NodeId>> getFilter(Context context)
++    {
++        Object x = context.get(SystemTDB.symTupleFilter) ;
++
++        try {
++            @SuppressWarnings("unchecked")
++            Predicate<Tuple<NodeId>> f = (Predicate<Tuple<NodeId>>)x ;
++            return f ;
++        } catch (ClassCastException ex)
++        {
++            throw new TDBException("Not a Filter<Tuple<NodeId>>:"+x, ex) ;
++        }
++    }
++
++    public static void setFilter(Context context, Predicate<Tuple<NodeId>> filter)
++    {
++        context.set(SystemTDB.symTupleFilter, filter) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/QueryEngineTDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/QueryEngineTDB.java
index 0000000,0000000..dfece29
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/QueryEngineTDB.java
@@@ -1,0 -1,0 +1,206 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.solver;
++
++import org.apache.jena.atlas.lib.Lib ;
++import org.apache.jena.query.Query ;
++import org.apache.jena.sparql.algebra.Algebra ;
++import org.apache.jena.sparql.algebra.Op ;
++import org.apache.jena.sparql.core.DatasetDescription ;
++import org.apache.jena.sparql.core.DatasetGraph ;
++import org.apache.jena.sparql.core.DynamicDatasets ;
++import org.apache.jena.sparql.core.Substitute ;
++import org.apache.jena.sparql.engine.Plan ;
++import org.apache.jena.sparql.engine.QueryEngineFactory ;
++import org.apache.jena.sparql.engine.QueryEngineRegistry ;
++import org.apache.jena.sparql.engine.QueryIterator ;
++import org.apache.jena.sparql.engine.binding.Binding ;
++import org.apache.jena.sparql.engine.binding.BindingFactory ;
++import org.apache.jena.sparql.engine.iterator.QueryIteratorWrapper ;
++import org.apache.jena.sparql.engine.main.QueryEngineMain ;
++import org.apache.jena.sparql.mgt.Explain ;
++import org.apache.jena.sparql.util.Context ;
++import org.seaborne.tdb2.TDB2 ;
++import org.seaborne.tdb2.TDBException ;
++import org.seaborne.tdb2.migrate.A2 ;
++import org.seaborne.tdb2.store.DatasetGraphTDB ;
++import org.seaborne.tdb2.sys.TDBInternal ;
++
++// This exists to intercept the query execution setup.
++//  e.g choose the transformation optimizations
++// then to make the quad form.
++// TDB also uses a custom OpExecutor to intercept certain part 
++// of the Op evaluations
++
++public class QueryEngineTDB extends QueryEngineMain
++{
++    // ---- Wiring
++    static public QueryEngineFactory getFactory() { return factory ; } 
++    static public void register()       { QueryEngineRegistry.addFactory(factory) ; }
++    static public void unregister()     { QueryEngineRegistry.removeFactory(factory) ; }
++    
++    private Binding initialInput ;
++
++    // ---- Object
++    protected QueryEngineTDB(Op op, DatasetGraphTDB dataset, Binding input, Context context)
++    {
++        super(op, dataset, input, context) ;
++        this.initialInput = input ;
++    }
++    
++    private boolean doingDynamicDatasetBySpecialDataset = false ;
++    
++    protected QueryEngineTDB(Query query, DatasetGraphTDB dataset, Binding input, Context cxt)
++    { 
++        super(query, dataset, input, cxt) ; 
++        DatasetDescription dsDesc = DatasetDescription.create(query, context) ;
++        
++        if ( dsDesc != null )
++        {
++            doingDynamicDatasetBySpecialDataset = true ;
++            super.dataset = DynamicDatasets.dynamicDataset(dsDesc, dataset, cxt.isTrue(TDB2.symUnionDefaultGraph) ) ;
++        }
++        this.initialInput = input ; 
++    }
++    
++    // Choose the algebra-level optimizations to invoke. 
++    @Override
++    protected Op modifyOp(Op op)
++    {
++        op = Substitute.substitute(op, initialInput) ;
++        // Optimize (high-level)
++        op = super.modifyOp(op) ;
++
++        // Quadification
++        // Only apply if not a rewritten DynamicDataset
++        if ( ! doingDynamicDatasetBySpecialDataset )
++            op = Algebra.toQuadForm(op) ;
++        
++        // Record it.
++        setOp(op) ;
++        return op ;
++    }
++
++    @Override
++    public QueryIterator eval(Op op, DatasetGraph dsg, Binding input, Context context)
++    {
++        // Top of execution of a query.
++        // Op is quad'ed by now but there still may be some (graph ....) forms e.g. paths
++        
++        // Fix DatasetGraph for global union.
++        if ( context.isTrue(TDB2.symUnionDefaultGraph) && ! doingDynamicDatasetBySpecialDataset ) 
++        {
++            op = A2.unionDefaultGraphQuads(op) ;
++            Explain.explain("REWRITE(Union default graph)", op, context) ;
++        }
++        QueryIterator results = super.eval(op, dsg, input, context) ;
++        results = new QueryIteratorMaterializeBinding(results) ;
++        return results ; 
++    }
++    
++    /** Copy from any TDB internal BindingTDB to a Binding that
++     *  does not have any connection to the database.   
++     */
++    static class QueryIteratorMaterializeBinding extends QueryIteratorWrapper
++    {
++        public QueryIteratorMaterializeBinding(QueryIterator qIter)
++        {
++            super(qIter) ;
++        }
++        
++        @Override
++        protected Binding moveToNextBinding()
++        { 
++            Binding b = super.moveToNextBinding() ;
++            b = BindingFactory.materialize(b) ;
++            return b ;
++        }
++    }
++    
++    // Execution time (needs wiring to ARQ).
++    public long getMillis() { return -1 ; }
++    
++    // ---- Factory
++    protected static QueryEngineFactory factory = new QueryEngineFactoryTDB() ;
++        
++    protected static class QueryEngineFactoryTDB implements QueryEngineFactory
++    {
++        private static boolean isHandledByTDB(DatasetGraph dataset) {
++            return TDBInternal.isBackedByTDB(dataset);
++        }
++        
++        protected DatasetGraphTDB dsgToQuery(DatasetGraph dataset) {
++            try { 
++                return TDBInternal.requireStorage(dataset);
++            } catch (TDBException ex) {
++                // Check to a more specific message. 
++                throw new TDBException("Internal inconsistency: trying to execute query on unrecognized kind of DatasetGraph: "+Lib.className(dataset)) ;
++            }
++        }
++        
++        @Override
++        public boolean accept(Query query, DatasetGraph dataset, Context context) 
++        { return isHandledByTDB(dataset) ; }
++
++        @Override
++        public Plan create(Query query, DatasetGraph dataset, Binding input, Context context)
++        {
++            QueryEngineTDB engine = new QueryEngineTDB(query, dsgToQuery(dataset), input, context) ;
++            return engine.getPlan() ;
++        }
++        
++        @Override
++        public boolean accept(Op op, DatasetGraph dataset, Context context) 
++        { return isHandledByTDB(dataset) ; }
++
++        @Override
++        public Plan create(Op op, DatasetGraph dataset, Binding binding, Context context)
++        {
++            QueryEngineTDB engine = new QueryEngineTDB(op, dsgToQuery(dataset), binding, context) ;
++            return engine.getPlan() ;
++        }
++    }
++    
++//    // By rewrite, not using a general purpose dataset with the right graphs in.
++//    private static Op dynamicDatasetOp(Op op,  Context context)
++//    {
++//        Transform transform = null ;
++//    
++//        try {
++//            @SuppressWarnings("unchecked")
++//            Set<Node> defaultGraphs = (Set<Node>)(context.get(SystemTDB.symDatasetDefaultGraphs)) ;
++//            @SuppressWarnings("unchecked")
++//            Set<Node> namedGraphs = (Set<Node>)(context.get(SystemTDB.symDatasetNamedGraphs)) ;
++//            if ( defaultGraphs != null || namedGraphs != null )
++//                transform = new TransformDynamicDataset(defaultGraphs, 
++//                                                        namedGraphs, 
++//                                                        context.isTrue(TDB.symUnionDefaultGraph)) ;
++//        } catch (ClassCastException ex)
++//        {
++//            Log.warn(QueryEngineTDB.class, "Bad dynamic dataset description (ClassCastException)", ex) ;
++//            transform = null ;
++//            return op ;
++//        }
++//
++//        // Apply dynamic dataset modifications.
++//        if ( transform != null )
++//            op = Transformer.transform(transform, op) ;
++//        return op ;
++//    }        
++//    
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/QueryIterTDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/QueryIterTDB.java
index 0000000,0000000..49b9cee
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/QueryIterTDB.java
@@@ -1,0 -1,0 +1,58 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.solver;
++
++import java.util.Iterator ;
++import java.util.List ;
++
++import org.apache.jena.sparql.engine.ExecutionContext ;
++import org.apache.jena.sparql.engine.QueryIterator ;
++import org.apache.jena.sparql.engine.binding.Binding ;
++import org.apache.jena.sparql.engine.iterator.QueryIterPlainWrapper ;
++
++public class QueryIterTDB extends QueryIterPlainWrapper
++{
++    final private QueryIterator originalInput ;
++    private List<Abortable> killList ;
++    
++    // The original input needs closing as well.
++    public QueryIterTDB(Iterator<Binding> iterBinding, List<Abortable> killList , QueryIterator originalInput, ExecutionContext execCxt)
++    {
++        super(iterBinding, execCxt) ;
++        this.originalInput = originalInput ;
++        this.killList = killList ;
++    }
++    
++    @Override
++    protected void closeIterator()
++    { 
++        if ( originalInput != null )
++            originalInput.close();
++        super.closeIterator() ;
++    }
++
++    @Override
++    protected void requestCancel()
++    { 
++        if ( killList != null )
++            for ( Abortable it : killList )
++                it.abort() ;
++        if ( originalInput != null )
++            originalInput.cancel(); 
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/SolverLib.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/SolverLib.java
index 0000000,0000000..43dd3ab
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/SolverLib.java
@@@ -1,0 -1,0 +1,340 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.solver;
++
++import java.util.* ;
++import java.util.function.Function ;
++import java.util.function.Predicate ;
++
++import org.apache.jena.atlas.iterator.Iter ;
++import org.apache.jena.atlas.iterator.IteratorWrapper ;
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.atlas.lib.tuple.TupleFactory ;
++import org.apache.jena.graph.Node ;
++import org.apache.jena.graph.Triple ;
++import org.apache.jena.query.QueryCancelledException ;
++import org.apache.jena.sparql.core.BasicPattern ;
++import org.apache.jena.sparql.core.Quad ;
++import org.apache.jena.sparql.core.Var ;
++import org.apache.jena.sparql.engine.ExecutionContext ;
++import org.apache.jena.sparql.engine.QueryIterator ;
++import org.apache.jena.sparql.engine.binding.Binding ;
++import org.apache.jena.sparql.engine.binding.BindingFactory ;
++import org.apache.jena.sparql.engine.binding.BindingMap ;
++import org.apache.jena.sparql.engine.iterator.QueryIterNullIterator ;
++import org.seaborne.tdb2.TDBException ;
++import org.seaborne.tdb2.lib.NodeLib ;
++import org.seaborne.tdb2.store.DatasetGraphTDB ;
++import org.seaborne.tdb2.store.GraphTDB ;
++import org.seaborne.tdb2.store.NodeId ;
++import org.seaborne.tdb2.store.nodetable.NodeTable ;
++import org.seaborne.tdb2.store.nodetupletable.NodeTupleTable ;
++import org.seaborne.tdb2.sys.TDBInternal ;
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++/** Utilities used within the TDB BGP solver : local TDB store */
++public class SolverLib
++{
++    private static Logger log = LoggerFactory.getLogger(SolverLib.class) ; 
++    
++    /** Non-reordering execution of a basic graph pattern, given a iterator of bindings as input */ 
++    public static QueryIterator execute(GraphTDB graph, BasicPattern pattern, 
++                                        QueryIterator input, Predicate<Tuple<NodeId>> filter,
++                                        ExecutionContext execCxt)
++    {
++        // Maybe default graph or named graph.
++        NodeTupleTable ntt = graph.getNodeTupleTable() ;
++        return execute(ntt, graph.getGraphName(), pattern, input, filter, execCxt) ;
++    }
++    
++    /** Non-reordering execution of a quad pattern, given a iterator of bindings as input.
++     *  GraphNode is Node.ANY for execution over the union of named graphs.
++     *  GraphNode is null for execution over the real default graph.
++     */ 
++    public static QueryIterator execute(DatasetGraphTDB ds, Node graphNode, BasicPattern pattern,
++                                        QueryIterator input, Predicate<Tuple<NodeId>> filter,
++                                        ExecutionContext execCxt)
++    {
++        NodeTupleTable ntt = ds.chooseNodeTupleTable(graphNode) ;
++        return execute(ntt, graphNode, pattern, input, filter, execCxt) ;
++    }
++    
++    public static Iterator<BindingNodeId> convertToIds(Iterator<Binding> iterBindings, NodeTable nodeTable)
++    { return Iter.map(iterBindings, convFromBinding(nodeTable)) ; }
++    
++    /** Convert from Iterator<BindingNodeId> to Iterator<Binding>, conversion "on demand" 
++     * (in convToBinding(BindingNodeId, NodeTable)
++     */
++    public static Iterator<Binding> convertToNodes(Iterator<BindingNodeId> iterBindingIds, NodeTable nodeTable)
++    { return Iter.map(iterBindingIds, bindingNodeIds -> convToBinding(bindingNodeIds, nodeTable)) ; }
++    
++    // The worker.  Callers choose the NodeTupleTable.  
++    //     graphNode may be Node.ANY, meaning we should make triples unique.
++    //     graphNode may be null, meaning default graph
++
++    private static QueryIterator execute(NodeTupleTable nodeTupleTable, Node graphNode, BasicPattern pattern, 
++                                         QueryIterator input, Predicate<Tuple<NodeId>> filter,
++                                         ExecutionContext execCxt)
++    {
++        if ( Quad.isUnionGraph(graphNode) )
++            graphNode = Node.ANY ;
++        if ( Quad.isDefaultGraph(graphNode) )
++            graphNode = null ;
++        
++        List<Triple> triples = pattern.getList() ;
++        boolean anyGraph = (graphNode==null ? false : (Node.ANY.equals(graphNode))) ;
++        
++        int tupleLen = nodeTupleTable.getTupleTable().getTupleLen() ;
++        if ( graphNode == null ) {
++            if ( 3 != tupleLen )
++                throw new TDBException("SolverLib: Null graph node but tuples are of length "+tupleLen) ;
++        } else {
++            if ( 4 != tupleLen )
++                throw new TDBException("SolverLib: Graph node specified but tuples are of length "+tupleLen) ;
++        }
++        
++        // Convert from a QueryIterator (Bindings of Var/Node) to BindingNodeId
++        NodeTable nodeTable = nodeTupleTable.getNodeTable() ;
++        
++        Iterator<BindingNodeId> chain = Iter.map(input, SolverLib.convFromBinding(nodeTable)) ;
++        List<Abortable> killList = new ArrayList<>() ;
++        
++        for ( Triple triple : triples )
++        {
++            Tuple<Node> tuple = null ;
++            if ( graphNode == null )
++                // 3-tuples
++                tuple = TupleFactory.tuple(triple.getSubject(), triple.getPredicate(), triple.getObject()) ;
++            else
++                // 4-tuples.
++                tuple = TupleFactory.tuple(graphNode, triple.getSubject(), triple.getPredicate(), triple.getObject()) ;
++            chain = solve(nodeTupleTable, tuple, anyGraph, chain, filter, execCxt) ;
++            chain = makeAbortable(chain, killList) ; 
++        }
++        
++        // DEBUG POINT
++        if ( false )
++        {
++            if ( chain.hasNext())
++                chain = Iter.debug(chain) ;
++            else
++                System.out.println("No results") ;
++        }
++        
++        // Timeout wrapper ****
++        // QueryIterTDB gets called async.
++        // Iter.abortable?
++        // Or each iterator has a place to test.
++        // or pass in a thing to test?
++        
++        
++        // Need to make sure the bindings here point to parent.
++        Iterator<Binding> iterBinding = convertToNodes(chain, nodeTable) ;
++        
++        // "input" will be closed by QueryIterTDB but is otherwise unused.
++        // "killList" will be aborted on timeout.
++        return new QueryIterTDB(iterBinding, killList, input, execCxt) ;
++    }
++    
++    /** Create an abortable iterator, storing it in the killList.
++     *  Just return the input iterator if kilList is null. 
++     */
++    static <T> Iterator<T> makeAbortable(Iterator<T> iter, List<Abortable> killList)
++    {
++        if ( killList == null )
++            return iter ;
++        IterAbortable<T> k = new IterAbortable<>(iter) ;
++        killList.add(k) ;
++        return k ;
++    }
++    
++    /** Iterator that adds an abort operation which can be called
++     *  at any time, including from another thread, and causes the
++     *  iterator to throw an exception when next touched (hasNext, next).  
++     */
++    static class IterAbortable<T> extends IteratorWrapper<T> implements Abortable
++    {
++        volatile boolean abortFlag = false ;
++        
++        public IterAbortable(Iterator<T> iterator)
++        {
++            super(iterator) ;
++        }
++        
++        /** Can call asynchronously at anytime */
++        @Override
++        public void abort() { 
++            abortFlag = true ;
++        }
++        
++        @Override
++        public boolean hasNext()
++        {
++            if ( abortFlag )
++                throw new QueryCancelledException() ;
++            return iterator.hasNext() ; 
++        }
++        
++        @Override
++        public T next()
++        {
++            if ( abortFlag )
++                throw new QueryCancelledException() ;
++            return iterator.next() ; 
++        }
++    }
++    
++    public static Iterator<BindingNodeId> solve(NodeTupleTable nodeTupleTable, 
++                                                Tuple<Node> tuple,
++                                                boolean anyGraph,
++                                                Iterator<BindingNodeId> chain, Predicate<Tuple<NodeId>> filter,
++                                                ExecutionContext execCxt)
++    {
++        return new StageMatchTuple(nodeTupleTable, chain, tuple, anyGraph, filter, execCxt) ;
++    }
++
++    public static Binding convToBinding(BindingNodeId bindingNodeIds, NodeTable nodeTable) {
++        if ( true )
++            return new BindingTDB(bindingNodeIds, nodeTable) ;
++        else {
++            // Makes nodes immediately. Causing unnecessary NodeTable accesses
++            // (e.g. project)
++            BindingMap b = BindingFactory.create() ;
++            for (Var v : bindingNodeIds) {
++                NodeId id = bindingNodeIds.get(v) ;
++                Node n = nodeTable.getNodeForNodeId(id) ;
++                b.add(v, n) ;
++            }
++            return b ;
++        }
++    }
++    
++    // Transform : Binding ==> BindingNodeId
++    public static Function<Binding, BindingNodeId> convFromBinding(final NodeTable nodeTable)
++    {
++        return binding -> SolverLib.convert(binding, nodeTable);
++    }
++    
++    /** Binding ==> BindingNodeId, given a NodeTable */
++    public static BindingNodeId convert(Binding binding, NodeTable nodeTable) 
++    {
++        if ( binding instanceof BindingTDB )
++            return ((BindingTDB)binding).getBindingId() ;
++
++        BindingNodeId b = new BindingNodeId(binding) ;
++        // and copy over, getting NodeIds.
++        Iterator<Var> vars = binding.vars() ;
++
++        for ( ; vars.hasNext() ; )
++        {
++            Var v = vars.next() ;
++            Node n = binding.get(v) ;  
++            if ( n == null )
++                // Variable mentioned in the binding but not actually defined.
++                // Can occur with BindingProject
++                continue ;
++
++            // Rely on the node table cache for efficency - we will likely be
++            // repeatedly looking up the same node in different bindings.
++            NodeId id = nodeTable.getNodeIdForNode(n) ;
++            // Even put in "does not exist" for a node now known not to be in the DB.
++            b.put(v, id) ;
++        }
++        return b ;
++    }
++    
++    /** Find whether a specific graph name is in the quads table. */
++    public static QueryIterator testForGraphName(DatasetGraphTDB ds, Node graphNode, QueryIterator input,
++                                                 Predicate<Tuple<NodeId>> filter, ExecutionContext execCxt) {
++        NodeId nid = TDBInternal.getNodeId(ds, graphNode) ;
++        boolean exists = !NodeId.isDoesNotExist(nid) ;
++        if ( exists ) {
++            // Node exists but is it used in the quad position?
++            NodeTupleTable ntt = ds.getQuadTable().getNodeTupleTable() ;
++            // Don't worry about abortable - this iterator should be fast
++            // (with normal indexing - at least one G???).
++            // Either it finds a starting point, or it doesn't.  We are only 
++            // interested in the first .hasNext.
++            Iterator<Tuple<NodeId>> iter1 = ntt.find(nid, NodeId.NodeIdAny, NodeId.NodeIdAny, NodeId.NodeIdAny) ;
++            if ( filter != null )
++                iter1 = Iter.filter(iter1, filter) ;
++            exists = iter1.hasNext() ;
++        }
++
++        if ( exists )
++            return input ;
++        else {
++            input.close() ;
++            return QueryIterNullIterator.create(execCxt) ;
++        }
++    }
++
++    /** Find all the graph names in the quads table. */
++    public static QueryIterator graphNames(DatasetGraphTDB ds, Node graphNode, QueryIterator input,
++                                           Predicate<Tuple<NodeId>> filter, ExecutionContext execCxt) {
++        List<Abortable> killList = new ArrayList<>() ;
++        Iterator<Tuple<NodeId>> iter1 = ds.getQuadTable().getNodeTupleTable().find(NodeId.NodeIdAny, NodeId.NodeIdAny,
++                                                                                   NodeId.NodeIdAny, NodeId.NodeIdAny) ;
++        if ( filter != null )
++            iter1 = Iter.filter(iter1, filter) ;
++
++        Iterator<NodeId> iter2 = Iter.map(iter1, t -> t.get(0)) ;
++        // Project is cheap - don't brother wrapping iter1
++        iter2 = makeAbortable(iter2, killList) ;
++
++        Iterator<NodeId> iter3 = Iter.distinct(iter2) ;
++        iter3 = makeAbortable(iter3, killList) ;
++
++        Iterator<Node> iter4 = NodeLib.nodes(ds.getQuadTable().getNodeTupleTable().getNodeTable(), iter3) ;
++
++        final Var var = Var.alloc(graphNode) ;
++        Iterator<Binding> iterBinding = Iter.map(iter4, node -> BindingFactory.binding(var, node)) ;
++        // Not abortable.
++        return new QueryIterTDB(iterBinding, killList, input, execCxt) ;
++    }
++
++    public static Set<NodeId> convertToNodeIds(Collection<Node> nodes, DatasetGraphTDB dataset)
++    {
++        Set<NodeId> graphIds = new HashSet<>() ;
++        NodeTable nt = dataset.getQuadTable().getNodeTupleTable().getNodeTable() ;
++        for ( Node n : nodes )
++            graphIds.add(nt.getNodeIdForNode(n)) ;
++        return graphIds ;
++    }
++
++    public static Iterator<Tuple<NodeId>> unionGraph(NodeTupleTable ntt)
++    {
++        Iterator<Tuple<NodeId>> iter = ntt.find((NodeId)null, null, null, null) ;
++        iter = Iter.map(iter, quadsToAnyTriples) ;
++        //iterMatches = Iter.distinct(iterMatches) ;
++        
++        // This depends on the way indexes are choose and
++        // the indexing pattern. It assumes that the index 
++        // chosen ends in G so same triples are adjacent 
++        // in a union query.
++        /// See TupleTable.scanAllIndex that ensures this.
++        iter = Iter.distinctAdjacent(iter) ;
++        return iter ;
++    }
++    
++    private static Function<Tuple<NodeId>, Tuple<NodeId>> quadsToAnyTriples = item -> {
++        return TupleFactory.create4(NodeId.NodeIdAny, item.get(1), item.get(2), item.get(3) ) ;
++    } ;
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/StageGeneratorDirectTDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/StageGeneratorDirectTDB.java
index 0000000,0000000..5033f42
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/solver/StageGeneratorDirectTDB.java
@@@ -1,0 -1,0 +1,57 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.solver;
++
++import java.util.function.Predicate;
++
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.graph.Graph ;
++import org.apache.jena.sparql.core.BasicPattern ;
++import org.apache.jena.sparql.engine.ExecutionContext ;
++import org.apache.jena.sparql.engine.QueryIterator ;
++import org.apache.jena.sparql.engine.main.StageGenerator ;
++import org.seaborne.tdb2.store.GraphTDB ;
++import org.seaborne.tdb2.store.NodeId ;
++
++/** Execute TDB requests directly -- no reordering
++ *  Using OpExecutor is preferred.
++ */ 
++public class StageGeneratorDirectTDB implements StageGenerator
++{
++    // Using OpExecutor is preferred.
++    StageGenerator above = null ;
++    
++    public StageGeneratorDirectTDB(StageGenerator original)
++    {
++        above = original ;
++    }
++    
++    @Override
++    public QueryIterator execute(BasicPattern pattern, QueryIterator input, ExecutionContext execCxt)
++    {
++        // --- In case this isn't for TDB
++        Graph g = execCxt.getActiveGraph() ;
++        
++        if ( ! ( g instanceof GraphTDB ) )
++            // Not us - bounce up the StageGenerator chain
++            return above.execute(pattern, input, execCxt) ;
++        GraphTDB graph = (GraphTDB)g ;
++        Predicate<Tuple<NodeId>> filter = QC2.getFilter(execCxt.getContext()) ;
++        return SolverLib.execute(graph, pattern, input, filter, execCxt) ;
++    }
++}


[34/50] [abbrv] jena git commit: JENA-1396: Merge //github/afs/mantis as subdirectory jena-db/

Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/assembler/Vocab.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/assembler/Vocab.java
index 0000000,0000000..ae501bd
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/assembler/Vocab.java
@@@ -1,0 -1,0 +1,36 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.assembler ;
++
++import org.apache.jena.rdf.model.Property ;
++import org.apache.jena.rdf.model.Resource ;
++import org.apache.jena.rdf.model.ResourceFactory ;
++
++public class Vocab {
++    public static Resource type(String namespace, String localName) {
++        return ResourceFactory.createResource(namespace + localName) ;
++    }
++
++    public static Resource resource(String namespace, String localName) {
++        return ResourceFactory.createResource(namespace + localName) ;
++    }
++
++    public static Property property(String namespace, String localName) {
++        return ResourceFactory.createProperty(namespace + localName) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/assembler/VocabTDB2.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/assembler/VocabTDB2.java
index 0000000,0000000..a661368
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/assembler/VocabTDB2.java
@@@ -1,0 -1,0 +1,90 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.assembler;
++
++
++import org.apache.jena.assembler.Assembler ;
++import org.apache.jena.assembler.ConstAssembler ;
++import org.apache.jena.assembler.JA ;
++import org.apache.jena.assembler.assemblers.AssemblerGroup ;
++import org.apache.jena.rdf.model.Property ;
++import org.apache.jena.rdf.model.Resource ;
++import org.apache.jena.sparql.core.assembler.AssemblerUtils ;
++import org.seaborne.tdb2.TDB2 ;
++
++public class VocabTDB2
++{
++    private static final String NS = TDB2.namespace ;
++    
++    public static String getURI() { return NS ; } 
++
++    // Types
++    public static final Resource tDatasetTDB        = Vocab.type(NS, "DatasetTDB2") ;
++    public static final Resource tDatasetTDB_alt    = Vocab.type(NS, "DatasetTDB") ;
++    public static final Resource tGraphTDB          = Vocab.type(NS, "GraphTDB2") ;
++    public static final Resource tGraphTDB_alt      = Vocab.type(NS, "GraphTDB") ;
++//    public static final Resource tTupleIndex        = Vocab.type(NS, "TupleIndex") ;
++    public static final Resource tNodeTable         = Vocab.type(NS, "NodeTable") ;
++
++    public static final Property pLocation          = Vocab.property(NS, "location") ;
++    public static final Property pUnionDefaultGraph = Vocab.property(NS, "unionDefaultGraph") ;
++    
++    public static final Property pIndex             = Vocab.property(NS, "index") ;
++    public static final Property pGraphName1        = Vocab.property(NS, "graphName") ;
++    public static final Property pGraphName2        = Vocab.property(NS, "namedGraph") ;
++    public static final Property pDataset           = Vocab.property(NS, "dataset") ;
++    
++    public static final Property pNodes             = Vocab.property(NS, "nodes") ;
++
++    // Indexes
++    public static final Property pDescription       = Vocab.property(getURI(), "description") ;
++    public static final Property pFile              = Vocab.property(getURI(), "file") ;
++
++    // Nodes
++    public static final Property pNodeIndex         = Vocab.property(getURI(), "nodeIndex") ;
++    public static final Property pNodeData          = Vocab.property(getURI(), "nodeData") ;
++    
++    // Setting
++    public static final Property pSetting           = Vocab.property(getURI(), "setting") ;
++    public static final Property pName              = Vocab.property(getURI(), "name") ;
++    public static final Property pValue             = Vocab.property(getURI(), "value") ;
++    
++    private static boolean initialized = false ; 
++    
++    static { init() ; }
++    
++    static synchronized public void init()
++    {
++        if ( initialized )
++            return ;
++        registerWith(Assembler.general) ;
++        initialized = true ;
++    }
++    
++    static void registerWith(AssemblerGroup g)
++    {
++        // Wire in the extension assemblers (extensions relative to the Jena assembler framework)
++        // Domain and range for properties.
++        // Separated and use ja:imports
++        AssemblerUtils.registerDataset(tDatasetTDB, new DatasetAssemblerTDB());
++        AssemblerUtils.registerDataset(tDatasetTDB_alt, new DatasetAssemblerTDB());
++        AssemblerUtils.register(ConstAssembler.general(), tGraphTDB, new TDBGraphAssembler(), JA.Model);
++        AssemblerUtils.register(ConstAssembler.general(), tGraphTDB_alt, new TDBGraphAssembler(), JA.Model);
++
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/graph/TransactionHandlerTDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/graph/TransactionHandlerTDB.java
index 0000000,0000000..664b005
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/graph/TransactionHandlerTDB.java
@@@ -1,0 -1,0 +1,60 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.graph;
++
++import org.apache.jena.graph.impl.TransactionHandlerBase ;
++import org.apache.jena.query.ReadWrite;
++import org.seaborne.dboe.transaction.txn.TransactionCoordinator;
++import org.seaborne.tdb2.store.DatasetGraphTDB;
++import org.seaborne.tdb2.store.GraphTDB ;
++
++public class TransactionHandlerTDB extends TransactionHandlerBase //implements TransactionHandler 
++{
++    private final GraphTDB graph ;
++    private final DatasetGraphTDB dsg;
++
++    public TransactionHandlerTDB(GraphTDB graph) {
++        this.graph = graph;
++        this.dsg = graph.getDSG();
++    }
++
++    @Override
++    public void abort() {
++        graph.getDSG().abort();
++        graph.getDSG().end();
++    }
++
++    @Override
++    public void begin() {
++        if ( TransactionCoordinator.promotion )
++            dsg.begin(ReadWrite.READ);
++        else
++            dsg.begin(ReadWrite.WRITE);
++    }
++
++    @Override
++    public void commit() {
++        dsg.commit();
++        dsg.end();
++    }
++
++    @Override
++    public boolean transactionsSupported() {
++        return true;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/lib/Async.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/lib/Async.java
index 0000000,0000000..2a0bf52
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/lib/Async.java
@@@ -1,0 -1,0 +1,84 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.lib;
++
++import java.util.concurrent.* ;
++
++import org.seaborne.tdb2.TDBException ;
++
++/** Asyncrhonous operations support
++ *  This class provides a simple framework for asynchronous operations.
++ *  There is a thread pool and a pending queue. 
++ *  <p>
++ *  Default settings are thread pool of one and at most two pending operations.
++ *  When a new async operation is added, the pending queue is checked, and the
++ *  call blocks until the pending queue is below the threashold.
++ *  <p>
++ *  With the default setting of a thread pool of one, operations are
++ *  executed in the order submitted.
++ *  <p>
++ *  If the far end is the bottleneck, a longer queeue is no help.
++ */
++public final class Async {
++    // Currently specific to ThriftRunnable
++    private static final int THREAD_POOL_SIZE = 1 ;
++    private static final int PENDING_MAX = 2 ;
++    private static final int BlockingQueueSize = PENDING_MAX+2 ;
++    
++    private final int pendingQueueLimit ;
++    private final int blockingQueueSize ;
++    private final ExecutorService executorService; 
++    private final BlockingQueue<Future<Void>> outstanding ; 
++    
++    public Async() {
++        this(THREAD_POOL_SIZE, PENDING_MAX) ;
++    }
++
++    public Async(int threadPoolSize, int pendingQueueLimit) {
++        this.pendingQueueLimit = pendingQueueLimit ;
++        this.blockingQueueSize = this.pendingQueueLimit+1 ;
++        this.executorService = Executors.newFixedThreadPool(threadPoolSize) ;
++        this.outstanding = new ArrayBlockingQueue<>(BlockingQueueSize) ;
++    }
++    
++    /** Block until all pending oeprations has been completed */
++    public void completeAsyncOperations() {
++        reduceAsyncQueue(0) ;
++    }
++    
++    /** Block until the pending operations queue is below the given size. */
++    public void reduceAsyncQueue(int reduceSize) {
++        System.out.println("Reduce: "+outstanding.size()) ;
++        while ( outstanding.size() > reduceSize ) {
++            try { outstanding.take().get() ; }
++            catch (Exception ex) {
++                throw new TDBException("Exception taking from async queue", ex) ;
++            } 
++        }
++    }
++    
++    public void execAsync(Object lock, Runnable action) {
++        reduceAsyncQueue(pendingQueueLimit) ;
++        Future<Void> task = executorService.submit(()-> {
++            try { action.run() ; } 
++            catch (Exception ex)    { throw new TDBException("Unexpected exception: "+ex.getMessage(), ex) ; }
++            return null ;
++        }) ;
++        outstanding.add(task) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/lib/NodeLib.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/lib/NodeLib.java
index 0000000,0000000..97951d6
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/lib/NodeLib.java
@@@ -1,0 -1,0 +1,141 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.lib;
++
++import static org.seaborne.tdb2.sys.SystemTDB.LenNodeHash;
++
++import java.security.DigestException;
++import java.security.MessageDigest;
++import java.security.NoSuchAlgorithmException;
++import java.util.Iterator;
++
++import org.apache.jena.atlas.iterator.Iter;
++import org.apache.jena.atlas.lib.Bytes;
++import org.apache.jena.atlas.lib.Pool;
++import org.apache.jena.atlas.lib.PoolBase;
++import org.apache.jena.atlas.lib.PoolSync;
++import org.apache.jena.atlas.logging.Log;
++import org.apache.jena.graph.Node;
++import org.apache.jena.sparql.util.NodeUtils;
++import org.seaborne.dboe.base.record.Record;
++import org.seaborne.tdb2.TDBException;
++import org.seaborne.tdb2.store.Hash;
++import org.seaborne.tdb2.store.NodeId;
++import org.seaborne.tdb2.store.NodeIdFactory;
++import org.seaborne.tdb2.store.nodetable.NodeTable;
++
++public class NodeLib {
++    public static Hash hash(Node n) {
++        Hash h = new Hash(LenNodeHash);
++        setHash(h, n);
++        return h;
++    }
++
++    private static String BNODE   = "bnode";
++    private static String URI     = "uri";
++    private static String LITERAL = "literal";
++
++    public static void setHash(Hash h, Node n) {
++        if ( n.isURI() )
++            hash(h, n.getURI(), null, null, URI);
++        else if ( n.isBlank() )
++            hash(h, n.getBlankNodeLabel(), null, null, BNODE);
++        else if ( n.isLiteral() ) {
++            String dt = n.getLiteralDatatypeURI();
++            if ( NodeUtils.isSimpleString(n) || NodeUtils.isLangString(n) ) {
++                // RDF 1.1 : No datatype for:
++                // xsd:String as simple literals
++                // rdf:langString and @
++                dt = null;
++            }
++            hash(h, n.getLiteralLexicalForm(), n.getLiteralLanguage(), dt, LITERAL);
++        } else
++            throw new TDBException("Attempt to hash something strange: " + n);
++    }
++
++    private static int                 InitialPoolSize = 5;
++    private static Pool<MessageDigest> digesters       = PoolSync.create(new PoolBase<MessageDigest>());
++    static {
++        try {
++            for ( int i = 0 ; i < InitialPoolSize ; i++ )
++                digesters.put(MessageDigest.getInstance("MD5"));
++        }
++        catch (NoSuchAlgorithmException e) {
++            Log.warn(NodeLib.class, "NoSuchAlgorithmException", e);
++            throw new RuntimeException(e);  
++        }
++    }
++
++    private static MessageDigest allocDigest() {
++        try {
++            MessageDigest disgest = digesters.get();
++            if ( disgest == null )
++                disgest = MessageDigest.getInstance("MD5");
++            return disgest;
++        }
++        catch (NoSuchAlgorithmException e) {
++            e.printStackTrace();
++            return null;
++        }
++    }
++
++    private static void deallocDigest(MessageDigest digest) {
++        digest.reset();
++        digesters.put(digest);
++    }
++
++    private static void hash(Hash h, String lex, String lang, String datatype, String nodeName) {
++        if ( datatype == null )
++            datatype = "";
++        if ( lang == null )
++            lang = "";
++        String toHash = lex + "|" + lang + "|" + datatype + "|" + nodeName;
++        MessageDigest digest;
++        try {
++            // MessageDigest.getInstance("MD5");
++            digest = allocDigest();
++            digest.update(Bytes.string2bytes(toHash));
++            if ( h.getLen() == 16 ) {
++                // MD5 is 16 bytes.
++                digest.digest(h.getBytes(), 0, 16);
++            } else {
++                byte b[] = digest.digest(); // 16 bytes.
++                System.arraycopy(b, 0, h.getBytes(), 0, h.getLen());
++            }
++            deallocDigest(digest);
++            return;
++        }
++        catch (DigestException ex) {
++            Log.error(NodeLib.class, "DigestException", ex);
++        }
++    }
++
++    public static NodeId getNodeId(Record r, int idx) {
++        return NodeIdFactory.get(r.getKey(), idx);
++    }
++
++    public static Node termOrAny(Node node) {
++        if ( node == null || node.isVariable() )
++            return Node.ANY;
++        return node;
++    }
++
++    public static Iterator<Node> nodes(final NodeTable nodeTable, Iterator<NodeId> iter) {
++        return Iter.map(iter, nodeTable::getNodeForNodeId);
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/lib/TupleLib.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/lib/TupleLib.java
index 0000000,0000000..7f554f9
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/lib/TupleLib.java
@@@ -1,0 -1,0 +1,154 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.lib;
++
++import java.util.Iterator ;
++
++import org.apache.jena.atlas.iterator.Iter ;
++import org.apache.jena.atlas.lib.InternalErrorException ;
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.atlas.lib.tuple.TupleFactory ;
++import org.apache.jena.atlas.lib.tuple.TupleMap ;
++import org.apache.jena.graph.Node ;
++import org.apache.jena.graph.Triple ;
++import org.apache.jena.sparql.core.Quad ;
++import org.seaborne.dboe.base.record.Record ;
++import org.seaborne.dboe.base.record.RecordFactory ;
++import org.seaborne.tdb2.TDBException ;
++import org.seaborne.tdb2.store.NodeId ;
++import org.seaborne.tdb2.store.NodeIdFactory;
++import org.seaborne.tdb2.store.nodetable.NodeTable ;
++
++public class TupleLib
++{
++    public static Iterator<Tuple<Node>> convertToNodes(final NodeTable nodeTable, Iterator<Tuple<NodeId>> iter) {
++        return Iter.map(iter, item -> tupleNodes(nodeTable, item));
++    }
++
++    public static Iterator<Tuple<NodeId>> convertToNodeId(final NodeTable nodeTable, Iterator<Tuple<Node>> iter) {
++        return Iter.map(iter, item -> tupleNodeIds(nodeTable, item));
++    }
++
++    // Leave - bypasses extract step in Tuple<NodeId> -> Tuple<Node> -> Triple
++    public static Iterator<Triple> convertToTriples(final NodeTable nodeTable, Iterator<Tuple<NodeId>> iter) {
++        return Iter.map(iter, item -> triple(nodeTable, item));
++    }
++
++    public static Iterator<Quad> convertToQuads(final NodeTable nodeTable, Iterator<Tuple<NodeId>> iter) {
++        return Iter.map(iter, item -> quad(nodeTable, item));
++    }
++
++    public static Tuple<Node> tupleNodes(NodeTable nodeTable, Tuple<NodeId> ids) {
++        int N = ids.len();
++        Node[] n = new Node[N];
++        for ( int i = 0 ; i < N ; i++ )
++            n[i] = nodeTable.getNodeForNodeId(ids.get(i));
++        return TupleFactory.create(n);
++    }
++
++    public static Tuple<NodeId> tupleNodeIds(NodeTable nodeTable, Tuple<Node> nodes) {
++        int N = nodes.len();
++        NodeId[] n = new NodeId[N];
++        for ( int i = 0 ; i < N ; i++ )
++            n[i] = nodeTable.getNodeIdForNode(nodes.get(i));
++        return TupleFactory.create(n);
++    }
++    
++    private static Triple triple(NodeTable nodeTable, Tuple<NodeId> tuple) {
++        if ( tuple.len() != 3 )
++            throw new TDBException("Tuple is not of length 3: " + tuple);
++        return triple(nodeTable, tuple.get(0), tuple.get(1), tuple.get(2));
++    }
++
++    private static Triple triple(NodeTable nodeTable, NodeId s, NodeId p, NodeId o) {
++        if ( ! NodeId.isConcrete(s) )
++            throw new InternalErrorException("Invalid id for subject: "+fmt(s,p,o)) ;
++        if ( ! NodeId.isConcrete(p) )
++            throw new InternalErrorException("Invalid id for predicate: "+fmt(s,p,o)) ;
++        if ( ! NodeId.isConcrete(o) )
++            throw new InternalErrorException("Invalid id for object: "+fmt(s,p,o)) ;
++        
++        Node sNode = nodeTable.getNodeForNodeId(s) ;
++        if ( sNode == null )
++            throw new InternalErrorException("Invalid id node for subject (null node): "+fmt(s,p,o)) ;
++
++        Node pNode = nodeTable.getNodeForNodeId(p) ;
++        if ( pNode == null )
++        {
++            nodeTable.getNodeForNodeId(p) ;
++            throw new InternalErrorException("Invalid id node for predicate (null node): "+fmt(s,p,o)) ;
++        }
++        
++        Node oNode = nodeTable.getNodeForNodeId(o) ;
++        if ( oNode == null )
++            throw new InternalErrorException("Invalid id node for object (null node): "+fmt(s,p,o)) ;
++        
++        return new Triple(sNode, pNode, oNode) ;
++    }
++    
++    private static String fmt(NodeId s, NodeId p, NodeId o)
++    {
++        return "("+s+", "+p+", "+o+")" ;
++    }
++    
++    private static Quad quad(NodeTable nodeTable, Tuple<NodeId> tuple) 
++    {
++        if ( tuple.len() != 4 )
++            throw new TDBException("Tuple is not of length 4: "+tuple) ;
++        return quad(nodeTable, tuple.get(0), tuple.get(1), tuple.get(2), tuple.get(3)) ;
++    }
++    
++    private static Quad quad(NodeTable nodeTable, NodeId g, NodeId s, NodeId p, NodeId o) 
++    {
++        Node gNode = nodeTable.getNodeForNodeId(g) ;
++        Node sNode = nodeTable.getNodeForNodeId(s) ;
++        Node pNode = nodeTable.getNodeForNodeId(p) ;
++        Node oNode = nodeTable.getNodeForNodeId(o) ;
++        return new Quad(gNode, sNode, pNode, oNode) ;
++    }
++
++    // ---- Tuples and Records
++    public static Tuple<NodeId> tuple(Record r, TupleMap tMap) {
++        // Unmapping.
++        int N = r.getKey().length/NodeId.SIZE;
++        NodeId[] nodeIds = new NodeId[N] ;
++        for ( int i = 0 ; i < N ; i++ )
++        {
++            int j = i ;
++            if ( tMap != null )
++                j = tMap.unmapIdx(i) ;
++            NodeId id = NodeIdFactory.get(r.getKey(), j*NodeId.SIZE) ;
++            nodeIds[i] = id ;
++        }
++        return TupleFactory.create(nodeIds) ;
++    }
++
++
++    public static Record record(RecordFactory factory, Tuple<NodeId> tuple, TupleMap tMap) {
++        // Mapping.
++        byte[] b = new byte[tuple.len()*NodeId.SIZE] ;
++        for ( int i = 0 ; i < tuple.len() ; i++ )
++        {
++            int j = tMap.getSlotIdx(i) ;
++            // i'th Nodeid goes to j'th bytes slot.
++            NodeIdFactory.set(tuple.get(j), b, i*NodeId.SIZE) ;
++        }
++            
++        return factory.create(b) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/loader/Loader.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/loader/Loader.java
index 0000000,0000000..8555088f
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/loader/Loader.java
@@@ -1,0 -1,0 +1,78 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.loader;
++
++import org.apache.jena.atlas.lib.ProgressMonitor ;
++import org.apache.jena.atlas.logging.FmtLog ;
++import org.apache.jena.query.Dataset ;
++import org.apache.jena.riot.RDFDataMgr ;
++import org.apache.jena.riot.system.ProgressStreamRDF ;
++import org.apache.jena.riot.system.StreamRDF ;
++import org.apache.jena.riot.system.StreamRDFLib ;
++import org.seaborne.dboe.jenax.Txn ;
++import org.seaborne.tdb2.store.DatasetGraphTDB ;
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++public class Loader {
++    
++    private static final int BATCH_SIZE = 100 ;
++    
++    // XXX StreamRDFBatchSplit and parallel index update.
++    private static Logger LOG = LoggerFactory.getLogger("Loader") ;
++    
++    public static void bulkLoad(Dataset ds, String ... files) {
++        DatasetGraphTDB dsg = (DatasetGraphTDB)ds.asDatasetGraph() ;
++        StreamRDF s1 = StreamRDFLib.dataset(dsg) ;
++        ProgressMonitor plog = ProgressMonitor.create(LOG, "Triples", 100000, 10) ;
++        ProgressStreamRDF sMonitor = new ProgressStreamRDF(s1, plog) ;
++        StreamRDF s3 = sMonitor ;
++
++        plog.start(); 
++        Txn.executeWrite(ds, () -> {
++            for ( String fn : files ) {
++                if ( files.length > 1 )
++                    FmtLog.info(LOG, "File: %s",fn);
++                RDFDataMgr.parse(s3, fn) ;
++            }
++        }) ;
++        plog.finish();
++        plog.finishMessage();
++    }
++    
++    public static void bulkLoadBatching(Dataset ds, String ... files) {
++        DatasetGraphTDB dsg = (DatasetGraphTDB)ds.asDatasetGraph() ;
++
++        StreamRDFBatchSplit s1 = new StreamRDFBatchSplit(dsg, 10) ;
++        ProgressMonitor plog = ProgressMonitor.create(LOG, "Triples", 100000, BATCH_SIZE) ;
++        // Want the monitor on the outside to capture transaction wrapper costs.
++        StreamRDF s3 = new ProgressStreamRDF(s1, plog) ;
++
++        plog.start(); 
++        Txn.executeWrite(ds, () -> {
++            for ( String fn : files ) {
++                if ( files.length > 1 )
++                    FmtLog.info(LOG, "File: %s",fn);
++                RDFDataMgr.parse(s3, fn) ;
++            }
++        }) ;
++        plog.finish();  
++        plog.finishMessage();
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/loader/StreamRDFBatchSplit.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/loader/StreamRDFBatchSplit.java
index 0000000,0000000..fb7e25c
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/loader/StreamRDFBatchSplit.java
@@@ -1,0 -1,0 +1,224 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.loader;
++
++import java.util.* ;
++import java.util.stream.Collectors ;
++
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.atlas.lib.tuple.TupleFactory ;
++import org.apache.jena.graph.Node ;
++import org.apache.jena.graph.Triple ;
++import org.apache.jena.riot.other.BatchedStreamRDF ;
++import org.apache.jena.riot.system.StreamRDF ;
++import org.apache.jena.sparql.core.Quad ;
++import org.seaborne.dboe.transaction.txn.Transaction ;
++import org.seaborne.tdb2.TDBException ;
++import org.seaborne.tdb2.setup.TDBDatasetDetails ;
++import org.seaborne.tdb2.store.DatasetGraphTDB ;
++import org.seaborne.tdb2.store.NodeId ;
++import org.seaborne.tdb2.store.NodeIdFactory;
++import org.seaborne.tdb2.store.nodetable.NodeTable ;
++import org.seaborne.tdb2.store.tupletable.TupleTable ;
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++/**
++ * @see BatchedStreamRDF BatchedStreamRDF, which batches by subject
++ */
++public class StreamRDFBatchSplit implements StreamRDF {
++    // Need to handle transactions?
++    // Lizard des (TxnClient) 
++    
++    private static Logger log = LoggerFactory.getLogger(StreamRDFBatchSplit.class) ;
++    protected static NodeId placeholder = NodeIdFactory.genUnique();
++    protected final List<Triple> triples ;
++    protected final List<Tuple<NodeId>> tuples ;
++    protected final Map<Node, NodeId> mapping ;
++    
++    private final int batchSize ;
++    private final TDBDatasetDetails details ;
++    private final DatasetGraphTDB dsg ;
++    private Transaction txn = null ;
++    
++    public StreamRDFBatchSplit(DatasetGraphTDB dsg, int batchSize) {
++        this.dsg = dsg ;
++        this.batchSize = batchSize ;
++        this.triples = new ArrayList<>(batchSize) ;
++        this.tuples = new ArrayList<>(triples.size()) ;
++        this.mapping = new HashMap<>(2*batchSize) ;
++        this.details = new TDBDatasetDetails(dsg) ;
++    }
++        
++    @Override
++    public void start() {
++        log.info("Batch size: "+batchSize);
++        // Multiple starts in one trnasaciton are possible.
++        if ( txn == null )
++            txn = dsg.getTxnSystem().getThreadTransaction() ;
++        if ( txn == null )
++            throw new TDBException("Not in a transaction") ;
++    }
++
++    @Override
++    public void triple(Triple triple) {
++        //Find nodes.
++        //log.info("Triple: "+triple) ;
++        processNode(triple.getSubject()) ;
++        processNode(triple.getPredicate()) ;
++        processNode(triple.getObject()) ;
++        triples.add(triple) ;
++        if ( triples.size() >= batchSize )
++            processBatch() ;
++    }
++
++    int batchNumber = 0 ;
++    
++    protected void processBatch() {
++        //if ( batchNumber < 10 )
++        batchNumber++ ;
++        //FmtLog.info(log, ">>processBatch: [%d]->%d", batchNumber, triples.size()) ;
++
++        Set<Node> requiredNodes = mapping.keySet() ;
++
++        boolean executeBatchNodesPhase = true ;
++        boolean executeIndexPhase = true ;
++        // Derived control.
++        boolean batchUpdateIndexes = true ;
++        
++        if ( executeBatchNodesPhase )
++            // Check this is a cache node table.
++            batchUpdateNodes(requiredNodes, details) ;
++        
++        if ( executeIndexPhase ) {
++            if ( batchUpdateIndexes )
++                batchUpdateIndexes(dsg, details, triples, /*tuples*/null) ;
++            else
++                incrementalUpdateIndexes(triples, dsg) ;
++        }
++        triples.clear();
++        tuples.clear() ;
++        //FmtLog.info(log, "<<processBatch") ;
++        mapping.clear();
++//        if ( batchSize < 10 )
++//            System.exit(0) ;
++    }
++   
++    private static void incrementalUpdateIndexes(List<Triple> triples, DatasetGraphTDB dsg) {
++        for ( Triple triple : triples ) {
++            dsg.getTripleTable().add(triple); 
++        }
++    }
++
++    /** This files the cache so that the tuples adds are faster */ 
++    private static void batchUpdateNodes(Set<Node> required, TDBDatasetDetails details) {
++        List<Node> nodes = new ArrayList<>() ;
++        // Resolve NodeIds
++        
++        // ** Move this into cache - code. 
++        
++        for ( Node n : required ) {
++            // 
++            if ( details.ntCache.getNodeIdForNodeCache(n) == null /* set input - no need :: && ! nodes.contains(n) /* Not good?*/ )
++                nodes.add(n) ;
++        }
++        //log.info("Batch nodes: "+nodes.size()) ;
++        // This drops into the default method.
++        details.ntTop.bulkNodeToNodeId(nodes, true) ;
++        
++        // Check
++        // Resolve NodeIds
++        for ( Node n : required ) {
++            if ( details.ntCache.getNodeIdForNodeCache(n) == null  )
++                log.info("Not in cache: "+n) ;
++        }
++        //details.ntCluster.bulkNodeToNodeId(nodes, true) ;
++        
++        
++        
++    }
++
++    private static void batchUpdateIndexes(DatasetGraphTDB dsg, TDBDatasetDetails details, List<Triple> batchTriples, List<Tuple<NodeId>> workspace) {
++        List<Tuple<NodeId>> tuples = workspace ;
++        if ( tuples == null )
++            tuples = new ArrayList<>(batchTriples.size()) ;
++
++        convert(batchTriples, tuples, details.ntTop) ;
++        //log.info("Batch triples: "+tuples.size()) ;
++
++        TupleTable tupleTable = dsg.getTripleTable().getNodeTupleTable().getTupleTable() ;
++        tupleTable.addAll(tuples);
++    }
++
++    // check for duplicate code
++    private static List<Tuple<NodeId>> convert(List<Triple> triples, NodeTable nodeTable) {
++        return triples.stream().map(t -> TupleFactory.tuple(nodeTable.getAllocateNodeId(t.getSubject()),
++                                                            nodeTable.getAllocateNodeId(t.getPredicate()),
++                                                            nodeTable.getAllocateNodeId(t.getObject())))
++            .collect(Collectors.toList());
++    }
++    
++    private static void convert(List<Triple> triples, List<Tuple<NodeId>> tuples, NodeTable nodeTable) {
++        // Slightly faster.  But larger batches?
++        for ( Triple t : triples ) {
++            NodeId nid_s = nodeTable.getAllocateNodeId(t.getSubject()) ;
++            NodeId nid_p = nodeTable.getAllocateNodeId(t.getPredicate()) ;
++            NodeId nid_o = nodeTable.getAllocateNodeId(t.getObject()) ;
++            Tuple<NodeId> x = TupleFactory.tuple(nid_s, nid_p, nid_o) ;
++            tuples.add(x) ;
++        }
++        
++//        triples.stream().map(t->
++//                  TupleFactory.tuple
++//                  (nodeTable.getAllocateNodeId(t.getSubject()),
++//                   nodeTable.getAllocateNodeId(t.getPredicate()),
++//                   nodeTable.getAllocateNodeId(t.getObject())))
++//                .collect(Collectors.toCollection(()->tuples)) ;
++    }
++    
++    
++    private void processNode(Node node) {
++        
++        if ( mapping.containsKey(node)) 
++            return ;
++        
++        if ( NodeId.hasInlineDatatype(node) ) {
++            NodeId nodeId = NodeId.inline(node) ;
++            if ( nodeId != null )
++                return ;
++        }
++        mapping.put(node, placeholder) ;
++    }
++    
++    @Override
++    public void quad(Quad quad) {}
++
++    @Override
++    public void base(String base) {}
++
++    @Override
++    public void prefix(String prefix, String iri) {}
++
++    @Override
++    public void finish() { 
++        if ( ! triples.isEmpty() )
++            processBatch() ;
++    }
++
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/migrate/A2.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/migrate/A2.java
index 0000000,0000000..4dd1a90
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/migrate/A2.java
@@@ -1,0 -1,0 +1,37 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.migrate;
++
++import org.apache.jena.sparql.algebra.Op ;
++import org.apache.jena.sparql.algebra.Transform ;
++import org.apache.jena.sparql.algebra.Transformer ;
++import org.apache.jena.sparql.core.Quad ;
++
++public class A2
++{
++    /** Convert a pattern, assumed to be quad form, 
++     * so that the default graph is the union of named graphs.  
++     */
++    public static Op unionDefaultGraphQuads(Op op)
++    {
++        // Rewrite so that any explicitly named "default graph" is union graph.
++        Transform t = new TransformGraphRename(Quad.defaultGraphNodeGenerated, Quad.unionGraph)  ;
++        op = Transformer.transform(t, op) ;
++        return op ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/migrate/TransformGraphRename.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/migrate/TransformGraphRename.java
index 0000000,0000000..4759bed
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/migrate/TransformGraphRename.java
@@@ -1,0 -1,0 +1,53 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.migrate;
++
++import org.apache.jena.graph.Node ;
++import org.apache.jena.sparql.algebra.Op ;
++import org.apache.jena.sparql.algebra.TransformCopy ;
++import org.apache.jena.sparql.algebra.op.OpGraph ;
++import org.apache.jena.sparql.algebra.op.OpQuadPattern ;
++
++public class TransformGraphRename extends TransformCopy
++{ 
++    private Node oldGraphName ;
++    private Node newGraphName ;
++
++    public TransformGraphRename(Node oldGraphName, Node newGraphName)
++    {
++        this.oldGraphName = oldGraphName ;
++        this.newGraphName = newGraphName ;
++    }
++
++    // Does not affect variables.
++    @Override
++    public Op transform(OpGraph opGraph, Op x)
++    { 
++        if ( opGraph.getNode().equals(oldGraphName) )
++            opGraph = new OpGraph(newGraphName, x) ;
++        return super.transform(opGraph, x) ;
++    }
++
++    @Override
++    public Op transform(OpQuadPattern opQuadPattern)
++    {
++        if ( opQuadPattern.getGraphNode().equals(oldGraphName) )
++            opQuadPattern = new OpQuadPattern(newGraphName, opQuadPattern.getBasicPattern()) ;
++        return super.transform(opQuadPattern) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/modify/UpdateEngineTDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/modify/UpdateEngineTDB.java
index 0000000,0000000..844f5a9
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/modify/UpdateEngineTDB.java
@@@ -1,0 -1,0 +1,52 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.modify;
++
++import org.apache.jena.sparql.core.DatasetGraph ;
++import org.apache.jena.sparql.engine.binding.Binding ;
++import org.apache.jena.sparql.modify.UpdateEngine ;
++import org.apache.jena.sparql.modify.UpdateEngineFactory ;
++import org.apache.jena.sparql.modify.UpdateEngineMain ;
++import org.apache.jena.sparql.modify.UpdateEngineRegistry ;
++import org.apache.jena.sparql.util.Context ;
++import org.seaborne.tdb2.store.DatasetGraphTxn ;
++
++public class UpdateEngineTDB extends UpdateEngineMain
++{
++    public UpdateEngineTDB(DatasetGraphTxn graphStore, Binding inputBinding, Context context)
++    { super(graphStore, inputBinding, context) ; }
++    
++
++    // ---- Factory
++    public static UpdateEngineFactory getFactory() { 
++        return new UpdateEngineFactory()
++        {
++            @Override
++            public boolean accept(DatasetGraph dataset, Context context) {
++                return (dataset instanceof DatasetGraphTxn) ;
++            }
++            
++            @Override
++            public UpdateEngine create(DatasetGraph dataset, Binding inputBinding, Context context) {
++                return new UpdateEngineTDB((DatasetGraphTxn)dataset, inputBinding, context);
++            }
++        } ;
++    }
++
++    public static void register() { UpdateEngineRegistry.get().add(getFactory()) ; }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/setup/ComponentIdMgr.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/setup/ComponentIdMgr.java
index 0000000,0000000..7464a5a
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/setup/ComponentIdMgr.java
@@@ -1,0 -1,0 +1,101 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.setup;
++
++import java.util.HashMap ;
++import java.util.Map ;
++import java.util.UUID ;
++
++import org.seaborne.dboe.transaction.txn.ComponentId ;
++import org.slf4j.Logger ;
++import org.slf4j.LoggerFactory ;
++
++// Consistent name ->  ComponentId
++
++public class ComponentIdMgr {
++    private static Logger log = LoggerFactory.getLogger(ComponentIdMgr.class) ; 
++    
++    private static Map<String, Integer> names = new HashMap<>() ;  
++    static {
++        // Well know names.
++        setup(1, "SPO") ;
++        setup(2, "POS") ;
++        setup(3, "PSO") ;
++        setup(4, "OSP") ;
++        
++        setup(11, "GSPO") ;
++        setup(12, "GPOS") ;
++        setup(13, "GPSO") ;
++        setup(14, "GOSP") ;
++        
++        setup(21, "POSG") ;
++        setup(22, "PSOG") ;
++        setup(23, "OSPG") ;
++        setup(24, "SPOG") ;
++        
++        setup(30, "GPU") ;
++        
++        setup(40, "prefixes") ;
++        setup(41, "prefixes-data") ;
++        
++        setup(50, "nodes") ;
++        setup(51, "nodes-data") ;
++    }
++    
++    static void setup(int idx, String unitName) {
++        if ( names.containsKey(unitName) )
++            log.error("Name '"+unitName+"' is already registered") ;
++        names.put(unitName, idx) ;
++    }
++    
++    private Map<String, ComponentId> allocated = new HashMap<>() ;
++    
++    // Name to index mapping. 
++    static Map<String, String> mapper = new HashMap<>() ;
++    private final UUID base ;
++    
++    public ComponentIdMgr(UUID base) {
++        this.base = base ;
++    }
++    
++    public static int getIndex(String name) {
++        Integer idx = names.get(name) ;
++        if ( idx == null ) {
++            log.error("Unregistered '"+name+"'") ;
++            return -1 ;
++        }
++        return idx ;
++    }
++    
++    public ComponentId getComponentId(String name) {
++//        // Trace duplicates
++//        final String tracename = "SPO" ;
++//        if ( tracename.equals(name) )
++//            log.info("Name '"+name+"'") ;
++        if ( ! names.containsKey(name))
++            log.error("Name '"+name+"' is not registered") ;
++        if ( allocated.containsKey(name) ) {
++            log.error("ComponentId for '"+name+"' has already been allocated") ;
++            return allocated.get(name) ;
++        }
++        ComponentId cid = ComponentId.alloc(name, base, names.get(name)) ;
++        allocated.put(name, cid) ;
++        return cid ;
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/setup/StoreParams.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/setup/StoreParams.java
index 0000000,0000000..2c59757
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/setup/StoreParams.java
@@@ -1,0 -1,0 +1,441 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.setup;
++
++import java.util.Objects ;
++
++import org.seaborne.dboe.base.block.FileMode ;
++import org.seaborne.dboe.index.IndexParams ;
++import org.seaborne.tdb2.setup.StoreParamsBuilder.Item ;
++
++/** System parameters for a TDB database instance. 
++ * <p>
++ * Some parameters can be changed from run to run
++ * and some parameters can only be changed at the point the database is
++ * created.  
++ * <p>
++ * Getting paramters settings wrong can destroy a databse.   
++ * Alternating the block size is not encouraged and should only be
++ * done if necessary.  It can silently destroy a database if set
++ * to a different value than thatused to create the database.  The
++ * default value of 8Kbytes is good for almost use.
++ * 
++ * @see StoreParamsBuilder  for constructing StoreParams
++ * @see StoreParamsConst    for default values. 
++ */
++public class StoreParams implements IndexParams, StoreParamsDynamic
++{
++    /* These are items you can change JVM to JVM */
++    
++    /*package*/ final Item<FileMode>           fileMode ;
++    /*package*/ final Item<Integer>            blockSize ;
++    /*package*/ final Item<Integer>            blockReadCacheSize ;
++    /*package*/ final Item<Integer>            blockWriteCacheSize ;
++    /*package*/ final Item<Integer>            Node2NodeIdCacheSize ;
++    /*package*/ final Item<Integer>            NodeId2NodeCacheSize ;
++    /*package*/ final Item<Integer>            NodeMissCacheSize ;
++
++    /* These are items affect database layout and
++     * only can be applied when a database is created.
++     * They do not affect existing databases.
++     * If you want to, say, change the index structure,
++     * you'll need to use the index tools.  
++     */
++    
++    /*package*/ final Item<String>             nodeTableBaseName ;
++    
++    /*package*/ final Item<String>             primaryIndexTriples ;
++    /*package*/ final Item<String[]>           tripleIndexes ;
++    
++    /*package*/ final Item<String>             primaryIndexQuads ;
++    /*package*/ final Item<String[]>           quadIndexes ;
++    
++    /*package*/ final Item<String>             prefixTableBaseName ;
++    /*package*/ final Item<String>             primaryIndexPrefix ;
++    /*package*/ final Item<String[]>           prefixIndexes ;
++
++    /** Build StoreParams, starting from system defaults.
++     * 
++     * @return StoreParamsBuilder
++     */
++    public static StoreParamsBuilder builder() { return StoreParamsBuilder.create() ; }
++    
++    /** Build StoreParams, starting from given default values.
++     * 
++     * @return StoreParamsBuilder
++     */
++    public static StoreParamsBuilder builder(StoreParams params) { return StoreParamsBuilder.create(params) ; }
++    
++    /*package*/ StoreParams(Item<FileMode> fileMode, Item<Integer> blockSize,
++                            Item<Integer> blockReadCacheSize, Item<Integer> blockWriteCacheSize,
++                            Item<Integer> node2NodeIdCacheSize, Item<Integer> nodeId2NodeCacheSize,
++                            Item<Integer> nodeMissCacheSize,
++                            
++                            Item<String> nodeTableBaseName, 
++                            Item<String> primaryIndexTriples, Item<String[]> tripleIndexes,
++                            Item<String> primaryIndexQuads, Item<String[]> quadIndexes,
++                            
++                            Item<String> prefixTableBasename,
++                            Item<String> primaryIndexPrefix, Item<String[]> prefixIndexes) {
++        this.fileMode               = fileMode ;
++        this.blockSize              = blockSize ;
++        this.blockReadCacheSize     = blockReadCacheSize ;
++        this.blockWriteCacheSize    = blockWriteCacheSize ;
++        this.Node2NodeIdCacheSize   = node2NodeIdCacheSize ;
++        this.NodeId2NodeCacheSize   = nodeId2NodeCacheSize ;
++        this.NodeMissCacheSize      = nodeMissCacheSize ;
++
++        this.nodeTableBaseName      = nodeTableBaseName ;
++        
++        this.primaryIndexTriples    = primaryIndexTriples ;
++        this.tripleIndexes          = tripleIndexes ;
++        this.primaryIndexQuads      = primaryIndexQuads ;
++        this.quadIndexes            = quadIndexes ;
++        this.primaryIndexPrefix     = primaryIndexPrefix ;
++        this.prefixIndexes          = prefixIndexes ;
++        
++        this.prefixTableBaseName         = prefixTableBasename ;
++    }
++    
++    /** The system default settings. This is the normal set to use.
++     *  It is the set of values used when no StoreParams is provided,
++     *  which is the normal usage.
++     */
++    public static StoreParams getDftStoreParams() {
++        return StoreParamsConst.dftStoreParams ;
++    }
++    
++    /** A {@code StoreParams} that provides a smaller
++     * in-JVM foot print.  This is compatible with
++     * any database but it it is wise to use this consistently,
++     * that is, use when created and when opened later.
++     * It reduces cache sizes and runs the database in "direct"
++     * file mode so as not to use memory mapped files
++     * in addition to the JVM space.
++     */
++    public static StoreParams getSmallStoreParams() {
++        return StoreParamsConst.smallStoreParams ;
++    }
++
++    @Override
++    public FileMode getFileMode() {
++        return fileMode.value ;
++    }
++
++    @Override
++    public boolean isSetFileMode() {
++        return fileMode.isSet ;
++    }
++
++    @Override
++    public Integer getBlockSize() {
++        return blockSize.value ;
++    }
++
++    @Override
++    public Integer getBlockReadCacheSize() {
++        return blockReadCacheSize.value ;
++    }
++
++    @Override
++    public boolean isSetBlockReadCacheSize() {
++        return blockReadCacheSize.isSet ;
++    }
++
++    @Override
++    public Integer getBlockWriteCacheSize() {
++        return blockWriteCacheSize.value ;
++    }
++
++    @Override
++    public boolean isSetBlockWriteCacheSize() {
++        return blockWriteCacheSize.isSet ;
++    }
++
++    @Override
++    public Integer getNode2NodeIdCacheSize() {
++        return Node2NodeIdCacheSize.value ;
++    }
++
++    @Override
++    public boolean isSetNodeId2NodeCacheSize() {
++        return NodeId2NodeCacheSize.isSet ;
++    }
++
++    @Override
++    public boolean isSetNode2NodeIdCacheSize() {
++        return Node2NodeIdCacheSize.isSet ;
++    }
++
++    @Override
++    public Integer getNodeId2NodeCacheSize() {
++        return NodeId2NodeCacheSize.value ;
++    }
++
++    @Override
++    public Integer getNodeMissCacheSize() {
++        return NodeMissCacheSize.value ;
++    }
++
++    @Override
++    public boolean isSetNodeMissCacheSize() {
++        return NodeMissCacheSize.isSet ;
++    }
++
++    public String getNodeTableBaseName() {
++        return nodeTableBaseName.value ;
++    }
++    
++    public boolean isSetNodeTableBaseName() {
++        return nodeTableBaseName.isSet ;
++    }
++
++    public String getPrimaryIndexTriples() {
++        return primaryIndexTriples.value ;
++    }
++
++    public String[] getTripleIndexes() {
++        return tripleIndexes.value ;
++    }
++
++    public String getPrimaryIndexQuads() {
++        return primaryIndexQuads.value ;
++    }
++
++    public String[] getQuadIndexes() {
++        return quadIndexes.value ;
++    }
++
++    public String getPrefixTableBaseName() {
++        return prefixTableBaseName.value ;
++    }
++    
++    public boolean isSetPrefixBaseName() {
++        return prefixTableBaseName.isSet ;
++    }
++
++
++    
++    public String getPrimaryIndexPrefix() {
++        return primaryIndexPrefix.value ;
++    }
++
++    public String[] getPrefixIndexes() {
++        return prefixIndexes.value ;
++    }
++
++    @Override
++    public String toString() {
++        StringBuilder buff = new StringBuilder() ;
++        fmt(buff, "fileMode", getFileMode().toString(), fileMode.isSet) ;
++        fmt(buff, "blockSize", getBlockSize(), blockSize.isSet) ;
++        fmt(buff, "readCacheSize", getBlockReadCacheSize(), blockReadCacheSize.isSet) ;
++        fmt(buff, "writeCacheSize", getBlockWriteCacheSize(), blockWriteCacheSize.isSet) ;
++        fmt(buff, "Node2NodeIdCacheSize", getNode2NodeIdCacheSize(), Node2NodeIdCacheSize.isSet) ;
++        fmt(buff, "NodeId2NodeCacheSize", getNodeId2NodeCacheSize(), NodeId2NodeCacheSize.isSet) ;
++        fmt(buff, "NodeMissCacheSize", getNodeMissCacheSize(), NodeMissCacheSize.isSet) ;
++
++        fmt(buff, "nodeTableBaseName", getNodeTableBaseName(), nodeTableBaseName.isSet) ;
++        fmt(buff, "primaryIndexTriples", getPrimaryIndexTriples(), primaryIndexTriples.isSet) ;
++        fmt(buff, "tripleIndexes", getTripleIndexes(), tripleIndexes.isSet) ;
++        fmt(buff, "primaryIndexQuads", getPrimaryIndexQuads(), primaryIndexQuads.isSet) ;
++        fmt(buff, "quadIndexes", getQuadIndexes(), quadIndexes.isSet) ;
++        
++        fmt(buff, "prefixTableBaseName", getPrefixTableBaseName(), prefixTableBaseName.isSet) ;
++        fmt(buff, "primaryIndexPrefix", getPrimaryIndexPrefix(), primaryIndexPrefix.isSet) ;
++        fmt(buff, "prefixIndexes", getPrefixIndexes(), prefixIndexes.isSet) ;
++
++        return buff.toString() ;
++    }
++    
++    private void fmt(StringBuilder buff, String name, String[] strings, boolean isSet) {
++        String dftStr = "" ;
++        if ( ! isSet )
++            dftStr = "dft:" ;
++        buff.append(String.format("%-20s   %s[%s]\n", name, dftStr, String.join(", ", strings))) ;
++    }
++
++    private void fmt(StringBuilder buff, String name, String value, boolean isSet) {
++        String dftStr = "" ;
++        if ( ! isSet )
++            dftStr = "dft:" ;
++        buff.append(String.format("%-20s   %s%s\n", name, dftStr, value)) ;
++    }
++
++    private void fmt(StringBuilder buff, String name, int value, boolean isSet) {
++        String dftStr = "" ;
++        if ( ! isSet )
++            dftStr = "dft:" ;
++        buff.append(String.format("%-20s   %s%s\n", name, dftStr, value)) ;
++    }
++
++    /** Equality but ignore "isSet" */
++    public static boolean sameValues(StoreParams params1, StoreParams params2) {
++        if ( params1 == null && params2 == null )
++            return true ;
++        if ( params1 == null )
++            return false ;
++        if ( params2 == null )
++            return false ;
++        if ( !sameValues(params1.fileMode, params2.fileMode) )
++            return false ;
++        if ( !sameValues(params1.blockSize, params2.blockSize) )
++            return false ;
++        if ( !sameValues(params1.blockReadCacheSize, params2.blockReadCacheSize) )
++            return false ;
++        if ( !sameValues(params1.blockWriteCacheSize, params2.blockWriteCacheSize) )
++            return false ;
++        if ( !sameValues(params1.Node2NodeIdCacheSize, params2.Node2NodeIdCacheSize) )
++            return false ;
++        if ( !sameValues(params1.NodeId2NodeCacheSize, params2.NodeId2NodeCacheSize) )
++            return false ;
++        if ( !sameValues(params1.NodeMissCacheSize, params2.NodeMissCacheSize) )
++            return false ;
++        if ( !sameValues(params1.nodeTableBaseName, params2.nodeTableBaseName) )
++            return false ;
++        if ( !sameValues(params1.primaryIndexTriples, params2.primaryIndexTriples) )
++            return false ;
++        if ( !sameValues(params1.tripleIndexes, params2.tripleIndexes) )
++            return false ;
++        if ( !sameValues(params1.primaryIndexQuads, params2.primaryIndexQuads) )
++            return false ;
++        if ( !sameValues(params1.quadIndexes, params2.quadIndexes) )
++            return false ;
++        if ( !sameValues(params1.prefixTableBaseName, params2.prefixTableBaseName) )
++            return false ;
++        if ( !sameValues(params1.primaryIndexPrefix, params2.primaryIndexPrefix) )
++            return false ;
++        if ( !sameValues(params1.prefixIndexes, params2.prefixIndexes) )
++            return false ;
++        return true ;
++    }
++    
++    private static <X> boolean sameValues(Item<X> item1, Item<X> item2) {
++        return Objects.deepEquals(item1.value, item2.value) ; 
++    }
++
++    @Override
++    public int hashCode() {
++        final int prime = 31 ;
++        int result = 1 ;
++        result = prime * result + ((Node2NodeIdCacheSize == null) ? 0 : Node2NodeIdCacheSize.hashCode()) ;
++        result = prime * result + ((NodeId2NodeCacheSize == null) ? 0 : NodeId2NodeCacheSize.hashCode()) ;
++        result = prime * result + ((NodeMissCacheSize == null) ? 0 : NodeMissCacheSize.hashCode()) ;
++        result = prime * result + ((blockReadCacheSize == null) ? 0 : blockReadCacheSize.hashCode()) ;
++        result = prime * result + ((blockSize == null) ? 0 : blockSize.hashCode()) ;
++        result = prime * result + ((blockWriteCacheSize == null) ? 0 : blockWriteCacheSize.hashCode()) ;
++        result = prime * result + ((fileMode == null) ? 0 : fileMode.hashCode()) ;
++        result = prime * result + ((nodeTableBaseName == null) ? 0 : nodeTableBaseName.hashCode()) ;
++        result = prime * result + ((prefixTableBaseName == null) ? 0 : prefixTableBaseName.hashCode()) ;
++        result = prime * result + ((prefixIndexes == null) ? 0 : prefixIndexes.hashCode()) ;
++        result = prime * result + ((primaryIndexPrefix == null) ? 0 : primaryIndexPrefix.hashCode()) ;
++        result = prime * result + ((primaryIndexQuads == null) ? 0 : primaryIndexQuads.hashCode()) ;
++        result = prime * result + ((primaryIndexTriples == null) ? 0 : primaryIndexTriples.hashCode()) ;
++        result = prime * result + ((quadIndexes == null) ? 0 : quadIndexes.hashCode()) ;
++        result = prime * result + ((tripleIndexes == null) ? 0 : tripleIndexes.hashCode()) ;
++        return result ;
++    }
++
++    @Override
++    public boolean equals(Object obj) {
++        if ( this == obj )
++            return true ;
++        if ( obj == null )
++            return false ;
++        if ( getClass() != obj.getClass() )
++            return false ;
++        StoreParams other = (StoreParams)obj ;
++        if ( Node2NodeIdCacheSize == null ) {
++            if ( other.Node2NodeIdCacheSize != null )
++                return false ;
++        } else if ( !Node2NodeIdCacheSize.equals(other.Node2NodeIdCacheSize) )
++            return false ;
++        if ( NodeId2NodeCacheSize == null ) {
++            if ( other.NodeId2NodeCacheSize != null )
++                return false ;
++        } else if ( !NodeId2NodeCacheSize.equals(other.NodeId2NodeCacheSize) )
++            return false ;
++        if ( NodeMissCacheSize == null ) {
++            if ( other.NodeMissCacheSize != null )
++                return false ;
++        } else if ( !NodeMissCacheSize.equals(other.NodeMissCacheSize) )
++            return false ;
++        if ( blockReadCacheSize == null ) {
++            if ( other.blockReadCacheSize != null )
++                return false ;
++        } else if ( !blockReadCacheSize.equals(other.blockReadCacheSize) )
++            return false ;
++        if ( blockSize == null ) {
++            if ( other.blockSize != null )
++                return false ;
++        } else if ( !blockSize.equals(other.blockSize) )
++            return false ;
++        if ( blockWriteCacheSize == null ) {
++            if ( other.blockWriteCacheSize != null )
++                return false ;
++        } else if ( !blockWriteCacheSize.equals(other.blockWriteCacheSize) )
++            return false ;
++        if ( fileMode == null ) {
++            if ( other.fileMode != null )
++                return false ;
++        } else if ( !fileMode.equals(other.fileMode) )
++            return false ;
++        if ( nodeTableBaseName == null ) {
++            if ( other.nodeTableBaseName != null )
++                return false ;
++        } else if ( !nodeTableBaseName.equals(other.nodeTableBaseName) )
++            return false ;
++        if ( prefixTableBaseName == null ) {
++            if ( other.prefixTableBaseName != null )
++                return false ;
++        } else if ( !prefixTableBaseName.equals(other.prefixTableBaseName) )
++            return false ;
++        if ( prefixIndexes == null ) {
++            if ( other.prefixIndexes != null )
++                return false ;
++        } else if ( !prefixIndexes.equals(other.prefixIndexes) )
++            return false ;
++        if ( primaryIndexPrefix == null ) {
++            if ( other.primaryIndexPrefix != null )
++                return false ;
++        } else if ( !primaryIndexPrefix.equals(other.primaryIndexPrefix) )
++            return false ;
++        if ( primaryIndexQuads == null ) {
++            if ( other.primaryIndexQuads != null )
++                return false ;
++        } else if ( !primaryIndexQuads.equals(other.primaryIndexQuads) )
++            return false ;
++        if ( primaryIndexTriples == null ) {
++            if ( other.primaryIndexTriples != null )
++                return false ;
++        } else if ( !primaryIndexTriples.equals(other.primaryIndexTriples) )
++            return false ;
++        if ( quadIndexes == null ) {
++            if ( other.quadIndexes != null )
++                return false ;
++        } else if ( !quadIndexes.equals(other.quadIndexes) )
++            return false ;
++        if ( tripleIndexes == null ) {
++            if ( other.tripleIndexes != null )
++                return false ;
++        } else if ( !tripleIndexes.equals(other.tripleIndexes) )
++            return false ;
++        return true ;
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/setup/StoreParamsBuilder.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/setup/StoreParamsBuilder.java
index 0000000,0000000..3fb2f36
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/setup/StoreParamsBuilder.java
@@@ -1,0 -1,0 +1,308 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.setup;
++
++import org.seaborne.dboe.base.block.FileMode ;
++
++public class StoreParamsBuilder {
++    // Immutable.
++    static class Item<X> {
++        final X value  ;
++        final boolean isSet ;
++        
++        Item(X value, boolean isSet) {
++            this.value = value ;
++            this.isSet = isSet ;
++        }
++        @Override
++        public int hashCode() {
++            final int prime = 31 ;
++            int result = 1 ;
++            result = prime * result + (isSet ? 1231 : 1237) ;
++            result = prime * result + ((value == null) ? 0 : value.hashCode()) ;
++            return result ;
++        }
++        @Override
++        public boolean equals(Object obj) {
++            if ( this == obj )
++                return true ;
++            if ( obj == null )
++                return false ;
++            if ( getClass() != obj.getClass() )
++                return false ;
++            Item<?> other = (Item<?>)obj ;
++            if ( isSet != other.isSet )
++                return false ;
++            if ( value == null ) {
++                if ( other.value != null )
++                    return false ;
++            } else if ( !value.equals(other.value) )
++                return false ;
++            return true ;
++        }
++    }
++    
++    // See also StoreParamsConst.
++    /** Database and query configuration */ 
++    // Key names are the base name -  encode/decode may add a prefix.
++    
++    private Item<FileMode>           fileMode              = new Item<>(StoreParamsConst.fileMode, false) ;
++
++    private Item<Integer>            blockReadCacheSize    = new Item<>(StoreParamsConst.blockReadCacheSize, false) ;
++
++    private Item<Integer>            blockWriteCacheSize   = new Item<>(StoreParamsConst.blockWriteCacheSize, false) ;
++
++    private Item<Integer>            Node2NodeIdCacheSize  = new Item<>(StoreParamsConst.Node2NodeIdCacheSize, false) ;
++
++    private Item<Integer>            NodeId2NodeCacheSize  = new Item<>(StoreParamsConst.NodeId2NodeCacheSize, false) ;
++
++    private Item<Integer>            NodeMissCacheSize     = new Item<>(StoreParamsConst.NodeMissCacheSize, false) ;
++
++    /** Database layout - ignored after a database is created */
++
++    private Item<Integer>            blockSize             = new Item<>(StoreParamsConst.blockSize, false) ;
++
++    private Item<String>             nodeTableBaseName     = new Item<>(StoreParamsConst.nodeTableBaseName, false) ;
++    
++    private Item<String>             primaryIndexTriples   = new Item<>(StoreParamsConst.primaryIndexTriples, false) ;
++
++    private Item<String[]>           tripleIndexes         = new Item<>(StoreParamsConst.tripleIndexes, false) ;
++
++    private Item<String>             primaryIndexQuads     = new Item<>(StoreParamsConst.primaryIndexQuads, false) ;
++
++    private Item<String[]>           quadIndexes           = new Item<>(StoreParamsConst.quadIndexes, false) ;
++
++    private Item<String>             prefixTableBaseName   = new Item<>(StoreParamsConst.prefixTableBaseName, false) ;
++    
++    private Item<String>             primaryIndexPrefix    = new Item<>(StoreParamsConst.primaryIndexPrefix, false) ;
++
++    private Item<String[]>           prefixIndexes         = new Item<>(StoreParamsConst.prefixIndexes, false) ;
++    
++    public static StoreParamsBuilder create() {
++        return new StoreParamsBuilder() ;
++    }
++
++    public static StoreParamsBuilder create(StoreParams params) {
++        return new StoreParamsBuilder(params) ;
++    }
++
++    /** Using a base set of {@link StoreParams}, and update with dynamic parameters.
++     * 
++     * @param baseParams
++     * @param additionalParams
++     * @return StoreParams
++     */
++    
++    public static StoreParams modify(StoreParams baseParams, StoreParamsDynamic additionalParams) {
++        StoreParamsBuilder b = new StoreParamsBuilder(baseParams) ;
++        // Merge explicitly set params 
++        if ( additionalParams.isSetFileMode() )
++            b.fileMode(additionalParams.getFileMode()) ;
++        
++        if ( additionalParams.isSetBlockReadCacheSize() )
++            b.blockReadCacheSize(additionalParams.getBlockReadCacheSize()) ;
++
++        if ( additionalParams.isSetBlockWriteCacheSize() )
++            b.blockWriteCacheSize(additionalParams.getBlockWriteCacheSize()) ;
++        
++        if ( additionalParams.isSetNode2NodeIdCacheSize() )            
++            b.node2NodeIdCacheSize(additionalParams.getNode2NodeIdCacheSize()) ;
++        
++        if ( additionalParams.isSetNodeId2NodeCacheSize() )            
++            b.nodeId2NodeCacheSize(additionalParams.getNodeId2NodeCacheSize()) ;
++        
++        if ( additionalParams.isSetNodeMissCacheSize() )
++            b.nodeMissCacheSize(additionalParams.getNodeMissCacheSize()) ;
++
++        return b.build();
++    }
++    
++
++    private StoreParamsBuilder() {}
++    
++    /** Initial with a StoreParams as default values */
++    private StoreParamsBuilder(StoreParams other) {
++        this.fileMode               = other.fileMode ;
++        this.blockSize              = other.blockSize ;
++        this.blockReadCacheSize     = other.blockReadCacheSize ; 
++        this.blockWriteCacheSize    = other.blockWriteCacheSize ; 
++        this.Node2NodeIdCacheSize   = other.Node2NodeIdCacheSize ; 
++        this.NodeId2NodeCacheSize   = other.NodeId2NodeCacheSize ; 
++        this.NodeMissCacheSize      = other.NodeMissCacheSize ; 
++
++        this.nodeTableBaseName      = other.nodeTableBaseName ; 
++        
++        this.primaryIndexTriples    = other.primaryIndexTriples ; 
++        this.tripleIndexes          = other.tripleIndexes ; 
++        
++        this.primaryIndexQuads      = other.primaryIndexQuads ; 
++        this.quadIndexes            = other.quadIndexes ; 
++
++        this.prefixTableBaseName    = other.prefixTableBaseName ; 
++        this.primaryIndexPrefix     = other.primaryIndexPrefix ; 
++        this.prefixIndexes          = other.prefixIndexes ; 
++    }
++    
++    public StoreParams build() {
++        return new StoreParams(
++                 fileMode, blockSize, blockReadCacheSize, blockWriteCacheSize, 
++                 Node2NodeIdCacheSize, NodeId2NodeCacheSize, NodeMissCacheSize,
++                 nodeTableBaseName,
++                 primaryIndexTriples, tripleIndexes,
++                 primaryIndexQuads, quadIndexes, 
++                 prefixTableBaseName, primaryIndexPrefix,
++                 prefixIndexes) ;
++    }
++    
++    public FileMode getFileMode() {
++        return fileMode.value ;
++    }
++    
++    public StoreParamsBuilder fileMode(FileMode fileMode) {
++        this.fileMode = new Item<>(fileMode, true) ;
++        return this ;
++    }
++    
++    public int getBlockSize() {
++        return blockSize.value ;
++    }
++
++    public StoreParamsBuilder blockSize(int blockSize) {
++        this.blockSize = new Item<>(blockSize, true) ;
++        return this ;
++    }
++
++    public int getBlockReadCacheSize() {
++        return blockReadCacheSize.value ;
++    }
++
++    public StoreParamsBuilder blockReadCacheSize(int blockReadCacheSize) {
++        this.blockReadCacheSize = new Item<>(blockReadCacheSize, true) ;
++        return this ;
++    }
++
++    public int getBlockWriteCacheSize() {
++        return blockWriteCacheSize.value ;
++    }
++
++   public StoreParamsBuilder blockWriteCacheSize(int blockWriteCacheSize) {
++       this.blockWriteCacheSize = new Item<>(blockWriteCacheSize, true) ;
++       return this ;
++   }
++
++    public int getNode2NodeIdCacheSize() {
++        return Node2NodeIdCacheSize.value ;
++    }
++
++   public StoreParamsBuilder node2NodeIdCacheSize(int node2NodeIdCacheSize) {
++       Node2NodeIdCacheSize = new Item<>(node2NodeIdCacheSize, true) ;
++       return this ;
++   }
++
++    public int getNodeId2NodeCacheSize() {
++        return NodeId2NodeCacheSize.value ;
++    }
++
++   public StoreParamsBuilder nodeId2NodeCacheSize(int nodeId2NodeCacheSize) {
++       NodeId2NodeCacheSize = new Item<>(nodeId2NodeCacheSize, true) ;
++       return this ;
++   }
++
++    public int getNodeMissCacheSize() {
++        return NodeMissCacheSize.value ;
++    }
++
++   public StoreParamsBuilder nodeMissCacheSize(int nodeMissCacheSize) {
++       NodeMissCacheSize = new Item<>(nodeMissCacheSize, true) ;
++       return this ;
++   }
++
++   public String getNodeTableBaseName() {
++       return nodeTableBaseName.value ;
++   }
++   
++   public StoreParamsBuilder nodeTableBaseName(String nodeTableBaseName) {
++       this.nodeTableBaseName = new Item<>(nodeTableBaseName, true);
++       return this ;
++   }
++   
++   public String getPrimaryIndexTriples() {
++       return primaryIndexTriples.value ;
++   }
++
++   public StoreParamsBuilder primaryIndexTriples(String primaryIndexTriples) {
++       this.primaryIndexTriples = new Item<>(primaryIndexTriples, true) ;
++       return this ;
++   }
++
++   public String[] getTripleIndexes() {
++       return tripleIndexes.value ;
++   }
++
++   public StoreParamsBuilder tripleIndexes(String[] tripleIndexes) {
++       this.tripleIndexes = new Item<>(tripleIndexes, true) ;
++       return this ;
++   }
++
++   public String getPrimaryIndexQuads() {
++       return primaryIndexQuads.value ;
++   }
++
++   public StoreParamsBuilder primaryIndexQuads(String primaryIndexQuads) {
++       this.primaryIndexQuads = new Item<>(primaryIndexQuads, true) ;
++       return this ;
++   }
++
++    public String[] getQuadIndexes() {
++        return quadIndexes.value ;
++    }
++
++   public StoreParamsBuilder quadIndexes(String[] quadIndexes) {
++       this.quadIndexes = new Item<>(quadIndexes, true) ;
++       return this ;
++   }
++
++
++   public String getPreifixTableBaseName() {
++       return prefixTableBaseName.value ;
++   }
++   
++   public StoreParamsBuilder prefixTableBaseName(String prefixTableBaseName) {
++       this.prefixTableBaseName = new Item<>(prefixTableBaseName, true) ;
++       return this ;
++   }
++   
++   public String getPrimaryIndexPrefix() {
++        return primaryIndexPrefix.value ;
++    }
++
++   public StoreParamsBuilder primaryIndexPrefix(String primaryIndexPrefix) {
++       this.primaryIndexPrefix = new Item<>(primaryIndexPrefix, true) ;
++       return this ;
++   }
++
++    public String[] getPrefixIndexes() {
++        return prefixIndexes.value ;
++    }
++
++   public StoreParamsBuilder prefixIndexes(String[] prefixIndexes) {
++       this.prefixIndexes = new Item<>(prefixIndexes, true) ;
++       return this ;
++   }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/main/java/org/seaborne/tdb2/setup/StoreParamsCodec.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/main/java/org/seaborne/tdb2/setup/StoreParamsCodec.java
index 0000000,0000000..87653f9
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/main/java/org/seaborne/tdb2/setup/StoreParamsCodec.java
@@@ -1,0 -1,0 +1,189 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.setup;
++
++import static org.seaborne.tdb2.setup.StoreParamsConst.* ;
++
++import java.io.BufferedOutputStream ;
++import java.io.FileOutputStream ;
++import java.io.IOException ;
++import java.io.OutputStream ;
++
++import org.apache.jena.atlas.AtlasException ;
++import org.apache.jena.atlas.io.IO ;
++import org.apache.jena.atlas.json.* ;
++import org.apache.jena.atlas.lib.Lib ;
++import org.seaborne.dboe.base.block.FileMode ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.dboe.sys.Names;
++import org.seaborne.tdb2.TDBException ;
++
++/** Encode and decode {@link StoreParams} */ 
++public class StoreParamsCodec {
++    
++    /** Write to a file */ 
++    public static void write(Location location, StoreParams params) {
++        write(location.getPath(Names.TDB_CONFIG_FILE) ,params) ;
++    }
++    
++    /** Write to a file */ 
++    public static void write(String filename, StoreParams params) {
++        try (OutputStream out = new FileOutputStream(filename); 
++             OutputStream out2 = new BufferedOutputStream(out); ) {
++            JsonObject object = encodeToJson(params) ;
++            JSON.write(out2, object) ;
++            out2.write('\n') ;
++        }
++        catch (IOException ex) { IO.exception(ex); }
++    }
++
++    /** Read from a file */ 
++    public static StoreParams read(Location location) {
++        return read(location.getPath(Names.TDB_CONFIG_FILE)) ;
++    }
++    
++    /** Read from a file, if possible. */ 
++    public static StoreParams read(String filename) {
++        try {
++            JsonObject obj = JSON.read(filename) ;
++            return StoreParamsCodec.decode(obj) ;
++        } 
++        catch (JsonParseException ex) { return null ; }
++        catch (AtlasException ex) { return null ; }
++    }
++    
++    public static JsonObject encodeToJson(StoreParams params) {
++        JsonBuilder builder = new JsonBuilder() ;
++        builder.startObject("StoreParams") ;    // "StoreParams" is an internal alignment marker - not in the JSON.
++        
++        encode(builder, key(fFileMode),                 params.getFileMode().name()) ;
++        encode(builder, key(fBlockSize),                params.getBlockSize()) ;
++        encode(builder, key(fBlockReadCacheSize),       params.getBlockReadCacheSize()) ;
++        encode(builder, key(fBlockWriteCacheSize),      params.getBlockWriteCacheSize()) ;
++        encode(builder, key(fNode2NodeIdCacheSize),     params.getNode2NodeIdCacheSize()) ;
++        encode(builder, key(fNodeId2NodeCacheSize),     params.getNodeId2NodeCacheSize()) ;
++        encode(builder, key(fNodeMissCacheSize),        params.getNodeMissCacheSize()) ;
++        encode(builder, key(fNodeTableBaseName),        params.getNodeTableBaseName()) ;
++        encode(builder, key(fPrimaryIndexTriples),      params.getPrimaryIndexTriples()) ;
++        encode(builder, key(fTripleIndexes),            params.getTripleIndexes()) ;
++        encode(builder, key(fPrimaryIndexQuads),        params.getPrimaryIndexQuads()) ;
++        encode(builder, key(fQuadIndexes),              params.getQuadIndexes()) ;
++        encode(builder, key(fPrefixTableBaseName),      params.getPrefixTableBaseName()) ;
++        encode(builder, key(fPrimaryIndexPrefix),       params.getPrimaryIndexPrefix()) ;
++        encode(builder, key(fPrefixIndexes),            params.getPrefixIndexes()) ;
++        
++        builder.finishObject("StoreParams") ;
++        return (JsonObject)builder.build() ;
++    }
++
++    private static final String jsonKeyPrefix= "tdb." ;
++    
++    private static String key(String string) {
++        if ( string.startsWith(jsonKeyPrefix))
++            throw new TDBException("Key name already starts with '"+jsonKeyPrefix+"'") ;
++        return jsonKeyPrefix+string ;
++    }
++
++    private static String unkey(String string) {
++        if ( ! string.startsWith(jsonKeyPrefix) )
++            throw new TDBException("JSON key name does not start with '"+jsonKeyPrefix+"'") ;
++        return string.substring(jsonKeyPrefix.length()) ;
++    }
++
++    public static StoreParams decode(JsonObject json) {
++        StoreParamsBuilder builder = StoreParams.builder() ;
++        
++        for ( String key : json.keys() ) {
++            String short_key = unkey(key) ;
++            switch(short_key) {
++                case fFileMode :               builder.fileMode(FileMode.valueOf(getString(json, key))) ;   break ;
++                case fBlockSize:               builder.blockSize(getInt(json, key)) ;                       break ;
++                case fBlockReadCacheSize:      builder.blockReadCacheSize(getInt(json, key)) ;              break ;
++                case fBlockWriteCacheSize:     builder.blockWriteCacheSize(getInt(json, key)) ;             break ;
++                case fNode2NodeIdCacheSize:    builder.node2NodeIdCacheSize(getInt(json, key)) ;            break ;
++                case fNodeId2NodeCacheSize:    builder.nodeId2NodeCacheSize(getInt(json, key)) ;            break ;
++                case fNodeMissCacheSize:       builder.nodeMissCacheSize(getInt(json, key)) ;               break ;
++                
++                case fNodeTableBaseName:       builder.nodeTableBaseName(getString(json, key)) ;            break ;
++                case fPrimaryIndexTriples:     builder.primaryIndexTriples(getString(json, key)) ;          break ;
++                case fTripleIndexes:           builder.tripleIndexes(getStringArray(json, key)) ;           break ;
++                case fPrimaryIndexQuads:       builder.primaryIndexQuads(getString(json, key)) ;            break ;
++                case fQuadIndexes:             builder.quadIndexes(getStringArray(json, key)) ;             break ;
++                
++                case fPrefixTableBaseName:     builder.prefixTableBaseName(getString(json, key)) ;          break ;
++                case fPrimaryIndexPrefix:      builder.primaryIndexPrefix(getString(json, key)) ;           break ;
++                case fPrefixIndexes:           builder.prefixIndexes(getStringArray(json, key)) ;           break ;
++                
++                default:
++                    throw new TDBException("StoreParams key no recognized: "+key) ;
++            }
++        }
++        return builder.build() ;
++    }
++
++    // "Get or error" operations.
++    
++    private static String getString(JsonObject json, String key) {
++        if ( ! json.hasKey(key) )
++            throw new TDBException("StoreParamsCodec.getString: no such key: "+key) ;
++        String x = json.get(key).getAsString().value() ;
++        return x ;
++    }
++
++    private static Integer getInt(JsonObject json, String key) {
++        if ( ! json.hasKey(key) )
++            throw new TDBException("StoreParamsCodec.getInt: no such key: "+key) ;
++        Integer x = json.get(key).getAsNumber().value().intValue() ;
++        return x ;
++    }
++    
++    private static String[] getStringArray(JsonObject json, String key) {
++        if ( ! json.hasKey(key) )
++            throw new TDBException("StoreParamsCodec.getStringArray: no such key: "+key) ;
++        JsonArray a = json.get(key).getAsArray() ;
++        String[] x = new String[a.size()] ;
++        for ( int i = 0 ; i < a.size() ; i++ ) {
++            x[i] = a.get(i).getAsString().value() ;
++        }
++        return x ;
++    }
++
++    // Encode helper.
++    private static void encode(JsonBuilder builder, String name, Object value) {
++        if ( value instanceof Number ) {
++            long x = ((Number)value).longValue() ;
++            builder.key(name).value(x) ;
++            return ;
++        }
++        if ( value instanceof String ) {
++            builder.key(name).value(value.toString()) ;
++            return ;
++        }
++        if ( value instanceof String[] ) {
++            String[] x = (String[])value ;
++            builder.key(name) ;
++            builder.startArray() ;
++            for ( String s : x ) {
++                builder.value(s) ;
++            }
++            builder.finishArray() ;
++            return ;
++        }
++        throw new TDBException("Class of value not recognized: "+Lib.classShortName(value.getClass())) ;
++    }
++}


[21/50] [abbrv] jena git commit: Remove ObjectFile (unused).

Posted by an...@apache.org.
Remove ObjectFile (unused).

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

Branch: refs/heads/jena-tdb2
Commit: 67b69b5e2258da02451be2064e5980647908d709
Parents: 9b987b0
Author: Andy Seaborne <an...@seaborne.org>
Authored: Wed Sep 13 14:48:24 2017 +0100
Committer: Andy Seaborne <an...@seaborne.org>
Committed: Wed Sep 13 14:48:24 2017 +0100

----------------------------------------------------------------------
 .../seaborne/dboe/base/file/BinaryDataFile.java |  14 +-
 .../seaborne/dboe/base/file/FileFactory.java    |  25 -
 .../dboe/base/objectfile/ObjectFile.java        |  95 ----
 .../dboe/base/objectfile/ObjectFileBuilder.java |  24 -
 .../dboe/base/objectfile/ObjectFileLogger.java  | 143 ------
 .../dboe/base/objectfile/ObjectFileStorage.java | 476 -------------------
 .../base/objectfile/ObjectFileSwitcher.java     |  45 --
 .../dboe/base/objectfile/ObjectFileWrapper.java |  78 ---
 .../dboe/base/objectfile/StringFile.java        | 145 ------
 .../test/java/org/seaborne/dboe/TC_DBOE_IO.java |   4 +-
 .../java/org/seaborne/dboe/base/TC_Base.java    |  41 --
 .../base/objectfile/AbstractTestObjectFile.java | 140 ------
 .../base/objectfile/AbstractTestStringFile.java | 145 ------
 .../dboe/base/objectfile/TS_ObjectFile.java     |  33 --
 .../objectfile/TestObjectFileBuffering.java     | 101 ----
 .../base/objectfile/TestObjectFileDisk.java     |  49 --
 .../dboe/base/objectfile/TestObjectFileMem.java |  39 --
 .../base/objectfile/TestStringFileDisk.java     |  48 --
 .../dboe/base/objectfile/TestStringFileMem.java |  40 --
 .../dboe/trans/data/TransBinaryDataFile.java    |   3 +-
 .../dboe/trans/data/TransObjectFile.java        | 276 -----------
 .../seaborne/dboe/trans/data/TS_TransData.java  |   3 +-
 .../dboe/trans/data/TestTransObjectFile.java    | 133 ------
 23 files changed, 8 insertions(+), 2092 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/67b69b5e/dboe-base/src/main/java/org/seaborne/dboe/base/file/BinaryDataFile.java
----------------------------------------------------------------------
diff --git a/dboe-base/src/main/java/org/seaborne/dboe/base/file/BinaryDataFile.java b/dboe-base/src/main/java/org/seaborne/dboe/base/file/BinaryDataFile.java
index f840fb9..3d9772c 100644
--- a/dboe-base/src/main/java/org/seaborne/dboe/base/file/BinaryDataFile.java
+++ b/dboe-base/src/main/java/org/seaborne/dboe/base/file/BinaryDataFile.java
@@ -21,16 +21,11 @@ import java.io.RandomAccessFile ;
 
 import org.apache.jena.atlas.lib.Closeable ;
 import org.apache.jena.atlas.lib.Sync ;
-import org.seaborne.dboe.base.objectfile.ObjectFile ;
 
-/** An append-only, read anywhere, binary file.
- *  
+/** An append-only, read-anywhere, binary file.
+ * A {@code BinaryDataFile} does not record the length and assumes the
+ * entries are self-defining.
  * 
- * An {@link ObjectFile} is a series of length+binary records.
- * A {@link BinaryDataFile} does not record the length and assumes the
- * entires are self-defining.
- * 
- *  @see ObjectFile
  *  @see RandomAccessFile
  */
 public interface BinaryDataFile extends Closeable, Sync {
@@ -39,11 +34,10 @@ public interface BinaryDataFile extends Closeable, Sync {
     //  
     // The choice seems to come down to ByteBuffers vs byte[]
     // which in turn is a small/large data (scattered data)
-    // issue.  We are currently expecting small I/O so byte[]
+    // issue.  We are currently expecting small(er) I/O so byte[]
     // and being like Thrift is better.
 
     // byte[] vs ByteBuffer
-    // Merge with BufferChannel?
     
     /** Open the file */
     public void open() ;

http://git-wip-us.apache.org/repos/asf/jena/blob/67b69b5e/dboe-base/src/main/java/org/seaborne/dboe/base/file/FileFactory.java
----------------------------------------------------------------------
diff --git a/dboe-base/src/main/java/org/seaborne/dboe/base/file/FileFactory.java b/dboe-base/src/main/java/org/seaborne/dboe/base/file/FileFactory.java
index 390ba3b..ebdc1e4 100644
--- a/dboe-base/src/main/java/org/seaborne/dboe/base/file/FileFactory.java
+++ b/dboe-base/src/main/java/org/seaborne/dboe/base/file/FileFactory.java
@@ -17,10 +17,6 @@
 
 package org.seaborne.dboe.base.file ;
 
-import org.seaborne.dboe.base.objectfile.ObjectFile ;
-import org.seaborne.dboe.base.objectfile.ObjectFileStorage ;
-import org.seaborne.dboe.base.objectfile.StringFile ;
-
 public class FileFactory {
     
     public static BinaryDataFile createBinaryDataFile(FileSet fileset, String ext) {
@@ -38,27 +34,6 @@ public class FileFactory {
         return new BinaryDataFileMem() ;
     }
 
-    // TODO More use of fileset.
-    // Prune use elsewhere e.g BlockMgrFactory.
-    
-    public static StringFile createStringFileDisk(String filename) {
-        return new StringFile(createObjectFileDisk(filename)) ;
-    }
-
-    public static StringFile createStringFileMem(String filename) {
-        return new StringFile(createObjectFileMem(filename)) ;
-    }
-
-    public static ObjectFile createObjectFileDisk(String filename) {
-        BufferChannel file = BufferChannelFile.create(filename) ;
-        return new ObjectFileStorage(file) ;
-    }
-
-    public static ObjectFile createObjectFileMem(String filename) {
-        BufferChannel file = BufferChannelMem.create(filename) ;
-        return new ObjectFileStorage(file) ;
-    }
-
     public static BufferChannel createBufferChannel(FileSet fileset, String ext) {
         String x = fileset.filename(ext) ;
         if ( fileset.isMem() )

http://git-wip-us.apache.org/repos/asf/jena/blob/67b69b5e/dboe-base/src/main/java/org/seaborne/dboe/base/objectfile/ObjectFile.java
----------------------------------------------------------------------
diff --git a/dboe-base/src/main/java/org/seaborne/dboe/base/objectfile/ObjectFile.java b/dboe-base/src/main/java/org/seaborne/dboe/base/objectfile/ObjectFile.java
deleted file mode 100644
index a210d55..0000000
--- a/dboe-base/src/main/java/org/seaborne/dboe/base/objectfile/ObjectFile.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- *  Licensed 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.
- *
- *  See the NOTICE file distributed with this work for additional
- *  information regarding copyright ownership.
- */
-
-package org.seaborne.dboe.base.objectfile;
-
-import java.nio.ByteBuffer ;
-import java.util.Iterator ;
-
-import org.apache.jena.atlas.lib.Closeable ;
-import org.apache.jena.atlas.lib.Pair ;
-import org.apache.jena.atlas.lib.Sync ;
-import org.seaborne.dboe.base.block.Block ;
-
-/** 
- * An ObjectFile is an append-read file, that is you can append data
- * to the stream or read any block.
- */
-
-public interface ObjectFile extends Sync, Closeable
-{
-    public static final String type = "object" ;
-    
-    /** A label to identify this ObjectFile */ 
-    public String getLabel() ;
-    
-    /** Allocate space for a write - pass this buffer to completeWrite .
-     * The data to be written can be smaller than that requested but
-     * the data must be in position 0 -> limit.   
-     */
-    public Block allocWrite(int bytesSpace) ;
-    
-    /** Announce that a write is complete (buffer must come from allocWrite)
-     */  
-    public void completeWrite(Block buffer) ;
-
-    /** Decide not to perform the write */
-    public void abortWrite(Block buffer) ;
-
-    /** Write out the buffer - return the accessor number */ 
-    public long write(ByteBuffer buffer) ;
-
-    /** Read a buffer at the accessor number. */
-    public ByteBuffer read(long id) ;
-    
-    /** Length, in bytes
-     * @see #truncate
-     */
-    public long length() ;
-
-    /** The append point.  The length, in units used by read/write for ids.
-     * @see #reposition
-     */
-    public long position() ;
-    
-    /** Any objects in this file? */
-    public boolean isEmpty() ;
-
-    /** Reset the "append" point; may only be moved earlier.
-     * All data after the new point is lost.
-     * The new position must correspond to a position returned by
-     * {@link #write(ByteBuffer)} or an id in a {@link Block Block} from {@link #completeWrite(Block)}
-     * <p>
-     * {@link #truncate} is in bytes;
-     * {@link #reposition} is in units returned {@link #write}
-     * @see #position
-     */
-    public void reposition(long id) ;
-    
-    /** Truncate the file.
-     * The new file point should correspond to a position used by {@link #write}, otherwise the
-     * data written from the last valid position to this point is corrupted.
-     * <p>
-     * {@link #truncate} is in bytes;
-     * {@link #reposition} is in units returned {@link #write}
-     * @see #length
-     */
-    public void truncate(long size) ;
-
-    /** All the bytebuffers - debugging aid */
-    public Iterator<Pair<Long, ByteBuffer>> all() ;
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/67b69b5e/dboe-base/src/main/java/org/seaborne/dboe/base/objectfile/ObjectFileBuilder.java
----------------------------------------------------------------------
diff --git a/dboe-base/src/main/java/org/seaborne/dboe/base/objectfile/ObjectFileBuilder.java b/dboe-base/src/main/java/org/seaborne/dboe/base/objectfile/ObjectFileBuilder.java
deleted file mode 100644
index c8082af..0000000
--- a/dboe-base/src/main/java/org/seaborne/dboe/base/objectfile/ObjectFileBuilder.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- *  Licensed 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.
- *
- *  See the NOTICE file distributed with this work for additional
- *  information regarding copyright ownership.
- */
-
-package org.seaborne.dboe.base.objectfile;
-
-import org.seaborne.dboe.base.file.FileSet ;
-
-public interface ObjectFileBuilder {
-    ObjectFile buildObjectFile(FileSet fileSet, String ext) ;
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/67b69b5e/dboe-base/src/main/java/org/seaborne/dboe/base/objectfile/ObjectFileLogger.java
----------------------------------------------------------------------
diff --git a/dboe-base/src/main/java/org/seaborne/dboe/base/objectfile/ObjectFileLogger.java b/dboe-base/src/main/java/org/seaborne/dboe/base/objectfile/ObjectFileLogger.java
deleted file mode 100644
index 25d5316..0000000
--- a/dboe-base/src/main/java/org/seaborne/dboe/base/objectfile/ObjectFileLogger.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- *  Licensed 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.
- *
- *  See the NOTICE file distributed with this work for additional
- *  information regarding copyright ownership.
- */
-
-package org.seaborne.dboe.base.objectfile;
-
-import java.nio.ByteBuffer ;
-import java.util.Iterator ;
-
-import org.apache.jena.atlas.lib.Pair ;
-import org.seaborne.dboe.base.block.Block ;
-import org.slf4j.Logger ;
-import org.slf4j.LoggerFactory ;
-
-public class ObjectFileLogger implements ObjectFile
-{
-    protected final ObjectFile other ;
-    private static Logger defaultLogger = LoggerFactory.getLogger(ObjectFile.class) ; 
-    private final Logger log ;
-    private final String label  ;
-
-    public ObjectFileLogger(String label, ObjectFile other)
-    {
-        this.other = other ;
-        this.label = label ;
-        log = defaultLogger ;
-    }
-
-    @Override
-    public Block allocWrite(int maxBytes)
-    {
-        Block blk = other.allocWrite(maxBytes) ;
-        info("allocWrite("+maxBytes+") -> "+blk.getId()) ;
-        return blk ;
-    }
-
-    @Override
-    public void completeWrite(Block buffer)
-    {
-        info("completeWrite("+buffer.getId()+")") ;
-        other.completeWrite(buffer) ;
-    }
-
-    @Override
-    public void abortWrite(Block buffer)
-    {
-        info("abortWrite("+buffer.getId()+")") ;
-        other.abortWrite(buffer) ;
-    }
-
-    @Override
-    public long write(ByteBuffer buffer)
-    {
-        info("write"+buffer) ;
-        return other.write(buffer) ;
-    }
-
-    @Override
-    public void reposition(long id)
-    {
-        info("reposition("+id+")") ;
-        other.reposition(id) ;
-    }
-
-    @Override
-    public ByteBuffer read(long id)
-    {
-        info("read("+id+")") ;
-        return other.read(id) ;
-    }
-
-    @Override
-    public Iterator<Pair<Long, ByteBuffer>> all()
-    {
-        info("all()") ;
-        return other.all() ;
-    }
-
-    @Override
-    public void truncate(long size)
-    {
-        info("truncate("+size+")") ;
-        other.truncate(size) ;
-    }
-
-    @Override
-    public void sync()
-    {
-        info("sync") ;
-        other.sync() ;
-    }
-
-    @Override
-    public void close()
-    {
-        info("close") ;
-        other.close() ;
-    }
-
-    @Override
-    public String getLabel()
-    {
-        return other.getLabel() ;
-    }
-
-    @Override
-    public long length()
-    {
-        return position() ;
-    }
-
-    @Override
-    public long position()
-    {
-        return other.position() ;
-    }
-    
-    @Override
-    public boolean isEmpty()
-    {
-        return other.isEmpty() ;
-    }
-
-    private void info(String string)
-    {
-        if ( label != null )
-            string = label+": "+string ;
-        log.info(string) ; 
-    }
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/67b69b5e/dboe-base/src/main/java/org/seaborne/dboe/base/objectfile/ObjectFileStorage.java
----------------------------------------------------------------------
diff --git a/dboe-base/src/main/java/org/seaborne/dboe/base/objectfile/ObjectFileStorage.java b/dboe-base/src/main/java/org/seaborne/dboe/base/objectfile/ObjectFileStorage.java
deleted file mode 100644
index fc34c22..0000000
--- a/dboe-base/src/main/java/org/seaborne/dboe/base/objectfile/ObjectFileStorage.java
+++ /dev/null
@@ -1,476 +0,0 @@
-/*
- *  Licensed 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.
- *
- *  See the NOTICE file distributed with this work for additional
- *  information regarding copyright ownership.
- */
-
-package org.seaborne.dboe.base.objectfile;
-
-import static org.seaborne.dboe.sys.Sys.SizeOfInt ;
-
-import java.nio.ByteBuffer ;
-import java.util.Iterator ;
-
-import org.apache.jena.atlas.iterator.Iter ;
-import org.apache.jena.atlas.iterator.IteratorSlotted ;
-import org.apache.jena.atlas.lib.Pair ;
-import org.apache.jena.atlas.logging.Log ;
-import org.seaborne.dboe.base.block.Block ;
-import org.seaborne.dboe.base.file.BufferChannel ;
-import org.seaborne.dboe.base.file.FileException ;
-import org.seaborne.dboe.sys.Sys ;
-import org.seaborne.dboe.sys.SystemFile ;
-import org.slf4j.Logger ;
-import org.slf4j.LoggerFactory ;
-
-/** Variable length ByteBuffer file on disk. 
- *  Buffering for delayed writes.
- */  
-
-public class ObjectFileStorage implements ObjectFile 
-{
-    private static Logger log = LoggerFactory.getLogger(ObjectFileStorage.class) ;
-    public static boolean logging = false ;
-
-    private void log(String fmt, Object... args) {
-        if ( !logging )
-            return ;
-        log.debug(state() + " " + String.format(fmt, args)) ;
-    }
-
-    /* 
-     * No synchronization - assumes that the caller has some appropriate lock
-     * because the combination of file and cache operations needs to be thread safe.
-     * 
-     * The position of the channel is assumed to be the end of the file always.
-     * Read operations are done with absolute channel calls, 
-     * which do not reset the position.
-     * 
-     * Writing is buffered.
-     */
-    
-    // The object length slot.  
-    private ByteBuffer lengthBuffer = ByteBuffer.allocate(SizeOfInt) ;
-    
-    // Delayed write buffer.
-    private final ByteBuffer writeBuffer ;
-    
-    private final BufferChannel file ;
-    private long filesize ; 
-    
-    // Two-step write - alloc, write
-    private boolean inAllocWrite = false ;
-    private Block allocBlock = null ;
-    private long allocLocation = -1 ;
-    
-    // Old values for aborting a allocate-write
-    int oldBufferPosn = -1 ;
-    int oldBufferLimit = -1 ;
-
-    public ObjectFileStorage(BufferChannel file) {
-        this(file, SystemFile.ObjectFileWriteBufferSize) ;
-    }
-
-    public ObjectFileStorage(BufferChannel file, int bufferSize) {
-        this.file = file ;
-        filesize = file.size() ;
-        this.file.position(filesize) ; // End of file.
-        log("File size: 0x%X, posn: 0x%X", filesize, file.position()) ;
-        writeBuffer = (bufferSize >= 0) ? ByteBuffer.allocate(bufferSize) : null ;
-    }
-
-    @Override
-    public long write(ByteBuffer bb) {
-        log("W") ;
-
-        if ( inAllocWrite )
-            Log.error(this, "In the middle of an alloc-write") ;
-        inAllocWrite = false ;
-        if ( writeBuffer == null ) {
-            long x = rawWrite(bb) ;
-            log("W -> 0x%X", x) ;
-            return x ;
-        }
-
-        int len = bb.limit() - bb.position() ;
-        int spaceNeeded = len + SizeOfInt ;
-
-        if ( writeBuffer.position() + spaceNeeded > writeBuffer.capacity() )
-            // No room - flush.
-            flushOutputBuffer() ;
-        if ( writeBuffer.position() + spaceNeeded > writeBuffer.capacity() ) {
-            long x = rawWrite(bb) ;
-            if ( logging )
-                log("W -> 0x%X", x) ;
-            return x ;
-        }
-
-        long loc = writeBuffer.position() + filesize ;
-        writeBuffer.putInt(len) ;
-        writeBuffer.put(bb) ;
-        if ( logging )
-            log("W -> 0x%X", loc) ;
-        return loc ;
-    }
-
-    private long rawWrite(ByteBuffer bb) {
-        if ( logging )
-            log("RW %s", bb) ;
-        int len = bb.limit() - bb.position() ;
-        lengthBuffer.rewind() ;
-        lengthBuffer.putInt(len) ;
-        lengthBuffer.flip() ;
-        long location = file.position() ;
-        file.write(lengthBuffer) ;
-        int x = file.write(bb) ;
-        if ( x != len )
-            throw new FileException() ;
-        filesize = filesize + x + SizeOfInt ;
-
-        if ( logging ) {
-            log("Posn: %d", file.position()) ;
-            log("RW ->0x%X", location) ;
-        }
-        return location ;
-    }
-
-    @Override
-    public Block allocWrite(int bytesSpace) {
-        // log.info("AW("+bytesSpace+"):"+state()) ;
-        if ( inAllocWrite )
-            Log.error(this, "In the middle of an alloc-write") ;
-
-        // Include space for length.
-        int spaceRequired = bytesSpace + SizeOfInt ;
-
-        // Find space.
-        if ( writeBuffer != null && spaceRequired > writeBuffer.remaining() )
-            flushOutputBuffer() ;
-
-        if ( writeBuffer == null || spaceRequired > writeBuffer.remaining() ) {
-            // Too big. Have flushed buffering if buffering.
-            inAllocWrite = true ;
-            ByteBuffer bb = ByteBuffer.allocate(bytesSpace) ;
-            allocBlock = new Block(filesize, bb) ;
-            allocLocation = -1 ;
-            // log.info("AW:"+state()+"-> ----") ;
-            return allocBlock ;
-        }
-
-        // Will fit.
-        inAllocWrite = true ;
-        int start = writeBuffer.position() ;
-        // Old values for restoration
-        oldBufferPosn = start ;
-        oldBufferLimit = writeBuffer.limit() ;
-
-        // id (but don't tell the caller yet).
-        allocLocation = filesize + start ;
-
-        // Slice it.
-        writeBuffer.putInt(bytesSpace) ;
-        writeBuffer.position(start + SizeOfInt) ;
-        writeBuffer.limit(start + spaceRequired) ;
-        ByteBuffer bb = writeBuffer.slice() ;
-
-        allocBlock = new Block(allocLocation, bb) ;
-
-        if ( logging )
-            log("AW: %s->0x%X", state(), allocLocation) ;
-        return allocBlock ;
-    }
-
-    @Override
-    public void completeWrite(Block block) {
-        if ( logging )
-            log("CW: %s @0x%X", block, allocLocation) ;
-        if ( !inAllocWrite )
-            throw new FileException("Not in the process of an allocated write operation pair") ;
-        if ( allocBlock != null && (allocBlock.getByteBuffer() != block.getByteBuffer()) )
-            throw new FileException("Wrong byte buffer in an allocated write operation pair") ;
-
-        inAllocWrite = false ;
-
-        ByteBuffer buffer = block.getByteBuffer() ;
-
-        if ( allocLocation == -1 ) {
-            // It was too big to use the buffering.
-            rawWrite(buffer) ;
-            return ;
-        }
-        // Write area is 0 -> limit
-        if ( 0 != buffer.position() )
-            log.warn("ObjectFleStorage: position != 0") ;
-        buffer.position(0) ;
-        int actualLength = buffer.limit() - buffer.position() ;
-        // Insert object length
-        int idx = (int)(allocLocation - filesize) ;
-        writeBuffer.putInt(idx, actualLength) ;
-        // And bytes to idx+actualLength+4 are used
-        allocBlock = null ;
-        int newLen = idx + actualLength + 4 ;
-        writeBuffer.position(newLen) ;
-        writeBuffer.limit(writeBuffer.capacity()) ;
-        allocLocation = -1 ;
-        oldBufferPosn = -1 ;
-        oldBufferLimit = -1 ;
-    }
-
-    @Override
-    public void abortWrite(Block block) {
-        if ( ! inAllocWrite )
-            return ;
-        allocBlock = null ;
-        int oldstart = (int)(allocLocation - filesize) ;
-        if ( oldstart != oldBufferPosn )
-            throw new FileException("Wrong reset point: calc=" + oldstart + " : expected=" + oldBufferPosn) ;
-
-        writeBuffer.position(oldstart) ;
-        writeBuffer.limit(oldBufferLimit) ;
-        allocLocation = -1 ;
-        oldBufferPosn = -1 ;
-        oldBufferLimit = -1 ;
-        inAllocWrite = false ;
-    }
-
-    private void flushOutputBuffer() {
-        if ( logging )
-            log("Flush") ;
-
-        if ( writeBuffer == null )
-            return ;
-        if ( writeBuffer.position() == 0 )
-            return ;
-
-        if ( false ) {
-            String x = getLabel() ;
-            if ( x.contains("nodes") ) {
-                long x1 = filesize ;
-                long x2 = writeBuffer.position() ;
-                long x3 = x1 + x2 ;
-                System.out.printf("Flush(%s) : %d/0x%04X (%d/0x%04X) %d/0x%04X\n", getLabel(), x1, x1, x2, x2, x3, x3) ;
-            }
-        }
-
-        long location = filesize ;
-        writeBuffer.flip() ;
-        int x = file.write(writeBuffer) ;
-        filesize += x ;
-        writeBuffer.clear() ;
-    }
-
-    @Override
-    public void reposition(long posn) {
-        if ( inAllocWrite )
-            throw new FileException("In the middle of an alloc-write") ;
-        if ( posn < 0 || posn > position() )
-            throw new IllegalArgumentException("reposition: Bad location: " + posn) ;
-        flushOutputBuffer() ;
-        file.truncate(posn) ;
-        filesize = posn ;
-    }
-
-    @Override
-    public void truncate(long size) {
-        // System.out.println("truncate: "+size+" ("+filesize+","+writeBuffer.position()+")")
-        // ;
-        reposition(size) ;
-    }
-
-    @Override
-    public ByteBuffer read(long loc) {
-        if ( logging )
-            log("R(0x%X)", loc) ;
-
-        if ( inAllocWrite )
-            throw new FileException("In the middle of an alloc-write") ;
-        if ( loc < 0 )
-            throw new IllegalArgumentException("ObjectFile.read[" + file.getLabel() + "]: Bad read: " + loc) ;
-
-        // Maybe it's in the in the write buffer.
-        // Maybe the write buffer should keep more structure?
-        if ( loc >= filesize ) {
-            if ( loc >= filesize + writeBuffer.position() )
-                throw new IllegalArgumentException("ObjectFileStorage.read[" + file.getLabel() + "]: Bad read: location=" + loc
-                                                   + " >= max=" + (filesize + writeBuffer.position())) ;
-
-            int x = writeBuffer.position() ;
-            int y = writeBuffer.limit() ;
-
-            int offset = (int)(loc - filesize) ;
-            int len = writeBuffer.getInt(offset) ;
-            int posn = offset + SizeOfInt ;
-            // Slice the data bytes,
-            writeBuffer.position(posn) ;
-            writeBuffer.limit(posn + len) ;
-            ByteBuffer bb = writeBuffer.slice() ;
-            writeBuffer.limit(y) ;
-            writeBuffer.position(x) ;
-            return bb ;
-        }
-
-        // No - it's in the underlying file storage.
-        lengthBuffer.clear() ;
-        int x = file.read(lengthBuffer, loc) ;
-        if ( x != 4 )
-            throw new FileException("ObjectFileStorage.read[" + file.getLabel() + "](" + loc + ")[filesize=" + filesize + "][file.size()="
-                                    + file.size() + "]: Failed to read the length : got " + x + " bytes") ;
-        int len = lengthBuffer.getInt(0) ;
-        // Sanity check.
-        if ( len > filesize - (loc + SizeOfInt) ) {
-            String msg = "ObjectFileStorage.read[" + file.getLabel() + "](" + loc + ")[filesize=" + filesize + "][file.size()="
-                         + file.size() + "]: Impossibly large object : " + len + " bytes > filesize-(loc+SizeOfInt)="
-                         + (filesize - (loc + SizeOfInt)) ;
-            Sys.errlog.error(msg) ;
-            throw new FileException(msg) ;
-        }
-
-        ByteBuffer bb = ByteBuffer.allocate(len) ;
-        if ( len == 0 )
-            // Zero bytes.
-            return bb ;
-        x = file.read(bb, loc + SizeOfInt) ;
-        bb.flip() ;
-        if ( x != len )
-            throw new FileException("ObjectFileStorage.read: Failed to read the object (" + len + " bytes) : got " + x + " bytes") ;
-        return bb ;
-    }
-
-    @Override
-    public long length() {
-        return position() ;
-    }
-
-    @Override
-    public long position() {
-        if ( writeBuffer == null )
-            return filesize ;
-        return filesize + writeBuffer.position() ;
-    }
-
-    @Override
-    public boolean isEmpty() {
-        if ( writeBuffer == null )
-            return filesize == 0 ;
-        return writeBuffer.position() == 0 && filesize == 0 ;
-    }
-
-    @Override
-    public void close() {
-        flushOutputBuffer() ;
-        file.close() ;
-    }
-
-    @Override
-    public void sync() {
-        flushOutputBuffer() ;
-        file.sync() ;
-    }
-
-    @Override
-    public String getLabel() {
-        return file.getLabel() ;
-    }
-
-    @Override
-    public String toString() {
-        return file.getLabel() ;
-    }
-
-    @Override
-    public Iterator<Pair<Long, ByteBuffer>> all() {
-        flushOutputBuffer() ;
-        // file.position(0) ;
-        ObjectIterator iter = new ObjectIterator(0, filesize) ;
-        // return iter ;
-
-        if ( writeBuffer == null || writeBuffer.position() == 0 )
-            return iter ;
-        return Iter.concat(iter, new BufferIterator(writeBuffer)) ;
-    }
-
-    private String state() {
-        if ( writeBuffer == null )
-            return String.format(getLabel() + ": filesize=0x%X, file=(0x%X, 0x%X)", filesize, file.position(), file.size()) ;
-        else
-            return String.format(getLabel() + ": filesize=0x%X, file=(0x%X, 0x%X), writeBuffer=(0x%X,0x%X)", filesize, file.position(),
-                                 file.size(), writeBuffer.position(), writeBuffer.limit()) ;
-
-    }
-
-    private class BufferIterator extends IteratorSlotted<Pair<Long, ByteBuffer>> implements Iterator<Pair<Long, ByteBuffer>> {
-        private ByteBuffer buffer ;
-        private int        posn ;
-
-        public BufferIterator(ByteBuffer buffer) {
-            this.buffer = buffer ;
-            this.posn = 0 ;
-        }
-
-        @Override
-        protected Pair<Long, ByteBuffer> moveToNext() {
-            if ( posn >= buffer.limit() )
-                return null ;
-
-            int x = buffer.getInt(posn) ;
-            posn += Sys.SizeOfInt ;
-            ByteBuffer bb = ByteBuffer.allocate(x) ;
-            int p = buffer.position() ;
-            buffer.position(posn) ;
-            buffer.get(bb.array()) ;
-            buffer.position(p) ;
-            posn += x ;
-            return new Pair<>((long)x, bb) ;
-        }
-
-        @Override
-        protected boolean hasMore() {
-            return posn < buffer.limit() ;
-        }
-
-    }
-
-    private class ObjectIterator implements Iterator<Pair<Long, ByteBuffer>> {
-        final private long start ;
-        final private long finish ;
-        private long       current ;
-
-        public ObjectIterator(long start, long finish) {
-            this.start = start ;
-            this.finish = finish ;
-            this.current = start ;
-        }
-
-        @Override
-        public boolean hasNext() {
-            return (current < finish) ;
-        }
-
-        @Override
-        public Pair<Long, ByteBuffer> next() {
-            // read, but reserving the file position.
-            long x = current ;
-            long filePosn = file.position() ;
-            ByteBuffer bb = read(current) ;
-            file.position(filePosn) ;
-            current = current + bb.limit() + 4 ;
-            return new Pair<>(x, bb) ;
-        }
-
-        @Override
-        public void remove() {
-            throw new UnsupportedOperationException() ;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/67b69b5e/dboe-base/src/main/java/org/seaborne/dboe/base/objectfile/ObjectFileSwitcher.java
----------------------------------------------------------------------
diff --git a/dboe-base/src/main/java/org/seaborne/dboe/base/objectfile/ObjectFileSwitcher.java b/dboe-base/src/main/java/org/seaborne/dboe/base/objectfile/ObjectFileSwitcher.java
deleted file mode 100644
index b3fe36a..0000000
--- a/dboe-base/src/main/java/org/seaborne/dboe/base/objectfile/ObjectFileSwitcher.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- *  Licensed 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.
- *
- *  See the NOTICE file distributed with this work for additional
- *  information regarding copyright ownership.
- */
-
-package org.seaborne.dboe.base.objectfile;
-
-
-/** 
- * An ObjectFile is an append-read file, that is you can append data
- * to the stream or read any block.
- */
-
-public class ObjectFileSwitcher extends ObjectFileWrapper
-{
-    protected final ObjectFile objFile1 ;
-    protected final ObjectFile objFile2 ;
-
-    public ObjectFileSwitcher(ObjectFile objFile1, ObjectFile objFile2)
-    {
-        super(objFile1) ;
-        this.objFile1 = objFile1 ;
-        this.objFile2 = objFile2 ;
-    }
-    
-    public final void switchover()
-    {
-        if ( super.other == objFile1 )
-            super.other = objFile2 ;
-        else
-            super.other = objFile1 ;
-    }
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/67b69b5e/dboe-base/src/main/java/org/seaborne/dboe/base/objectfile/ObjectFileWrapper.java
----------------------------------------------------------------------
diff --git a/dboe-base/src/main/java/org/seaborne/dboe/base/objectfile/ObjectFileWrapper.java b/dboe-base/src/main/java/org/seaborne/dboe/base/objectfile/ObjectFileWrapper.java
deleted file mode 100644
index 413b7e4..0000000
--- a/dboe-base/src/main/java/org/seaborne/dboe/base/objectfile/ObjectFileWrapper.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- *  Licensed 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.
- *
- *  See the NOTICE file distributed with this work for additional
- *  information regarding copyright ownership.
- */
-
-package org.seaborne.dboe.base.objectfile;
-
-import java.nio.ByteBuffer ;
-import java.util.Iterator ;
-
-import org.apache.jena.atlas.lib.Pair ;
-import org.seaborne.dboe.base.block.Block ;
-
-/** 
- * An ObjectFile is an append-read file, that is you can append data
- * to the stream or read any block.
- */
-
-public class ObjectFileWrapper implements ObjectFile
-{
-    protected ObjectFile other ;
-
-    public ObjectFileWrapper(ObjectFile other)      { this.other = other ; }
-    
-    @Override
-    public Block allocWrite(int maxBytes)           { return other.allocWrite(maxBytes) ; }
-
-    @Override
-    public void completeWrite(Block buffer)         { other.completeWrite(buffer) ; }
-
-    @Override
-    public void abortWrite(Block buffer)            { other.abortWrite(buffer) ; }
-    
-    @Override
-    public long write(ByteBuffer buffer)            { return other.write(buffer) ; }
-    
-    @Override
-    public void reposition(long id)                 { other.reposition(id) ; }
-
-    @Override
-    public void truncate(long size)                 { other.truncate(size) ; }
-
-    @Override
-    public ByteBuffer read(long id)                 { return other.read(id) ; }
-
-    @Override
-    public String getLabel()                        { return other.getLabel()  ; }
-
-    @Override
-    public Iterator<Pair<Long, ByteBuffer>> all()   { return other.all() ; }
-
-    @Override
-    public void sync()                              { other.sync() ; }
-
-    @Override
-    public void close()                             { other.close() ; }
-
-    @Override
-    public long length()                            { return other.length() ; }
-
-    @Override
-    public long position()                          { return other.position() ; }
-    
-    @Override
-    public boolean isEmpty()                        { return other.isEmpty() ; }
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/67b69b5e/dboe-base/src/main/java/org/seaborne/dboe/base/objectfile/StringFile.java
----------------------------------------------------------------------
diff --git a/dboe-base/src/main/java/org/seaborne/dboe/base/objectfile/StringFile.java b/dboe-base/src/main/java/org/seaborne/dboe/base/objectfile/StringFile.java
deleted file mode 100644
index d7f8b60..0000000
--- a/dboe-base/src/main/java/org/seaborne/dboe/base/objectfile/StringFile.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- *  Licensed 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.
- *
- *  See the NOTICE file distributed with this work for additional
- *  information regarding copyright ownership.
- */
-
-package org.seaborne.dboe.base.objectfile;
-
-import java.nio.ByteBuffer ;
-
-import org.apache.jena.atlas.lib.Bytes ;
-import org.apache.jena.atlas.lib.Closeable ;
-import org.apache.jena.atlas.lib.Sync ;
-import org.seaborne.dboe.base.block.Block ;
-
-/** Wrap a {@link ObjectFile} with a string encoder/decoder.  
- * Controls the UTF encoder/decoder and is not limited to 64K byte encoded forms.
- */
-
-public class StringFile implements Sync, Closeable
-{
-    // TODO : 
-    
-    protected final ObjectFile file ;
-    /*
-     * Encoding: Simple for now:
-     *   length (4 bytes)
-     *   UTF-8 bytes. 
-     */
-    
-    public StringFile(ObjectFile file)
-    {
-        this.file = file ;
-    }
-    
-    public long write(String str)
-    { 
-        str = compress(str) ;
-        Block block = file.allocWrite(4*str.length()) ;
-        int len = Bytes.toByteBuffer(str, block.getByteBuffer()) ;
-        block.getByteBuffer().flip() ;
-        file.completeWrite(block) ;
-        return block.getId() ;
-    }
-    
-    public String read(long id)
-    {
-        ByteBuffer bb = file.read(id) ;
-        String x = Bytes.fromByteBuffer(bb) ;
-        x = decompress(x) ;
-        return x ;
-    }
-
-    @Override
-    public void close()
-    { file.close() ; }
-
-    @Override
-    public void sync() { file.sync() ; }
-    
-    public void flush() { sync() ; }
-
-    public ObjectFile getByteBufferFile()
-    {
-        return file ;
-    }
-    
-    // ---- Dump
-    public void dump() { dump(handler) ; }
-
-    public interface DumpHandler { void handle(long fileIdx, String str) ; }  
-    
-    public void dump(DumpHandler handler)
-    {
-        long fileIdx = 0 ;
-        while ( true )
-        {
-            ByteBuffer bb = file.read(fileIdx) ;
-            String str = Bytes.fromByteBuffer(bb) ;
-            handler.handle(fileIdx, str) ;
-            fileIdx = fileIdx + bb.limit() + 4 ;
-            if ( fileIdx >= file.position() )
-                break ;
-        }
-    }
-    
-    static StringFile.DumpHandler handler = new StringFile.DumpHandler() {
-        @Override
-        public void handle(long fileIdx, String str)
-        {
-            System.out.printf("0x%08X : %s\n", fileIdx, str) ;
-        }
-    } ;
-    
-    // Shodul be String -> Bytes.
-    private String compress(String str) { return str ; }
-    private String decompress(String x) { return x ; }
-    // ----
- 
-    
-//    // URI compression can be effective but literals are more of a problem.  More variety. 
-//    public final static boolean compression = false ; 
-//    private static StringAbbrev abbreviations = new StringAbbrev() ;
-//    static {
-//        abbreviations.add(  "rdf",      "<http://www.w3.org/1999/02/22-rdf-syntax-ns#") ;
-//        abbreviations.add(  "rdfs",     "<http://www.w3.org/2000/01/rdf-schema#") ;
-//        abbreviations.add(  "xsd",      "<http://www.w3.org/2001/XMLSchema#") ;
-//        
-//        // MusicBrainz
-//        abbreviations.add(  "mal",      "<http://musicbrainz.org/mm-2.1/album/") ;
-//        abbreviations.add(  "mt",       "<http://musicbrainz.org/mm-2.1/track/") ;
-//        abbreviations.add(  "mar",      "<http://musicbrainz.org/mm-2.1/artist/") ;
-//        abbreviations.add(  "mtr",      "<http://musicbrainz.org/mm-2.1/trmid/") ;
-//        abbreviations.add(  "mc",       "<http://musicbrainz.org/mm-2.1/cdindex/") ;
-//        
-//        abbreviations.add(  "m21",      "<http://musicbrainz.org/mm/mm-2.1#") ;
-//        abbreviations.add(  "dc",       "<http://purl.org/dc/elements/1.1/") ;
-//        // DBPedia
-//        abbreviations.add(  "r",        "<http://dbpedia/resource/") ;
-//        abbreviations.add(  "p",        "<http://dbpedia/property/") ;
-//    }
-//    private String compress(String str)
-//    {
-//        if ( !compression || abbreviations == null ) return str ;
-//        return abbreviations.abbreviate(str) ;
-//    }
-//
-//    private String decompress(String x)
-//    {
-//        if ( !compression || abbreviations == null ) return x ;
-//        return abbreviations.expand(x) ;
-//    }
-
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/67b69b5e/dboe-base/src/test/java/org/seaborne/dboe/TC_DBOE_IO.java
----------------------------------------------------------------------
diff --git a/dboe-base/src/test/java/org/seaborne/dboe/TC_DBOE_IO.java b/dboe-base/src/test/java/org/seaborne/dboe/TC_DBOE_IO.java
index 6eae773..35a697d 100644
--- a/dboe-base/src/test/java/org/seaborne/dboe/TC_DBOE_IO.java
+++ b/dboe-base/src/test/java/org/seaborne/dboe/TC_DBOE_IO.java
@@ -25,8 +25,8 @@ import org.junit.runner.RunWith ;
 import org.junit.runners.Suite ;
 import org.seaborne.dboe.base.block.FileMode ;
 import org.seaborne.dboe.base.block.TS_Block ;
+import org.seaborne.dboe.base.buffer.TS_Buffer ;
 import org.seaborne.dboe.base.file.TS_File ;
-import org.seaborne.dboe.base.objectfile.TS_ObjectFile ;
 import org.seaborne.dboe.base.record.TS_Record ;
 import org.seaborne.dboe.base.recordfile.TS_RecordFile ;
 import org.seaborne.dboe.sys.SystemIndex ;
@@ -35,9 +35,9 @@ import org.seaborne.dboe.sys.SystemIndex ;
 @Suite.SuiteClasses( {
     TS_Block.class
     , TS_File.class
+    , TS_Buffer.class
     , TS_Record.class
     , TS_RecordFile.class
-    , TS_ObjectFile.class
 } )
 
 public class TC_DBOE_IO {

http://git-wip-us.apache.org/repos/asf/jena/blob/67b69b5e/dboe-base/src/test/java/org/seaborne/dboe/base/TC_Base.java
----------------------------------------------------------------------
diff --git a/dboe-base/src/test/java/org/seaborne/dboe/base/TC_Base.java b/dboe-base/src/test/java/org/seaborne/dboe/base/TC_Base.java
deleted file mode 100644
index c02f079..0000000
--- a/dboe-base/src/test/java/org/seaborne/dboe/base/TC_Base.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- *  Licensed 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.
- *
- *  See the NOTICE file distributed with this work for additional
- *  information regarding copyright ownership.
- */
-
-package org.seaborne.dboe.base;
-
-import org.junit.runner.RunWith ;
-import org.junit.runners.Suite ;
-import org.seaborne.dboe.base.block.TS_Block ;
-import org.seaborne.dboe.base.buffer.TS_Buffer ;
-import org.seaborne.dboe.base.file.TS_File ;
-import org.seaborne.dboe.base.objectfile.TS_ObjectFile ;
-import org.seaborne.dboe.base.record.TS_Record ;
-import org.seaborne.dboe.base.recordfile.TS_RecordFile ;
-
-@RunWith(Suite.class)
-@Suite.SuiteClasses( {
-      TS_Block.class
-    , TS_File.class
-    , TS_Buffer.class
-    , TS_Record.class
-    , TS_RecordFile.class
-    , TS_ObjectFile.class
-})
-
-
-public class TC_Base
-{}

http://git-wip-us.apache.org/repos/asf/jena/blob/67b69b5e/dboe-base/src/test/java/org/seaborne/dboe/base/objectfile/AbstractTestObjectFile.java
----------------------------------------------------------------------
diff --git a/dboe-base/src/test/java/org/seaborne/dboe/base/objectfile/AbstractTestObjectFile.java b/dboe-base/src/test/java/org/seaborne/dboe/base/objectfile/AbstractTestObjectFile.java
deleted file mode 100644
index 475a9cc..0000000
--- a/dboe-base/src/test/java/org/seaborne/dboe/base/objectfile/AbstractTestObjectFile.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- *  Licensed 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.
- *
- *  See the NOTICE file distributed with this work for additional
- *  information regarding copyright ownership.
- */
-
-package org.seaborne.dboe.base.objectfile;
-
-import static org.seaborne.dboe.test.BufferTestLib.sameValue ;
-
-import java.nio.ByteBuffer ;
-
-import org.junit.Assert ;
-import org.junit.After ;
-import org.junit.Before ;
-import org.junit.Test ;
-import org.seaborne.dboe.base.block.Block ;
-import org.seaborne.dboe.base.objectfile.ObjectFile ;
-
-public abstract class AbstractTestObjectFile extends Assert
-{
-    ObjectFile file ;
-
-    @Before public void before() { file = make() ; }
-    @After public void after() { release(file); }
-    
-    @Test public void objectfile_01()
-    {
-
-        assertEquals(0, file.position()) ;
-    }
-
-    @Test public void objectfile_02()
-    {
-        Block block = file.allocWrite(10) ;
-        fill(block.getByteBuffer()) ;
-        file.completeWrite(block) ;
-        long x1 = block.getId() ;
-        assertEquals(0, x1) ;
-        
-        ByteBuffer bb = file.read(x1) ;
-        
-        // position
-        
-        assertTrue(sameValue(block.getByteBuffer(), bb)) ;
-    }
-
-    @Test public void objectfile_03()
-    {
-        ByteBuffer bb = ByteBuffer.allocate(10) ;
-        fill(bb) ;
-        long x1 = file.write(bb) ;
-        assertEquals(0, x1) ;
-    }
-
-    @Test public void objectfile_04()
-    {
-        Block block1 = file.allocWrite(10) ;
-        fill(block1.getByteBuffer()) ;
-        file.completeWrite(block1) ;
-        
-        Block block2 = file.allocWrite(20) ;
-        fill(block2.getByteBuffer()) ;
-        file.completeWrite(block2) ;
-        
-        long x1 = block1.getId() ;
-        long x2 = block2.getId() ;
-        
-        assertFalse(x1 == x2) ;
-    }
-
-    @Test public void objectfile_05()
-    {
-        ByteBuffer bb1 = ByteBuffer.allocate(10) ;
-        fill(bb1) ;
-        
-        ByteBuffer bb2 = ByteBuffer.allocate(20) ;
-        fill(bb2) ;
-        long x1 = file.write(bb1) ;
-        long x2 = file.write(bb2) ;
-        
-        assertFalse(x1 == x2) ;
-    }
-
-    @Test public void objectfile_06()
-    {
-        ByteBuffer bb1 = ByteBuffer.allocate(10) ;
-        fill(bb1) ;
-        
-        ByteBuffer bb2 = ByteBuffer.allocate(20) ;
-        fill(bb2) ;
-
-        long x1 = file.write(bb1) ;
-        long x2 = file.write(bb2) ;
-        
-        ByteBuffer bb1a = file.read(x1) ;
-        ByteBuffer bb2a = file.read(x2) ;
-        assertNotSame(bb1a, bb2a) ;
-        assertTrue(sameValue(bb1, bb1a)) ;
-        assertTrue(sameValue(bb2, bb2a)) ;
-    }
-    
-//    // Oversized writes.
-//
-//    @Test public void objectfile_07()
-//    {
-//        
-//    }
-//
-//    @Test public void objectfile_08()
-//    {}
-//
-//    @Test public void objectfile_09()
-//    {}
-//
-//    @Test public void objectfile_10()
-//    {}
-    
-    public static void fill(ByteBuffer byteBuffer)
-    {
-        int len = byteBuffer.remaining() ;
-        for ( int i = 0 ; i < len ; i++ )
-            byteBuffer.put((byte)(i&0xFF)) ;
-        byteBuffer.rewind() ;
-    }
-
-    protected abstract ObjectFile make() ;
-    protected abstract void release(ObjectFile file) ;
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/67b69b5e/dboe-base/src/test/java/org/seaborne/dboe/base/objectfile/AbstractTestStringFile.java
----------------------------------------------------------------------
diff --git a/dboe-base/src/test/java/org/seaborne/dboe/base/objectfile/AbstractTestStringFile.java b/dboe-base/src/test/java/org/seaborne/dboe/base/objectfile/AbstractTestStringFile.java
deleted file mode 100644
index 6475833..0000000
--- a/dboe-base/src/test/java/org/seaborne/dboe/base/objectfile/AbstractTestStringFile.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- *  Licensed 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.
- *
- *  See the NOTICE file distributed with this work for additional
- *  information regarding copyright ownership.
- */
-
-package org.seaborne.dboe.base.objectfile;
-
-import org.junit.Assert ;
-import org.junit.After ;
-import org.junit.Before ;
-import org.junit.Test ;
-import org.seaborne.dboe.base.objectfile.StringFile ;
-
-public abstract class AbstractTestStringFile extends Assert
-{
-    StringFile f = null ;
-    
-    @Before public void setup() { f = createStringFile() ; }
-    @After public void teardown() { removeStringFile(f) ; }
-    
-    protected abstract StringFile createStringFile() ;
-    protected abstract void removeStringFile(StringFile f) ;
-
-    @Test public void object_file_01()
-    {
-        String x1 = "abc" ;
-        long id1 = f.write(x1) ;
-        test(id1, x1) ;
-        test(0, x1) ;
-    }
-
-    @Test public void object_file_02()
-    {
-        String x1 = "" ;
-        
-        long id1 = f.write(x1) ;
-        test(id1, x1) ;
-        test(0, x1) ;
-    }
-
-    @Test public void object_file_03()
-    {
-        String x1 = "abbbbbbc" ;
-        String x2 = "deeeef" ;
-        
-        long id1 = f.write(x1) ;
-        long id2 = f.write(x2) ;
-        
-        assertNotEquals("Node Ids", id1, id2) ;
-        
-        test(id1, x1) ;
-        test(id2, x2) ;
-        test(0, x1) ;
-    }
-    
-    @Test public void object_file_04()
-    {
-        String x1 = "abbbbbbc" ;
-        String x2 = "deeeef" ;
-        
-        long id1 = f.write(x1) ;
-        long id2 = f.write(x2) ;
-        // Read in opposite order
-        test(id2, x2) ;
-        test(id1, x1) ;
-    }
-
-    @Test public void object_file_05()
-    {
-        String x = "孫子兵法" ;
-        
-        long id = f.write(x) ;
-        test(0, x) ;
-    }
-    
-    @Test public void object_file_06()
-    {
-        String x1 = "abbbbbbc" ;
-        String x2 = "孫子兵法" ;
-        
-        long id1 = f.write(x1) ;
-        f.flush() ;
-        long id2 = f.write(x2) ;
-        // No flush.
-        
-        assertNotEquals("Node Ids", id1, id2) ;
-        
-        String z = f.read(id2) ;
-        
-        test(id2, x2) ;
-        test(id1, x1) ;
-        test(0, x1) ;
-    }
-    
-    @Test public void object_file_07()
-    {
-        String x1 = "abbbbbbc" ;
-        String x2 = "孫子兵法" ;
-        
-        long id1 = f.write(x1) ;
-        long id2 = f.write(x2) ;
-        f.flush() ;
-        
-        assertNotEquals("Node Ids", id1, id2) ;
-        
-        test(id2, x2) ;
-        test(id1, x1) ;
-        test(0, x1) ;
-    }
-
-    @Test public void object_file_08()
-    {
-        String x1 = "abbbbbbc" ;
-        String x2 = "孫子兵法" ;
-        
-        long id1 = f.write(x1) ;
-        f.flush() ;
-        long id2 = f.write(x2) ;
-        f.flush() ;
-        
-        assertNotEquals("Node Ids", id1, id2) ;
-        
-        test(id2, x2) ;
-        test(id1, x1) ;
-        test(0, x1) ;
-    }
-
-    private void test(long id, String x)
-    {
-        String y = f.read(id) ;
-        assertEquals(x, y) ;
-    }
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/67b69b5e/dboe-base/src/test/java/org/seaborne/dboe/base/objectfile/TS_ObjectFile.java
----------------------------------------------------------------------
diff --git a/dboe-base/src/test/java/org/seaborne/dboe/base/objectfile/TS_ObjectFile.java b/dboe-base/src/test/java/org/seaborne/dboe/base/objectfile/TS_ObjectFile.java
deleted file mode 100644
index d3b8704..0000000
--- a/dboe-base/src/test/java/org/seaborne/dboe/base/objectfile/TS_ObjectFile.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- *  Licensed 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.
- *
- *  See the NOTICE file distributed with this work for additional
- *  information regarding copyright ownership.
- */
-
-package org.seaborne.dboe.base.objectfile;
-
-import org.junit.runner.RunWith ;
-import org.junit.runners.Suite ;
-
-@RunWith(Suite.class)
-@Suite.SuiteClasses( {
-    TestObjectFileMem.class
-    //, TestObjectFileDisk.class
-    //, TestObjectFileBuffering.class
-    , TestStringFileMem.class
-    //, TestStringFileDisk.class
-})
-
-public class TS_ObjectFile
-{ }

http://git-wip-us.apache.org/repos/asf/jena/blob/67b69b5e/dboe-base/src/test/java/org/seaborne/dboe/base/objectfile/TestObjectFileBuffering.java
----------------------------------------------------------------------
diff --git a/dboe-base/src/test/java/org/seaborne/dboe/base/objectfile/TestObjectFileBuffering.java b/dboe-base/src/test/java/org/seaborne/dboe/base/objectfile/TestObjectFileBuffering.java
deleted file mode 100644
index 40ba9b3..0000000
--- a/dboe-base/src/test/java/org/seaborne/dboe/base/objectfile/TestObjectFileBuffering.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- *  Licensed 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.
- *
- *  See the NOTICE file distributed with this work for additional
- *  information regarding copyright ownership.
- */
-
-package org.seaborne.dboe.base.objectfile;
-
-import static org.seaborne.dboe.base.objectfile.AbstractTestObjectFile.fill ;
-import static org.seaborne.dboe.test.BufferTestLib.sameValue ;
-
-import java.nio.ByteBuffer ;
-
-import org.junit.Assert ;
-import org.junit.Test ;
-import org.seaborne.dboe.base.block.Block ;
-import org.seaborne.dboe.base.file.BufferChannel ;
-import org.seaborne.dboe.base.file.BufferChannelMem ;
-import org.seaborne.dboe.base.objectfile.ObjectFile ;
-import org.seaborne.dboe.base.objectfile.ObjectFileStorage ;
-
-public class TestObjectFileBuffering extends Assert
-{
-    protected ObjectFile make(int bufferSize)
-    {
-        BufferChannel chan = BufferChannelMem.create() ;
-        return new ObjectFileStorage(chan, bufferSize) ;
-    }
-
-    private void write(int sizeOfBuffer, int... sizes)
-    {
-        ObjectFile file = make(sizeOfBuffer) ;
-        int N = sizes.length ;
-        ByteBuffer bb[] = new ByteBuffer[N] ;
-        long loc[] = new long[N] ;
-        ByteBuffer read[] = new ByteBuffer[N] ;
-        
-        for ( int i = 0 ; i < N ; i++ )
-        {
-            bb[i] = ByteBuffer.allocate(sizes[i]) ;
-            fill(bb[i]) ;
-            loc[i] = file.write(bb[i]) ;
-        }
-        //file.sync() ;
-        for ( int i = 0 ; i < N ; i++ )
-        {
-            read[i] = file.read(loc[i]) ;
-            assertNotSame(bb[i], read[i]) ;
-            sameValue(bb[i], read[i]) ;
-        }
-    }
-    
-    private void writePrealloc(int sizeOfBuffer, int... sizes)
-    {
-        ObjectFile file = make(sizeOfBuffer) ;
-        int N = sizes.length ;
-        Block blocks[] = new Block[N] ;
-        ByteBuffer read[] = new ByteBuffer[N] ;
-        
-        for ( int i = 0 ; i < N ; i++ )
-        {
-            blocks[i] = file.allocWrite(sizes[i]) ;
-            fill(blocks[i].getByteBuffer()) ;
-            file.completeWrite(blocks[i]) ;
-        }
-
-        for ( int i = 0 ; i < N ; i++ )
-        {
-            read[i] = file.read(blocks[i].getId()) ;
-            assertNotSame(blocks[i].getByteBuffer(), read[i]) ;
-            sameValue(blocks[i].getByteBuffer(), read[i]) ;
-        }
-    }
-
-    
-    @Test public void objectfile_50()       { write(5, 10) ; }
-    @Test public void objectfile_51()       { writePrealloc(5, 10) ; }
-    @Test public void objectfile_52()       { write(12, 10) ; }
-    @Test public void objectfile_53()       { writePrealloc(12, 10) ; }
-    @Test public void objectfile_54()       { write(12, 10, 8) ; }          // 10 is too big
-    @Test public void objectfile_55()       { writePrealloc(12, 10, 8) ; }  // 10 is too big
-    @Test public void objectfile_56()       { write(12, 6, 10) ; }
-    @Test public void objectfile_57()       { writePrealloc(12, 6, 10) ; }
-    @Test public void objectfile_58()       { write(20, 6, 10, 5) ; }
-    @Test public void objectfile_59()       { writePrealloc(20, 6, 10, 5) ; }
-
-    @Test public void objectfile_60()       { write(20, 4, 4, 8) ; }
-    @Test public void objectfile_61()       { writePrealloc(20, 4, 4, 8) ; }
-
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/67b69b5e/dboe-base/src/test/java/org/seaborne/dboe/base/objectfile/TestObjectFileDisk.java
----------------------------------------------------------------------
diff --git a/dboe-base/src/test/java/org/seaborne/dboe/base/objectfile/TestObjectFileDisk.java b/dboe-base/src/test/java/org/seaborne/dboe/base/objectfile/TestObjectFileDisk.java
deleted file mode 100644
index 0639d83..0000000
--- a/dboe-base/src/test/java/org/seaborne/dboe/base/objectfile/TestObjectFileDisk.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- *  Licensed 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.
- *
- *  See the NOTICE file distributed with this work for additional
- *  information regarding copyright ownership.
- */
-
-package org.seaborne.dboe.base.objectfile;
-
-import org.apache.jena.atlas.lib.FileOps ;
-import org.junit.AfterClass ;
-import org.seaborne.dboe.ConfigTestDBOE ;
-import org.seaborne.dboe.base.file.BufferChannel ;
-import org.seaborne.dboe.base.file.BufferChannelFile ;
-import org.seaborne.dboe.base.objectfile.ObjectFile ;
-import org.seaborne.dboe.base.objectfile.ObjectFileStorage ;
-
-public class TestObjectFileDisk extends AbstractTestObjectFile
-{
-    static String filename = ConfigTestDBOE.getTestingDir()+"/test-objectfile" ;
-
-    @AfterClass public static void cleanup() { FileOps.deleteSilent(filename) ; } 
-    
-    @Override
-    protected ObjectFile make()
-    {
-        FileOps.deleteSilent(filename) ;
-        BufferChannel chan = BufferChannelFile.create(filename) ;
-        // No buffering.
-        return new ObjectFileStorage(chan, -1) ;
-    }
-    
-    @Override
-    protected void release(ObjectFile file)
-    {
-        file.truncate(0) ;
-        file.close() ;
-    }
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/67b69b5e/dboe-base/src/test/java/org/seaborne/dboe/base/objectfile/TestObjectFileMem.java
----------------------------------------------------------------------
diff --git a/dboe-base/src/test/java/org/seaborne/dboe/base/objectfile/TestObjectFileMem.java b/dboe-base/src/test/java/org/seaborne/dboe/base/objectfile/TestObjectFileMem.java
deleted file mode 100644
index 0e9e8e0..0000000
--- a/dboe-base/src/test/java/org/seaborne/dboe/base/objectfile/TestObjectFileMem.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- *  Licensed 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.
- *
- *  See the NOTICE file distributed with this work for additional
- *  information regarding copyright ownership.
- */
-
-package org.seaborne.dboe.base.objectfile;
-
-import org.seaborne.dboe.base.file.FileFactory ;
-import org.seaborne.dboe.base.objectfile.ObjectFile ;
-
-public class TestObjectFileMem extends AbstractTestObjectFile
-{
-
-    @Override
-    protected ObjectFile make()
-    {
-        return FileFactory.createObjectFileMem("test") ;
-    }
-
-    @Override
-    protected void release(ObjectFile file)
-    {
-        file.truncate(0) ;
-        file.close() ;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/67b69b5e/dboe-base/src/test/java/org/seaborne/dboe/base/objectfile/TestStringFileDisk.java
----------------------------------------------------------------------
diff --git a/dboe-base/src/test/java/org/seaborne/dboe/base/objectfile/TestStringFileDisk.java b/dboe-base/src/test/java/org/seaborne/dboe/base/objectfile/TestStringFileDisk.java
deleted file mode 100644
index 9e6ef71..0000000
--- a/dboe-base/src/test/java/org/seaborne/dboe/base/objectfile/TestStringFileDisk.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- *  Licensed 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.
- *
- *  See the NOTICE file distributed with this work for additional
- *  information regarding copyright ownership.
- */
-
-package org.seaborne.dboe.base.objectfile;
-
-import static org.apache.jena.atlas.lib.FileOps.clearDirectory ;
-import org.apache.jena.atlas.lib.FileOps ;
-import org.seaborne.dboe.ConfigTestDBOE ;
-import org.seaborne.dboe.base.file.FileFactory ;
-import org.seaborne.dboe.base.file.Location ;
-import org.seaborne.dboe.base.objectfile.StringFile ;
-
-public class TestStringFileDisk extends AbstractTestStringFile
-{
-    String fn = null ;
-    
-    @Override
-    protected StringFile createStringFile()
-    {
-        String dir = ConfigTestDBOE.getTestingDir() ;
-      clearDirectory(dir) ;
-      Location loc = Location.create(dir) ;
-      fn = loc.getPath("xyz", "node") ;
-      FileOps.delete(fn) ;
-      return FileFactory.createStringFileDisk(fn) ;
-    }
-
-    @Override
-    protected void removeStringFile(StringFile f)
-    {
-        f.close() ;
-        FileOps.delete(fn) ;
-    }
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/67b69b5e/dboe-base/src/test/java/org/seaborne/dboe/base/objectfile/TestStringFileMem.java
----------------------------------------------------------------------
diff --git a/dboe-base/src/test/java/org/seaborne/dboe/base/objectfile/TestStringFileMem.java b/dboe-base/src/test/java/org/seaborne/dboe/base/objectfile/TestStringFileMem.java
deleted file mode 100644
index 0098fad..0000000
--- a/dboe-base/src/test/java/org/seaborne/dboe/base/objectfile/TestStringFileMem.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- *  Licensed 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.
- *
- *  See the NOTICE file distributed with this work for additional
- *  information regarding copyright ownership.
- */
-
-package org.seaborne.dboe.base.objectfile;
-
-import org.seaborne.dboe.base.file.FileFactory ;
-import org.seaborne.dboe.base.objectfile.StringFile ;
-
-public class TestStringFileMem extends AbstractTestStringFile
-{
-    
-    @Override
-    protected StringFile createStringFile()
-    {
-        // OLD
-        //return new StringFile(new ObjectFileMem(("mem-test"))) ;
-        // NEW
-        return FileFactory.createStringFileMem("mem-test") ;
-    }
-
-    @Override
-    protected void removeStringFile(StringFile f)
-    {
-        f.close() ;
-    }
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/67b69b5e/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/data/TransBinaryDataFile.java
----------------------------------------------------------------------
diff --git a/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/data/TransBinaryDataFile.java b/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/data/TransBinaryDataFile.java
index b3c1ed0..569db33 100644
--- a/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/data/TransBinaryDataFile.java
+++ b/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/data/TransBinaryDataFile.java
@@ -25,10 +25,9 @@ import org.apache.jena.atlas.io.IO ;
 import org.apache.jena.query.ReadWrite ;
 import org.seaborne.dboe.base.file.BinaryDataFile ;
 import org.seaborne.dboe.base.file.BufferChannel ;
-import org.seaborne.dboe.base.objectfile.ObjectFile ;
 import org.seaborne.dboe.transaction.txn.* ;
 
-/** Transactional {@link ObjectFile}.
+/** Transactional {@link BinaryDataFile}.
  *  An binary file that is append-only and allows only one writer at a time.
  *  All readers see the file up to the last commit point at the time 
  *  they started.  The sole writer sees more of the file.

http://git-wip-us.apache.org/repos/asf/jena/blob/67b69b5e/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/data/TransObjectFile.java
----------------------------------------------------------------------
diff --git a/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/data/TransObjectFile.java b/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/data/TransObjectFile.java
deleted file mode 100644
index ce2c678..0000000
--- a/dboe-trans-data/src/main/java/org/seaborne/dboe/trans/data/TransObjectFile.java
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- *  Licensed 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.
- *
- *  See the NOTICE file distributed with this work for additional
- *  information regarding copyright ownership.
- */
-
-package org.seaborne.dboe.trans.data;
-
-import java.nio.ByteBuffer ;
-import java.util.Iterator ;
-import java.util.concurrent.atomic.AtomicLong ;
-
-import org.apache.jena.atlas.lib.Pair ;
-import org.apache.jena.query.ReadWrite ;
-import org.seaborne.dboe.base.block.Block ;
-import org.seaborne.dboe.base.file.BufferChannel ;
-import org.seaborne.dboe.base.objectfile.ObjectFile ;
-import org.seaborne.dboe.transaction.txn.ComponentId ;
-import org.seaborne.dboe.transaction.txn.StateMgrData ;
-import org.seaborne.dboe.transaction.txn.TransactionalComponentLifecycle ;
-import org.seaborne.dboe.transaction.txn.TxnId ;
-
-/** Transactional {@link ObjectFile}.
- *  An object file is append-only and allows only one writer at a time.
- *  As a result, all readers see the file up to the last commit point at the time 
- *  they started.  The sole writer sees more of the file.
- */
-
-public class TransObjectFile extends TransactionalComponentLifecycle<TransObjectFile.TxnObjectFile>
-    implements ObjectFile {
-
-    /*
-     * The file is written to as we go along so abort requires some action. We
-     * can't recover from just the file, without any redo or undo recovery
-     * action. The length/position of the file may be duff, there is a possible
-     * abandoned section of the file.
-     * 
-     * But even if a partial entry, we don't corrupt data. Assumes no references
-     * to the abandoned area of the file.
-     */
-    
-    private final ObjectFileState stateMgr ;
-
-    // The current committed position.
-    // This is also the abort point.
-    // And the limit as seen by readers.
-    
-    // Recovery record: (length, position)
-    private final AtomicLong length ;
-    private final AtomicLong position ; 
-    
-    static class TxnObjectFile {
-        final long length ; 
-        final long position ; 
-
-        TxnObjectFile(long length, long position) {
-            this.length = length ;
-            this.position = position ;
-        }
-    }
-
-    static class ObjectFileState extends StateMgrData {
-        ObjectFileState(BufferChannel bufferChannel, long length, long position) {
-            super(bufferChannel, length, position) ;
-        }
-        private static int idxLength = 0 ; 
-        private static int idxPosition = 1 ;
-        long length()               { return get(idxLength) ; }
-        long position()             { return get(idxPosition) ; }
-        void length(long len)       { set(idxLength, len) ; } 
-        void position(long posn)    { set(idxPosition, posn) ; }
-    }
-
-    private final ObjectFile objFile ;
-    
-    public TransObjectFile(ObjectFile objFile, ComponentId cid, BufferChannel bufferChannel) {
-        super(cid) ;
-        stateMgr = new ObjectFileState(bufferChannel, 0L, 0L) ;
-        this.objFile = objFile ;
-        
-        // These may be updated by recovery. Start by setting to the
-        // "clean start" settings.
-        
-        length   = new AtomicLong(objFile.length()) ;
-        position = new AtomicLong(objFile.position()) ;
-    }
-    
-    private boolean recoveryAction = false ; 
-
-    @Override
-    public void startRecovery() {
-        recoveryAction = false ;
-    }
-    
-    @Override
-    public void recover(ByteBuffer ref) {
-        stateMgr.setState(ref);
-        length.set(stateMgr.length()) ;
-        position.set(stateMgr.position()) ;
-        recoveryAction = true ;
-    }
-
-    @Override
-    public void finishRecovery() {
-        // If we did a truncate.
-//        if ( recoveryAction )
-//            objFile.sync();
-    }
-    
-    @Override
-    public void cleanStart() { }
-
-    @Override
-    protected TxnObjectFile _begin(ReadWrite readWrite, TxnId txnId) {
-        return createState();
-    }
-    
-    private TxnObjectFile createState() {
-        // Atomic read across the two because it's called from within 
-        // TransactionCoordinator.begin$ or promote$ where there is a lock.
-        long xLength = length.get() ;
-        long xPosition = position.get() ;
-        return new TxnObjectFile(xLength, xPosition) ;
-    }
-    
-    @Override
-    protected TxnObjectFile _promote(TxnId txnId, TxnObjectFile state) {
-        // Our write state is the read state.
-        return createState();
-    }
-
-    @Override
-    protected ByteBuffer _commitPrepare(TxnId txnId, TxnObjectFile state) {
-        stateMgr.length(objFile.length()) ;
-        stateMgr.position(objFile.position());  
-        return stateMgr.getState() ;
-    }
-
-    @Override
-    protected void _commit(TxnId txnId, TxnObjectFile state) {
-        if ( isWriteTxn() ) {
-            // Force to disk.
-            objFile.sync();
-            stateMgr.writeState();
-            // Move visible commit point forward.
-            length.set(objFile.length()) ;
-            position.set(objFile.position()) ;
-        }
-    }
-
-    @Override
-    protected void _commitEnd(TxnId txnId, TxnObjectFile state) {
-    }
-
-    @Override
-    protected void _abort(TxnId txnId, TxnObjectFile state) {
-        if ( isWriteTxn() ) {
-            // One will imply the other.
-            objFile.truncate(state.length) ;
-            //objFile.reposition(state.position) ;
-            // sync to make sure the file looses the unwanted part.
-            // Neater, no abandoned sections (outside crashes).
-            objFile.sync() ;
-        }
-    }
-
-    @Override
-    protected void _complete(TxnId txnId, TxnObjectFile state) {}
-
-    @Override
-    protected void _shutdown() {}
-    
-    private void checkBoundsReader(long requestedPoint, TxnObjectFile state) { }
-    
-    // The Object file part, with transaction testing.
-    @Override
-    public Block allocWrite(int maxBytes) {
-        checkWriteTxn();
-        return objFile.allocWrite(maxBytes) ;
-    }
-
-    @Override
-    public void completeWrite(Block buffer) {
-        checkWriteTxn();
-        objFile.completeWrite(buffer) ;
-    }
-
-    @Override
-    public void abortWrite(Block buffer) {
-        checkWriteTxn();
-        objFile.abortWrite(buffer) ;
-    }
-
-    @Override
-    public long write(ByteBuffer buffer) {
-        checkWriteTxn();
-        return objFile.write(buffer) ;
-    }
-
-    @Override
-    public void reposition(long id) {
-        checkWriteTxn();
-        objFile.reposition(id) ;
-    }
-
-    @Override
-    public void truncate(long size) {
-        checkWriteTxn();
-        objFile.truncate(size) ;
-    }
-
-    @Override
-    public ByteBuffer read(long id) {
-        super.checkTxn();
-        if ( isReadTxn() )
-            // Reader. Check bounds.
-            checkBoundsReader(id, super.getDataState()) ;
-        return objFile.read(id) ;
-    }
-
-    @Override
-    public String getLabel() {
-        return objFile.getLabel() ;
-    }
-
-    @Override
-    public Iterator<Pair<Long, ByteBuffer>> all() {
-        super.checkTxn();
-        return objFile.all() ;
-    }
-
-    @Override
-    public void sync() {
-        super.checkTxn();
-        objFile.sync() ;
-    }
-
-    @Override
-    public void close() {
-        super.checkTxn();
-        objFile.close() ;
-    }
-
-    @Override
-    public long length() {
-        super.checkTxn();
-        if ( isReadTxn() )
-            return getDataState().length ;
-            // Reader. Check bounds.
-        return objFile.length() ;
-    }
-
-    @Override
-    public long position() {
-        super.checkTxn();
-        return objFile.position() ;
-    }
-
-    @Override
-    public boolean isEmpty() {
-        super.checkTxn();
-        return objFile.isEmpty() ;
-    }    
-}
-

http://git-wip-us.apache.org/repos/asf/jena/blob/67b69b5e/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/data/TS_TransData.java
----------------------------------------------------------------------
diff --git a/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/data/TS_TransData.java b/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/data/TS_TransData.java
index 0d4c092..d310e6b 100644
--- a/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/data/TS_TransData.java
+++ b/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/data/TS_TransData.java
@@ -23,8 +23,7 @@ import org.junit.runners.Suite.SuiteClasses ;
 
 @RunWith(Suite.class)
 @SuiteClasses( {
-    TestTransObjectFile.class
-    , TestTransIndexMem.class
+    TestTransIndexMem.class
     , TestTransBlob.class
     , TestTransBlobPersistent.class
     , TestTransBinaryDataFileGeneral.class

http://git-wip-us.apache.org/repos/asf/jena/blob/67b69b5e/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/data/TestTransObjectFile.java
----------------------------------------------------------------------
diff --git a/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/data/TestTransObjectFile.java b/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/data/TestTransObjectFile.java
deleted file mode 100644
index c2d0330..0000000
--- a/dboe-trans-data/src/test/java/org/seaborne/dboe/trans/data/TestTransObjectFile.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- *  Licensed 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.
- *
- *  See the NOTICE file distributed with this work for additional
- *  information regarding copyright ownership.
- */
-
-package org.seaborne.dboe.trans.data;
-
-import java.nio.ByteBuffer ;
-
-import org.apache.jena.atlas.lib.Bytes ;
-import org.apache.jena.atlas.lib.StrUtils ;
-import org.junit.After ;
-import org.junit.Assert ;
-import org.junit.Before ;
-import org.junit.Test ;
-import org.seaborne.dboe.base.file.BufferChannel ;
-import org.seaborne.dboe.base.file.FileFactory ;
-import org.seaborne.dboe.base.file.Location ;
-import org.seaborne.dboe.base.objectfile.ObjectFile ;
-import org.seaborne.dboe.jenax.Txn ;
-import org.seaborne.dboe.transaction.Transactional ;
-import org.seaborne.dboe.transaction.TransactionalFactory ;
-import org.seaborne.dboe.transaction.txn.ComponentId ;
-import org.seaborne.dboe.transaction.txn.TransactionalBase ;
-import org.seaborne.dboe.transaction.txn.journal.Journal ;
-import org.seaborne.dboe.transaction.txn.journal.JournalEntry ;
-import org.seaborne.dboe.transaction.txn.journal.JournalEntryType ;
-
-public class TestTransObjectFile extends Assert {
-    private Journal journal ;
-    private ObjectFile baseObjectFile ;
-    private TransObjectFile transObjectFile ;
-    private Transactional transactional ;
-    
-    @Before public void before() {
-        journal = Journal.create(Location.mem()) ;
-        baseObjectFile = FileFactory.createObjectFileMem("ObjectFile") ;
-        BufferChannel chan = FileFactory.createBufferChannelMem() ;
-        ComponentId cid = ComponentId.allocLocal() ;
-        transObjectFile = new TransObjectFile(baseObjectFile, cid, chan) ;
-        transactional = TransactionalFactory.createTransactional(journal, transObjectFile) ;    
-    }
-
-    @After public void after() { }
-    
-    private static ByteBuffer str2bb(String x) {
-        byte[] d = StrUtils.asUTF8bytes(x) ;
-        ByteBuffer bb = ByteBuffer.wrap(d) ;
-        return bb ;
-    }
-    
-    private static long writeOne(Transactional transactional, TransObjectFile transObjectFile, String data) {
-        return 
-            Txn.calculateWrite(transactional, ()->{
-                ByteBuffer bb = str2bb(data) ;
-                return transObjectFile.write(bb) ;
-        }) ;
-    }
-    
-    private static String readOne(Transactional transactional, TransObjectFile transObjectFile, long posn) {
-        return Txn.calculateRead(transactional, ()->{
-            ByteBuffer bb = transObjectFile.read(posn) ;
-            return Bytes.fromByteBuffer(bb) ;
-        }) ;
-    }
-    
-    @Test public void transObjectFile_1() {
-        String str = "Hello World" ; 
-        long x = writeOne(transactional, transObjectFile, str) ;
-        String str2 = readOne(transactional, transObjectFile, x) ;
-        assertEquals(str, str2) ;
-    }
-
-    @Test public void transObjectFile_2() {
-        String str1 = "" ; 
-        String str2 = "$" ; 
-        long x1 = writeOne(transactional, transObjectFile, str1) ;
-        long x2 = writeOne(transactional, transObjectFile, str2) ;
-        assertNotEquals(x1, x2) ;
-        
-        String out2 = readOne(transactional, transObjectFile, x2) ;
-        String out1 = readOne(transactional, transObjectFile, x1) ;
-        assertEquals(str1, out1) ;
-        assertEquals(str2, out2) ;
-    }
-    
-    @Test public void transObjectFile_3() {
-        String str = "Hello World" ; 
-        long x = writeOne(transactional, transObjectFile, str) ;
-        // Assume journal not truncated.
-    }
-
-    @Test public void transObjectFile_4() {
-        String str1 = "Test4" ; 
-        String str2 = "TheNext" ;
-        long x1 = writeOne(transactional, transObjectFile, str1) ;
-        ByteBuffer bb = str2bb(str2) ;
-        long x2 = baseObjectFile.write(bb) ;
-        baseObjectFile.sync();
-        long x3 = Txn.calculateRead(transactional, ()->transObjectFile.length()) ;
-        assertEquals(x2, x3);
-        assertNotEquals(x3, baseObjectFile.length());
-
-        // Fake recovery.
-        ByteBuffer bbj = ByteBuffer.allocate(2*Long.BYTES) ;
-        bbj.putLong(x3) ;
-        bbj.putLong(0) ;
-        bbj.rewind() ;
-        journal.write(JournalEntryType.REDO, transObjectFile.getComponentId(), bbj) ;
-        journal.writeJournal(JournalEntry.COMMIT) ;
-        // Recovery.
-        //transObjectFile.recover(bb);
-        TransactionalBase transBase = (TransactionalBase)TransactionalFactory.createTransactional(journal, transObjectFile) ;
-        ByteBuffer bb1 = Txn.calculateRead(transBase, ()->transObjectFile.read(x3)) ;
-        String s1 = Bytes.fromByteBuffer(bb1) ;
-        assertEquals(str2, s1);
-        // Woot!
-        transBase.getTxnMgr().shutdown();
-    }
-}
-


[27/50] [abbrv] jena git commit: JENA-1396: Merge //github/afs/mantis as subdirectory jena-db/

Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/lib/TS_LibTDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/lib/TS_LibTDB.java
index 0000000,0000000..df245fb
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/lib/TS_LibTDB.java
@@@ -1,0 -1,0 +1,33 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.lib;
++
++import org.junit.runner.RunWith;
++import org.junit.runners.Suite;
++import org.seaborne.tdb2.lib.TestNodeLib ;
++
++@RunWith(Suite.class)
++@Suite.SuiteClasses( {
++    TestNodeLib.class
++})
++
++
++public class TS_LibTDB
++{
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/lib/TestNodeLib.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/lib/TestNodeLib.java
index 0000000,0000000..3e85f41
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/lib/TestNodeLib.java
@@@ -1,0 -1,0 +1,69 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.lib;
++
++import static org.seaborne.tdb2.lib.NodeLib.hash ;
++import static org.junit.Assert.*;
++import org.apache.jena.graph.Node ;
++import org.apache.jena.sparql.util.NodeFactoryExtra ;
++import org.junit.Test ;
++
++public class TestNodeLib
++{
++    // Tests of TDBs NodeLib
++    @Test public void hash1() 
++    {
++        Node x1 = NodeFactoryExtra.parseNode("<http://example/x>") ;
++        Node x2 = NodeFactoryExtra.parseNode("<http://example/x>") ;
++        assertEquals(hash(x1), hash(x2)) ;
++    }
++    
++    @Test public void hash2() 
++    {
++        Node x1 = NodeFactoryExtra.parseNode("<http://example/x1>") ;
++        Node x2 = NodeFactoryExtra.parseNode("<http://example/x2>") ;
++        assertNotEquals(hash(x1), hash(x2)) ;
++    }
++    
++    @Test public void hash3() 
++    {
++        Node x1 = NodeFactoryExtra.parseNode("<lex>") ;
++        Node x2 = NodeFactoryExtra.parseNode("'lex'") ;
++        Node x3 = NodeFactoryExtra.parseNode("_:lex") ;
++        assertNotEquals(hash(x1), hash(x2)) ;
++        assertNotEquals(hash(x2), hash(x3)) ;
++        assertNotEquals(hash(x3), hash(x1)) ;
++    }
++    
++    @Test public void hash4() 
++    {
++        Node x1 = NodeFactoryExtra.parseNode("123") ;
++        Node x2 = NodeFactoryExtra.parseNode("'123'") ;
++        assertNotEquals(hash(x1), hash(x2)) ;
++    }
++
++    @Test public void hash5() 
++    {
++        Node x1 = NodeFactoryExtra.parseNode("123") ;
++        Node x2 = NodeFactoryExtra.parseNode("123.0") ;
++        Node x3 = NodeFactoryExtra.parseNode("123e0") ;
++        assertNotEquals(hash(x1), hash(x2)) ;
++        assertNotEquals(hash(x2), hash(x3)) ;
++        assertNotEquals(hash(x3), hash(x1)) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/setup/TS_TDBSetup.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/setup/TS_TDBSetup.java
index 0000000,0000000..8f99097
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/setup/TS_TDBSetup.java
@@@ -1,0 -1,0 +1,32 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.setup;
++
++import org.junit.runner.RunWith ;
++import org.junit.runners.Suite ;
++
++@RunWith(Suite.class)
++@Suite.SuiteClasses( {
++    TestStoreParams.class
++    , TestStoreParamsChoose.class
++    , TestStoreParamsCreate.class
++})
++public class TS_TDBSetup {
++    
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParams.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParams.java
index 0000000,0000000..0d8468c
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParams.java
@@@ -1,0 -1,0 +1,144 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.setup;
++
++import org.apache.jena.atlas.json.JSON ;
++import org.apache.jena.atlas.json.JsonObject ;
++import static org.junit.Assert.*;
++import org.junit.Test ;
++import org.seaborne.dboe.base.block.FileMode ;
++import org.seaborne.tdb2.TDBException ;
++import org.seaborne.tdb2.setup.StoreParams ;
++import org.seaborne.tdb2.setup.StoreParamsBuilder ;
++import org.seaborne.tdb2.setup.StoreParamsCodec ;
++
++public class TestStoreParams {
++
++    @Test public void store_params_01() {
++        assertEqualsStoreParams(StoreParams.getDftStoreParams(), StoreParams.getDftStoreParams()) ; 
++    }
++    
++    @Test public void store_params_02() {
++        StoreParams input = StoreParams.getDftStoreParams() ;
++        StoreParams sp = StoreParams.builder(input).build() ;
++        assertEqualsStoreParams(StoreParams.getDftStoreParams(), sp) ; 
++    }
++
++    @Test public void store_params_03() {
++        StoreParams sp = StoreParams.builder().build() ;
++        assertEqualsStoreParams(StoreParams.getDftStoreParams(), sp) ; 
++    }
++
++    @Test public void store_params_04() {
++        StoreParams params = StoreParams.builder().build() ;
++        StoreParams params2 = roundTrip(params) ;
++        assertEqualsStoreParams(params,params2) ;
++    }
++    
++    // ----
++    
++    @Test public void store_params_10() {
++        StoreParams params = StoreParams.builder().fileMode(FileMode.direct).blockSize(1024).build() ;
++        StoreParams params2 = roundTrip(params) ;
++        assertEqualsStoreParams(params,params2) ;
++        assertEquals(params.getFileMode(), params2.getFileMode()) ;
++        assertEquals(params.getBlockSize(), params2.getBlockSize()) ;
++    }
++
++    @Test public void store_params_11() {
++        String xs = "{ \"tdb.block_size\": 2048 }" ;
++        JsonObject x = JSON.parse(xs) ;
++        StoreParams paramsExpected = StoreParams.builder().blockSize(2048).build() ;
++        StoreParams paramsActual = StoreParamsCodec.decode(x) ;
++        assertEqualsStoreParams(paramsExpected,paramsActual) ;
++    }
++
++    @Test public void store_params_12() {
++        String xs = "{ \"tdb.file_mode\": \"direct\" , \"tdb.block_size\": 2048 }" ;
++        JsonObject x = JSON.parse(xs) ;
++        StoreParams paramsExpected = StoreParams.builder().blockSize(2048).fileMode(FileMode.direct).build() ;
++        StoreParams paramsActual = StoreParamsCodec.decode(x) ;
++        assertEqualsStoreParams(paramsExpected,paramsActual) ;
++    }
++
++    @Test public void store_params_13() {
++        String xs = "{ \"tdb.triple_indexes\" : [ \"POS\" , \"PSO\"] } " ; 
++        JsonObject x = JSON.parse(xs) ;
++        StoreParams params = StoreParamsCodec.decode(x) ;
++        String[] expected =  { "POS" , "PSO" } ;
++        assertArrayEquals(expected, params.getTripleIndexes()) ;
++    }
++
++    @Test(expected=TDBException.class)
++    public void store_params_14() {
++        String xs = "{ \"tdb.triples_indexes\" : [ \"POS\" , \"PSO\"] } " ; // Misspelt. 
++        JsonObject x = JSON.parse(xs) ;
++        StoreParams params = StoreParamsCodec.decode(x) ;
++        String[] expected =  { "POS" , "PSO" } ;
++        assertArrayEquals(expected, params.getTripleIndexes()) ;
++    }
++
++    // Check that setting gets recorded and propagated.
++
++    @Test public void store_params_20() {
++        StoreParams params = StoreParams.builder().blockReadCacheSize(0).build();
++        assertTrue(params.isSetBlockReadCacheSize()) ;
++        assertFalse(params.isSetBlockWriteCacheSize()) ;
++    }
++    
++    @Test public void store_params_21() {
++        StoreParams params1 = StoreParams.builder().blockReadCacheSize(0).build();
++        assertTrue(params1.isSetBlockReadCacheSize()) ;
++        assertFalse(params1.isSetBlockWriteCacheSize()) ;
++        StoreParams params2 = StoreParams.builder(params1).blockWriteCacheSize(0).build();
++        assertTrue(params2.isSetBlockReadCacheSize()) ;
++        assertTrue(params2.isSetBlockWriteCacheSize()) ;
++        assertFalse(params2.isSetNodeMissCacheSize()) ;
++    }
++
++    // Modify
++    @Test public void store_params_22() {
++        StoreParams params1 = StoreParams.builder()
++            .blockReadCacheSize(0)
++            .blockWriteCacheSize(1)
++            .build();
++        StoreParams params2 = StoreParams.builder()
++            .blockReadCacheSize(5)
++            .build();
++        StoreParams params3 = StoreParamsBuilder.modify(params1, params2) ;
++        assertFalse(params2.isSetBlockWriteCacheSize()) ;
++        assertTrue(params3.isSetBlockReadCacheSize()) ;
++        assertTrue(params3.isSetBlockWriteCacheSize()) ;
++        assertEquals(5, params3.getBlockReadCacheSize().intValue()) ;   // From params2
++        assertEquals(1, params3.getBlockWriteCacheSize().intValue()) ;  // From params1, not params2(unset)
++        
++    }
++
++    
++    // --------
++    
++    private static StoreParams roundTrip(StoreParams params) {
++        JsonObject obj = StoreParamsCodec.encodeToJson(params) ;
++        StoreParams params2 = StoreParamsCodec.decode(obj) ;
++        return params2 ;
++    }
++    
++    private static void assertEqualsStoreParams(StoreParams params1, StoreParams params2) {
++        assertTrue(StoreParams.sameValues(params1, params2)) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParamsChoose.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParamsChoose.java
index 0000000,0000000..2b20d54
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParamsChoose.java
@@@ -1,0 -1,0 +1,157 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.setup;
++
++import static org.junit.Assert.*;
++import org.apache.jena.atlas.lib.FileOps ;
++import org.junit.Test ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.dboe.sys.Names;
++import org.seaborne.tdb2.ConfigTest ;
++
++//TestParamsCreate
++/** This test suite uses on-disk structures and can be slow */ 
++public class TestStoreParamsChoose {
++    private String DIR = ConfigTest.getCleanDir() ;
++    
++    static final StoreParams pApp = StoreParams.builder()
++        .blockSize(12)              // Not dynamic
++        .nodeMissCacheSize(12)      // Dynamic
++        .build();
++    static final StoreParams pLoc = StoreParams.builder()
++        .blockSize(0)
++        .nodeMissCacheSize(0).build();
++    
++    static final StoreParams pDft = StoreParams.getDftStoreParams() ;
++
++    @Test public void params_choose_new_1() {
++        StoreParams p = StoreParamsFactory.decideStoreParams(Location.mem(), true, null, null, pDft) ;
++        // New store, no pLoc, no pApp so pDft.
++        assertTrue(StoreParams.sameValues(p, pDft)) ;
++    }
++    
++    @Test public void params_choose_new_2() {
++        StoreParams p = StoreParamsFactory.decideStoreParams(Location.mem(), true, pApp, null, pDft) ;
++        // New store, no pLoc, so pApp is the enire settings.
++        assertEquals(12, p.getBlockSize().intValue()) ;
++        assertTrue(StoreParams.sameValues(p, pApp)) ;
++    }
++
++    @Test public void params_choose_new_3() {
++        StoreParams p = StoreParamsFactory.decideStoreParams(Location.mem(), true, null, pLoc, pDft) ;
++        // New store, pLoc, no pApp, so pLoc is the entire settings.
++        assertEquals(0, p.getBlockSize().intValue()) ;
++        assertTrue(StoreParams.sameValues(p, pLoc)) ;
++    }
++
++    @Test public void params_choose_new_4() {
++        StoreParams p = StoreParamsFactory.decideStoreParams(Location.mem(), true, pApp, pLoc, pDft) ;
++        // New store, pLoc, no pApp, so pLoc is the entire settings.
++        
++        assertFalse(StoreParams.sameValues(p, pApp)) ;
++        assertFalse(StoreParams.sameValues(p, pLoc)) ;
++        assertFalse(StoreParams.sameValues(p, pDft)) ;
++        
++        assertEquals(0, p.getBlockSize().intValue()) ;
++        assertEquals(12,  p.getNodeMissCacheSize().intValue()) ;
++    }
++
++    @Test public void params_choose_existing_1() {
++        StoreParams p = StoreParamsFactory.decideStoreParams(Location.mem(), false, null, null, pDft) ;
++        // p is pDft.
++        assertTrue(StoreParams.sameValues(p, pDft)) ;
++    }
++
++    @Test public void params_choose_existing_2() {
++        StoreParams p = StoreParamsFactory.decideStoreParams(Location.mem(), false, pApp, null, pDft) ;
++        // p is pLoc modified by pApp
++        assertFalse(StoreParams.sameValues(p, pApp)) ;
++        assertFalse(StoreParams.sameValues(p, pDft)) ;
++        // Existing store, no pLoc, so pDft is implicit pLoc and fixed the block size.  
++        assertEquals(pDft.getBlockSize(), p.getBlockSize()) ;
++        assertEquals(12, p.getNodeMissCacheSize().intValue()) ;
++    }
++    
++    @Test public void params_choose_existing_3() {
++        StoreParams p = StoreParamsFactory.decideStoreParams(Location.mem(), false, null, pLoc, pDft) ;
++        // p is pLoc
++        assertTrue(StoreParams.sameValues(p, pLoc)) ;
++        
++    }
++
++    @Test public void params_choose_existing_4() {
++        StoreParams p = StoreParamsFactory.decideStoreParams(Location.mem(), false, pApp, pLoc, pDft) ;
++        // p is pLoc modifed by pApp.
++        assertFalse(StoreParams.sameValues(p, pApp)) ;
++        assertFalse(StoreParams.sameValues(p, pLoc)) ;
++        assertFalse(StoreParams.sameValues(p, pDft)) ;
++        
++        assertEquals(0, p.getBlockSize().intValue()) ;
++        assertEquals(12,  p.getNodeMissCacheSize().intValue()) ;
++    }
++    
++    @Test public void params_choose_new_persist_1() {
++        // new database, app defined.
++        Location loc = Location.create(DIR) ;
++        FileOps.clearAll(loc.getDirectoryPath());
++        // Clear.
++        StoreParams p = StoreParamsFactory.decideStoreParams(loc, true, pApp, null, pDft) ;
++        // Check location now has a pLoc.
++        String fn = loc.getPath(Names.TDB_CONFIG_FILE) ;
++        assertTrue(FileOps.exists(fn)) ;
++
++        StoreParams pLoc2 = StoreParamsCodec.read(loc) ;
++        assertTrue(StoreParams.sameValues(pLoc2, p)) ;
++    }
++    
++    @Test public void params_choose_new_persist_2() {
++        // new database, location defined.
++        Location loc = Location.create(DIR) ;
++        FileOps.clearAll(loc.getDirectoryPath());
++        StoreParamsCodec.write(loc, pLoc); 
++        
++        // Clear.
++        StoreParams p = StoreParamsFactory.decideStoreParams(loc, true, null, pLoc, pDft) ;
++        // Check location still has a pLoc.
++        String fn = loc.getPath(Names.TDB_CONFIG_FILE) ;
++        assertTrue(FileOps.exists(fn)) ;
++
++        StoreParams pLoc2 = StoreParamsCodec.read(loc) ;
++        assertTrue(StoreParams.sameValues(pLoc, p)) ;
++    }
++
++    @Test public void params_choose_new_persist_3() {
++        // new database, location defined, application modified.
++        Location loc = Location.create(DIR) ;
++        FileOps.clearAll(loc.getDirectoryPath());
++        StoreParamsCodec.write(loc, pLoc); 
++        
++        // Clear.
++        StoreParams p = StoreParamsFactory.decideStoreParams(loc, true, pApp, pLoc, pDft) ;
++        // Check location still has a pLoc.
++        String fn = loc.getPath(Names.TDB_CONFIG_FILE) ;
++        assertTrue(FileOps.exists(fn)) ;
++
++        StoreParams pLoc2 = StoreParamsCodec.read(loc) ;
++        assertFalse(StoreParams.sameValues(pLoc, p)) ;
++        assertEquals(0, p.getBlockSize().intValue()) ;  // Location
++        assertEquals(12, p.getNodeMissCacheSize().intValue()) ;  // Application
++    }
++
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParamsCreate.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParamsCreate.java
index 0000000,0000000..071b135
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParamsCreate.java
@@@ -1,0 -1,0 +1,180 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.setup;
++
++import static org.junit.Assert.assertEquals ;
++import static org.junit.Assert.assertFalse ;
++import static org.junit.Assert.assertNotEquals ;
++import static org.junit.Assert.assertNotNull ;
++import static org.junit.Assert.assertNull ;
++import static org.junit.Assert.assertTrue ;
++
++import java.nio.file.Files ;
++import java.nio.file.Path ;
++import java.nio.file.Paths ;
++
++import org.apache.jena.atlas.json.JSON ;
++import org.apache.jena.atlas.json.JsonObject ;
++import org.apache.jena.atlas.lib.FileOps ;
++import org.junit.After ;
++import org.junit.Before ;
++import org.junit.Test ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.dboe.sys.Names;
++import org.seaborne.tdb2.ConfigTest ;
++import org.seaborne.tdb2.sys.StoreConnection ;
++import org.seaborne.tdb2.sys.TDBInternal ;
++
++/**
++ * This test suite uses on-disk structures, does a lot of clean/create/sync
++ * calls and can be noticably slow.
++ */
++public class TestStoreParamsCreate {
++    // Thse tests work on storage locations.
++    private final String DB_DIR = ConfigTest.getCleanDir() ;
++    private final Path db = Paths.get(DB_DIR) ;
++    private final Path cfg = Paths.get(DB_DIR, Names.TDB_CONFIG_FILE) ;
++    private final Location loc = Location.create(DB_DIR) ;
++    
++    static final StoreParams pApp = StoreParams.getSmallStoreParams() ; 
++    static final StoreParams pDft = StoreParams.getDftStoreParams() ;
++    static final StoreParams pSpecial = StoreParams.builder(pApp)
++        .blockSize(1024)
++        .blockReadCacheSize(4)
++        .build();
++    
++    private void expel() {
++        StoreConnection.internalExpel(loc, true);
++    }
++    
++    @Before public void clearupTest() { 
++        // Flush and clean.
++        TDBInternal.reset();
++        FileOps.clearAll(loc.getDirectoryPath());
++    }
++
++    @After public void expelDatabase() { 
++        expel();
++    }
++    
++    @Test public void params_create_01() {
++        StoreConnection.connectCreate(loc) ;
++        // Check.  Default setup, no params.
++        assertTrue("DB directory", Files.exists(db)) ;
++        assertFalse("Config file unexpectedly found", Files.exists(cfg)) ;
++    }
++    
++    @Test public void params_create_02() {
++        StoreConnection.connectCreate(loc, pApp) ;
++        // Check.  Custom setup.
++        assertTrue("DB directory", Files.exists(db)) ;
++        assertTrue("Config file not found", Files.exists(cfg)) ;
++        StoreParams pLoc = StoreParamsCodec.read(loc) ;
++        assertTrue(StoreParams.sameValues(pLoc, pApp)) ;
++    }
++    
++    // Defaults
++    @Test public void params_reconnect_01() { 
++        // Create.
++        StoreConnection.connectCreate(loc) ;
++        // Drop.
++        expel();
++        // Reconnect
++        StoreConnection.connectCreate(loc, null) ;
++        StoreParams pLoc = StoreParamsCodec.read(loc) ;
++        assertNull(pLoc) ;
++        
++        StoreParams pDB = StoreConnection.connectExisting(loc).getDatasetGraphTDB().getStoreParams() ;
++        assertNotNull(pDB) ;
++        // Should be the default setup.
++        assertTrue(StoreParams.sameValues(pDft, pDB)) ;
++    }
++    
++    // Defaults, then reconnect with app modified.
++    @Test public void params_reconnect_02() { 
++        // Create.
++        StoreConnection.connectCreate(loc, null) ;
++        // Drop.
++        expel();
++        // Reconnect
++        StoreConnection.connectCreate(loc, pSpecial) ;
++        //StoreParams pLoc = StoreParamsCodec.read(loc) ;
++        //assertNotNull(pLoc) ;
++        
++        StoreParams pDB = StoreConnection.connectExisting(loc).getDatasetGraphTDB().getStoreParams() ;
++        assertNotNull(pDB) ;
++        // Should be the default setup, modified by pApp for cache sizes.
++        assertFalse(StoreParams.sameValues(pDft, pDB)) ;
++        assertFalse(StoreParams.sameValues(pSpecial, pDB)) ;
++
++        // Check it's default-modified-by-special.
++        assertEquals(pSpecial.getBlockReadCacheSize(), pDB.getBlockReadCacheSize()) ;
++        assertNotEquals(pDft.getBlockReadCacheSize(), pDB.getBlockReadCacheSize()) ;
++        
++        assertNotEquals(pSpecial.getBlockSize(), pDB.getBlockSize()) ;
++        assertEquals(pDft.getBlockSize(), pDB.getBlockSize()) ;
++    }
++    
++    // Custom, then reconnect with some special settings.
++    @Test public void params_reconnect_03() { 
++        // Create.
++        StoreConnection.connectCreate(loc, pApp) ;
++        // Drop.
++        expel();
++        // Reconnect
++        StoreConnection.connectCreate(loc, pSpecial) ;
++        //StoreParams pLoc = StoreParamsCodec.read(loc) ;
++        //assertNotNull(pLoc) ;
++        
++        StoreParams pDB = StoreConnection.connectExisting(loc).getDatasetGraphTDB().getStoreParams() ;
++        assertNotNull(pDB) ;
++        // Should be the default setup, modified by pApp for cache sizes.
++        assertFalse(StoreParams.sameValues(pApp, pDB)) ;
++        assertFalse(StoreParams.sameValues(pSpecial, pDB)) ;
++
++        // Check it's default-modified-by-special.
++        assertEquals(pSpecial.getBlockReadCacheSize(), pDB.getBlockReadCacheSize()) ;
++        assertNotEquals(pApp.getBlockReadCacheSize(), pDB.getBlockReadCacheSize()) ;
++        
++        assertNotEquals(pSpecial.getBlockSize(), pDB.getBlockSize()) ;
++        assertEquals(pApp.getBlockSize(), pDB.getBlockSize()) ;
++    }
++
++    
++//    // Custom then modified.
++//    @Test public void params_reconnect_03() { 
++//        // Create.
++//        StoreConnection.connectCreate(loc, pLoc) ;
++//        // Drop.
++//        StoreConnection.expel(loc, true) ;
++//        // Reconnect
++//        StoreConnection.connectCreate(loc, pApp) ;
++//        StoreParams pLoc = StoreParamsCodec.read(loc) ;
++//        assertFalse(StoreParams.sameValues(pApp, pLoc)) ;
++//        assertFalse(StoreParams.sameValues(pApp, pLoc)) ;
++//    }
++
++    // Dataset tests
++
++    static StoreParams read(Location location) {
++        String fn = location.getPath(Names.TDB_CONFIG_FILE) ;
++        JsonObject obj = JSON.read(fn) ;
++        return StoreParamsCodec.decode(obj) ;
++    }
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/solver/TS_SolverTDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/solver/TS_SolverTDB.java
index 0000000,0000000..f52d8ad
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/solver/TS_SolverTDB.java
@@@ -1,0 -1,0 +1,30 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.solver;
++
++import org.junit.runner.RunWith ;
++import org.junit.runners.Suite ;
++
++@RunWith(Suite.class)
++@Suite.SuiteClasses( {
++    TestSolverTDB.class     // Tests the TDB connectivity
++    , TestStats.class
++})
++
++public class TS_SolverTDB
++{}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/solver/TestSolverTDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/solver/TestSolverTDB.java
index 0000000,0000000..43ef56d
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/solver/TestSolverTDB.java
@@@ -1,0 -1,0 +1,171 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.solver;
++
++
++import java.util.ArrayList ;
++import java.util.Iterator ;
++import java.util.List ;
++
++import static org.junit.Assert.*;
++import org.apache.jena.graph.Graph ;
++import org.apache.jena.graph.Node ;
++import org.apache.jena.graph.Triple ;
++import org.apache.jena.query.* ;
++import org.apache.jena.riot.RDFDataMgr ;
++import org.apache.jena.shared.PrefixMapping ;
++import org.apache.jena.shared.impl.PrefixMappingImpl ;
++import org.apache.jena.sparql.algebra.Algebra ;
++import org.apache.jena.sparql.algebra.Op ;
++import org.apache.jena.sparql.algebra.OpVars ;
++import org.apache.jena.sparql.core.Var ;
++import org.apache.jena.sparql.engine.QueryIterator ;
++import org.apache.jena.sparql.engine.binding.Binding ;
++import org.apache.jena.sparql.resultset.ResultSetCompare ;
++import org.apache.jena.sparql.sse.SSE ;
++import org.junit.AfterClass ;
++import org.junit.BeforeClass ;
++import org.junit.Test ;
++import org.seaborne.tdb2.ConfigTest ;
++import org.seaborne.tdb2.junit.TL ;
++
++public class TestSolverTDB
++{
++    static String graphData = null ;
++    static Graph graph = null ;
++    static Dataset dataset = null ;
++    static PrefixMapping pmap = null ;
++
++    @BeforeClass static public void beforeClass()
++    { 
++        dataset = TL.createTestDatasetMem() ;
++        dataset.begin(ReadWrite.WRITE);
++        graphData = ConfigTest.getTestingDataRoot()+"/Data/solver-data.ttl" ;
++        RDFDataMgr.read(dataset, graphData) ;
++        graph = dataset.asDatasetGraph().getDefaultGraph() ;
++        pmap = new PrefixMappingImpl() ;
++        pmap.setNsPrefix("", "http://example/") ;
++    }
++    
++    
++    @AfterClass static public void afterClass() {
++        dataset.abort();
++        TL.expel(dataset);
++    }
++            
++    static private void addAll(Graph srcGraph, Graph dstGraph)
++    {
++        Iterator<Triple> triples = srcGraph.find(Node.ANY, Node.ANY, Node.ANY) ;
++        triples.forEachRemaining(dstGraph::add) ;
++    }
++
++
++    @Test public void solve_01()
++    {
++        ResultSet rs1 = exec("(bgp (:s :p :o))", graph) ;
++        ResultSet rs2 = results("unit") ;
++        equals(rs1, rs2) ;
++    }
++    
++    @Test public void solve_02()
++    {
++        ResultSet rs1 = exec("(bgp (:s :p :o2))", graph) ;
++        ResultSet rs2 = results("empty") ;
++        equals(rs1, rs2) ;
++    }
++    
++    @Test public void solve_03()
++    {
++        // Above everything.
++        ResultSet rs1 = exec("(bgp (:zzzz :p 999999))", graph) ;
++        ResultSet rs2 = results("empty") ;
++        equals(rs1, rs2) ;
++    }
++    
++    @Test public void solve_04()
++    {
++        // Below everything.
++        ResultSet rs1 = exec("(bgp (:a :p :a))", graph) ;
++        ResultSet rs2 = results("empty") ;
++        equals(rs1, rs2) ;
++    }
++
++    @Test public void solve_05()
++    {
++        ResultSet rs1 = exec("(project (?s ?y) (bgp (?s :p ?z) (?z :q ?y)))", graph) ;
++        ResultSet rs2 = results("(row (?s :s) (?y :y))") ;
++        equals(rs1, rs2) ;
++    }
++    
++    @Test public void solve_06()
++    {
++        ResultSet rs1 = exec("(bgp (:s ?p ?o))", graph) ;
++        ResultSet rs2 = results("(row (?p :p) (?o :o))",
++                                "(row (?p :p) (?o 10))",
++                                "(row (?p :p) (?o :x))"
++                                ) ;
++        equals(rs1, rs2) ;
++    }
++
++    // ------
++    
++    private static void equals(ResultSet rs1, ResultSet rs2)
++    { same(rs1, rs2, true) ; }
++    
++    private static void same(ResultSet rs1, ResultSet rs2, boolean result)
++    {
++        ResultSetRewindable rsw1 = ResultSetFactory.makeRewindable(rs1) ;
++        ResultSetRewindable rsw2 = ResultSetFactory.makeRewindable(rs2) ;
++        boolean b = ResultSetCompare.equalsByValue(rsw1, rsw2) ;
++        if ( b != result)
++        {
++            System.out.println("Different: ") ;
++            rsw1.reset() ;
++            rsw2.reset() ;
++            ResultSetFormatter.out(rsw1) ;
++            ResultSetFormatter.out(rsw2) ;
++            System.out.println() ;
++        }
++        
++        assertTrue(b == result) ;
++    }
++    
++    private static ResultSet results(String... rows)
++    {
++        String str = "(table "+String.join("", rows)+")" ;
++        return SSE.parseTable(str).toResultSet() ; 
++    }
++    
++    
++    private static ResultSet exec(String pattern, Graph graph)
++    {
++        Op op = SSE.parseOp(pattern, pmap) ;
++        List<Var> vars =  new ArrayList<>() ;
++        vars.addAll(OpVars.visibleVars(op)) ;
++        QueryIterator qIter = Algebra.exec(op, graph) ;
++        return ResultSetFactory.create(qIter, Var.varNames(vars)) ;
++    }
++    
++    private static List<Binding> toList(QueryIterator qIter)
++    {
++        List<Binding> x = new ArrayList<>() ;
++        for ( ; qIter.hasNext() ; )
++            x.add(qIter.nextBinding()) ;
++        return x ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/solver/TestStats.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/solver/TestStats.java
index 0000000,0000000..a9dfe92
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/solver/TestStats.java
@@@ -1,0 -1,0 +1,117 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.solver;
++
++import static org.junit.Assert.assertEquals ;
++
++import java.util.Iterator ;
++
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.graph.NodeFactory ;
++import org.apache.jena.sparql.core.Quad ;
++import org.apache.jena.sparql.sse.SSE ;
++import org.junit.Test ;
++import org.seaborne.dboe.jenax.Txn ;
++import org.seaborne.tdb2.junit.TL ;
++import org.seaborne.tdb2.solver.stats.StatsCollectorNodeId ;
++import org.seaborne.tdb2.solver.stats.StatsResults ;
++import org.seaborne.tdb2.store.DatasetGraphTDB ;
++import org.seaborne.tdb2.store.NodeId ;
++import org.seaborne.tdb2.store.nodetable.NodeTable ;
++import org.seaborne.tdb2.store.nodetupletable.NodeTupleTable ;
++import org.seaborne.tdb2.sys.TDBInternal ;
++
++public class TestStats
++{
++    static DatasetGraphTDB dsg      = TDBInternal.getDatasetGraphTDB(TL.createTestDatasetGraphMem()) ;
++    static NodeTupleTable quads     = dsg.getQuadTable().getNodeTupleTable() ;
++    static NodeTupleTable triples   = dsg.getTripleTable().getNodeTupleTable() ;
++    static NodeTable nt             = quads.getNodeTable() ;
++    
++    static Quad q1 = SSE.parseQuad("(<g1> <s> <p> 1)") ; 
++    static Quad q2 = SSE.parseQuad("(<g2> <s> <p> 2)") ; 
++    static Quad q3 = SSE.parseQuad("(<g2> <s> <p> 9)") ; 
++    static Quad q4 = SSE.parseQuad("(_    <s> <p> 1)") ; 
++    static {
++        Txn.executeWrite(dsg, ()->{
++            dsg.add(q1) ;
++            dsg.add(q2) ;
++            dsg.add(q3) ;
++            dsg.add(q4) ;
++        });
++    }
++    
++    private StatsResults statsForGraph(NodeId gid) {
++        // StatsCollectorNodeId writes nodes for rdf:type (this is not good).
++        return Txn.calculateWrite(dsg, ()-> {
++            Iterator<Tuple<NodeId>> iter = quads.find(gid, null, null, null) ;
++
++            StatsCollectorNodeId stats = new StatsCollectorNodeId(nt) ;
++            for ( ; iter.hasNext() ; ) {
++                Tuple<NodeId> t = iter.next() ;
++                stats.record(t.get(0), t.get(1), t.get(2), t.get(3)) ;
++            }
++
++            return stats.results() ;
++        }) ;
++    }
++
++    private StatsResults statsForDftGraph() {
++        // StatsCollectorNodeId writes nodes for rdf:type (this is not good).
++        return Txn.calculateWrite(dsg, ()-> {
++            Iterator<Tuple<NodeId>> iter = triples.findAll() ;
++
++            StatsCollectorNodeId stats = new StatsCollectorNodeId(nt) ;
++            for ( ; iter.hasNext() ; ) {
++                Tuple<NodeId> t = iter.next() ;
++                stats.record(null, t.get(0), t.get(1), t.get(2)) ;
++            }
++
++            return stats.results() ;
++        }) ;
++    }
++
++    @Test public void stats_01() {
++        StatsResults r = statsForDftGraph() ;
++        assertEquals(1, r.getCount()) ; 
++        assertEquals(1, r.getPredicates().keySet().size()) ;
++    }
++
++    @Test public void stats_02() { 
++        NodeId gid = nt.getNodeIdForNode(NodeFactory.createURI("g1")) ;
++        StatsResults r = statsForGraph(gid) ;
++        assertEquals(1, r.getCount()) ; 
++        assertEquals(1, r.getPredicates().keySet().size()) ;
++    }
++    
++    @Test public void stats_03() { 
++        NodeId gid = nt.getNodeIdForNode(NodeFactory.createURI("g2")) ;
++        StatsResults r = statsForGraph(gid) ;
++        assertEquals(2, r.getCount()) ; 
++        assertEquals(1, r.getPredicates().keySet().size()) ;
++    }
++    
++    @Test public void stats_04() { 
++        StatsResults r = statsForGraph(null) ;
++        assertEquals(3, r.getCount()) ; 
++        assertEquals(1, r.getPredicates().keySet().size()) ;
++    }
++
++}
++
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/AbstractTestStoreConnectionBasics.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/AbstractTestStoreConnectionBasics.java
index 0000000,0000000..792fef4
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/AbstractTestStoreConnectionBasics.java
@@@ -1,0 -1,0 +1,185 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store;
++
++
++import static org.junit.Assert.*;
++import org.apache.jena.query.ReadWrite ;
++import org.apache.jena.sparql.core.Quad ;
++import org.apache.jena.sparql.sse.SSE ;
++import org.junit.After ;
++import org.junit.Before ;
++import org.junit.Test ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.dboe.jenax.Txn ;
++import org.seaborne.tdb2.sys.StoreConnection ;
++import org.seaborne.tdb2.sys.TDBInternal ;
++
++/** StoreConnection and transactions - basic wiring.
++ *  These tests are slow on rotational disk.
++ *  Complet cleaning of file areas is done.
++ */ 
++public abstract class AbstractTestStoreConnectionBasics
++{
++    // Subclass to give direct and mapped versions.
++    
++    // Per-test unique-ish.
++    static int count = 0 ;
++    long x = System.currentTimeMillis()+(count++) ;
++    
++    Quad q  = SSE.parseQuad("(<g> <s> <p> '000-"+x+"') ") ;
++    Quad q1 = SSE.parseQuad("(<g> <s> <p> '111-"+x+"')") ;
++    Quad q2 = SSE.parseQuad("(<g> <s> <p> '222-"+x+"')") ;
++    Quad q3 = SSE.parseQuad("(<g> <s> <p> '333-"+x+"')") ;
++    Quad q4 = SSE.parseQuad("(<g> <s> <p> '444-"+x+"')") ;
++    
++    Location location = null ;
++
++    protected abstract Location getLocation() ;
++    
++    @Before public void before()
++    {
++        TDBInternal.reset() ;
++        location = getLocation() ;
++    }
++
++    @After public void after() { TDBInternal.reset() ; } 
++
++    @Test
++    public void store_01()
++    {
++        StoreConnection sConn = StoreConnection.connectCreate(location) ;
++        assertNotNull(sConn);
++        StoreConnection.release(location) ;
++        StoreConnection sConn2 = StoreConnection.connectExisting(location) ;
++        assertNull(sConn2);
++        StoreConnection sConn3 = StoreConnection.connectCreate(location) ;
++        assertNotNull(sConn3);
++    }
++
++    @Test
++    public void store_02()
++    {
++        StoreConnection sConn = StoreConnection.connectCreate(location) ;
++
++        { // Isolate to stop mix ups on variables.
++            DatasetGraphTDB dsg = sConn.getDatasetGraphTDB() ;
++            Txn.executeWrite(dsg, ()->{
++                dsg.add(q1) ;
++            }) ;
++
++            Txn.executeWrite(dsg, ()->{
++                assertTrue(dsg.contains(q1)) ;
++            }) ;
++
++            Txn.executeRead(dsg, ()->{
++                assertTrue(dsg.contains(q1)) ;
++            }) ;
++        }
++
++        {
++            StoreConnection sConn2 = StoreConnection.connectCreate(location) ;
++            DatasetGraphTDB dsg2 = sConn.getDatasetGraphTDB() ;
++            Txn.executeRead(dsg2, ()->{
++                assertTrue(dsg2.contains(q1)) ;
++            }) ;
++        }
++        
++        StoreConnection.release(sConn.getLocation()) ;
++        sConn = null ;
++        
++        {
++            if ( ! location.isMem() ) {
++                StoreConnection sConn2 = StoreConnection.connectCreate(location) ;
++                DatasetGraphTDB dsg3 = sConn2.getDatasetGraphTDB() ;
++                Txn.executeRead(dsg3, ()->{
++                    assertTrue(dsg3.contains(q1)) ;
++                }) ;
++            }
++        }
++    }
++    
++    @Test
++    public void store_03()
++    {
++        StoreConnection sConn = StoreConnection.connectCreate(location) ;
++        
++        DatasetGraphTDB dsg = sConn.getDatasetGraphTDB() ;
++        Txn.executeWrite(dsg, ()->{
++            dsg.add(q1) ;
++        }) ;
++        
++        Txn.executeWrite(dsg, ()->{
++            assertTrue(dsg.contains(q1)) ;
++        }) ;
++        
++        try { 
++            Txn.executeWrite(dsg, ()->{
++                dsg.add(q2) ;
++                throw new RuntimeException() ; 
++            }) ;
++            fail("Should not get to here!") ;
++        } catch (RuntimeException ex) {}
++
++        Txn.executeRead(dsg, ()->{
++            assertTrue(dsg.contains(q1)) ;
++            assertFalse(dsg.contains(q2)) ;
++        }) ;
++    }
++    
++    @Test
++    public void store_04()
++    {
++        StoreConnection sConn = StoreConnection.connectCreate(location) ;
++        
++        DatasetGraphTDB dsg = sConn.getDatasetGraphTDB() ;
++        Txn.executeWrite(dsg, ()->{
++            dsg.add(q1) ;
++        }) ;
++        
++        Txn.executeWrite(dsg, ()->{
++            assertTrue(dsg.contains(q1)) ;
++        }) ;
++
++        dsg.begin(ReadWrite.WRITE);
++        dsg.add(q2) ;
++        dsg.abort() ;
++        dsg.end() ;
++                
++        Txn.executeRead(dsg, ()->{
++            assertTrue(dsg.contains(q1)) ;
++            assertFalse(dsg.contains(q2)) ;
++        }) ;
++    }
++    
++    @Test
++    public void store_05()
++    {
++        StoreConnection sConn = StoreConnection.connectCreate(location) ;
++        
++        DatasetGraphTDB dsg = sConn.getDatasetGraphTDB() ;
++        Txn.executeWrite(dsg, ()->{
++            dsg.add(q3) ;
++        }) ;
++        
++        Txn.executeWrite(dsg, ()->{
++            assertTrue(dsg.contains(q3)) ;
++        }) ;
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/AbstractTestTransPromoteTDB2.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/AbstractTestTransPromoteTDB2.java
index 0000000,0000000..7441a56
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/AbstractTestTransPromoteTDB2.java
@@@ -1,0 -1,0 +1,71 @@@
++/*
++ * 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.
++ */
++
++// **** COPIED from Jena 3.4.0 development.
++// After Mantis moves to 3.4.0 or later, this can be removed.
++
++//package org.apache.jena.sparql.transaction ;
++package org.seaborne.tdb2.store;
++
++import static org.junit.Assert.fail ;
++
++import org.apache.jena.query.ReadWrite ;
++import org.apache.jena.sparql.JenaTransactionException;
++import org.apache.jena.sparql.core.DatasetGraph ;
++import org.apache.jena.sparql.transaction.AbstractTestTransPromote;
++import org.apache.log4j.Logger ;
++import org.junit.Ignore;
++import org.junit.Test ;
++
++/** Tests for transactions that start read and then promote to write */
++public abstract class AbstractTestTransPromoteTDB2 extends AbstractTestTransPromote {
++
++    protected AbstractTestTransPromoteTDB2(Logger[] loggers) {
++        super(loggers);
++    }
++    
++    // Copy in Jena 3.4.0 development
++    // write-end becomes an exception.
++    
++    @Override
++    @Ignore
++    @Test public void promote_snapshot_05()         { }
++    @Test public void promote_snapshot_05_x()         { run_05_X(false) ; }
++
++    @Override
++    @Ignore
++    @Test public void promote_readCommitted_05()    { }
++
++    @Test public void promote_readCommitted_05_x()    { run_05_X(true) ; }
++    
++    private void run_05_X(boolean readCommitted) {
++        //Assume.assumeTrue( ! readCommitted || supportsReadCommitted());
++        
++        setReadCommitted(readCommitted);
++        DatasetGraph dsg = create() ;
++        dsg.begin(ReadWrite.READ) ;
++        dsg.add(q1) ;
++        
++        try {
++            dsg.end() ;
++            fail("begin(W);end() did not throw an exception");
++        } catch ( JenaTransactionException ex) {}
++
++        assertCount(0, dsg) ;
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TS_Store.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TS_Store.java
index 0000000,0000000..580b301
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TS_Store.java
@@@ -1,0 -1,0 +1,71 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store;
++
++import org.junit.AfterClass ;
++import org.junit.BeforeClass ;
++import org.junit.runner.RunWith ;
++import org.junit.runners.Suite ;
++import org.seaborne.dboe.base.block.FileMode ;
++import org.seaborne.tdb2.store.value.TestDoubleNode62;
++import org.seaborne.tdb2.store.value.TestNodeIdInline;
++import org.seaborne.tdb2.sys.SystemTDB ;
++import org.seaborne.tdb2.sys.TestOps ;
++
++@RunWith(Suite.class)
++@Suite.SuiteClasses( {
++    TestNodeId.class
++    , TestNodeIdInline.class
++    , TestDoubleNode62.class
++    , TestTripleTable.class
++    , TestGraphTDB.class
++    , TestGraphNamedTDB.class
++    , TestDatasetTDB.class
++    , TestDatasetTDBPersist.class
++    //, TestBulkLoader.class
++    // The script suite
++    , TestSuiteGraphTDB.class
++    
++    , Test_SPARQL_TDB.class
++    , TestDynamicDatasetTDB.class
++    , TestStoreConnectionMem.class
++    , TestStoreConnectionDirect.class
++    , TestStoreConnectionMapped.class
++    , TestStoreConnectionLock.class
++    , TestTransactions.class
++    , TestTransactionLifecycleTDB.class
++    , TestTransPromoteTDB.class
++    , TestQuadFilter.class
++} )
++public class TS_Store
++{ 
++    static FileMode mode ; 
++    
++    @BeforeClass
++    public static void beforeClass()
++    {
++        mode = SystemTDB.fileMode() ;
++    }
++    
++    @AfterClass
++    public static void afterClass()
++    {
++        if ( ! SystemTDB.fileMode().equals(mode) )
++            TestOps.setFileMode(mode) ;    
++    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestDatasetTDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestDatasetTDB.java
index 0000000,0000000..1ff5387
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestDatasetTDB.java
@@@ -1,0 -1,0 +1,323 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store;
++
++import static org.junit.Assert.*;
++import org.apache.jena.query.* ;
++import org.apache.jena.rdf.model.Model ;
++import org.apache.jena.rdf.model.ModelFactory ;
++import org.apache.jena.rdf.model.Property ;
++import org.apache.jena.rdf.model.Resource ;
++import org.apache.jena.riot.Lang ;
++import org.apache.jena.riot.RDFDataMgr ;
++import org.apache.jena.sparql.core.Quad ;
++import org.apache.jena.sparql.sse.SSE ;
++import org.junit.After ;
++import org.junit.Before ;
++import org.junit.Test ;
++import org.seaborne.tdb2.TDB2 ;
++import org.seaborne.tdb2.junit.TL ;
++
++/** Tests of datasets, prefixes, special URIs etc (see also {@link org.apache.jena.sparql.graph.GraphsTests} */
++public class TestDatasetTDB
++{
++    private Dataset dataset ;
++    
++    @Before public void before() {
++        dataset = TL.createTestDatasetMem() ;
++        dataset.begin(ReadWrite.WRITE);
++    }
++    
++    @After public void after() {
++        dataset.abort();
++        dataset.end();
++        TL.expel(dataset);
++        
++    }
++    
++    private Dataset dataset() {
++        return dataset ;
++    }
++    
++    private static void load(Model model, String file)
++    {
++        RDFDataMgr.read(model, file) ;
++    }
++    
++    private static String base1 = "http://example/" ;
++    private static String baseNS = "http://example/ns#" ;
++    
++    private static void load1(Model model)
++    {
++        model.setNsPrefix("", base1) ;
++        Resource r1 = model.createResource(base1+"r1") ;
++        Property p1 = model.createProperty(baseNS+"p1") ;
++        model.add(r1, p1, "x1") ;
++        model.add(r1, p1, "x2") ;
++    }
++    
++    private static void load2(Model model)
++    {
++        Resource r2 = model.createResource(base1+"r2") ;
++        Property p1 = model.createProperty(baseNS+"p1") ;
++        model.add(r2, p1, "x1") ;
++        model.add(r2, p1, "x2") ;
++    }
++
++    private static void load3(Model model)
++    {
++        Resource r3 = model.createResource(base1+"r3") ;
++        Property p1 = model.createProperty(baseNS+"p2") ;
++        model.add(r3, p1, "x1") ;
++        model.add(r3, p1, "x2") ;
++    }
++
++    @Test public void prefix1()
++    {
++        Dataset ds = dataset() ;
++        Model m = ds.getDefaultModel() ;
++        load1(m) ;
++        String x = m.expandPrefix(":x") ;
++        assertEquals(x, base1+"x") ;
++    }
++    
++    @Test public void prefix2()
++    {
++        Dataset ds = dataset() ;
++        Model m = ds.getDefaultModel() ;
++        load1(m) ;
++        Model m2 = ds.getNamedModel("http://example/graph/") ;
++        String x = m2.expandPrefix(":x") ;
++        assertEquals(x, ":x") ;
++    }
++    
++    @Test public void query1()
++    {
++        Dataset ds = dataset() ;
++        Model m = ds.getDefaultModel() ;
++        load1(m) ;
++        
++        String qs = "CONSTRUCT {?s ?p ?o } WHERE {?s ?p ?o}" ;
++        Query q = QueryFactory.create(qs) ;
++        QueryExecution qExec = QueryExecutionFactory.create(q, ds) ;
++        Model m2 = qExec.execConstruct() ;
++        assertTrue(m.isIsomorphicWith(m2)) ;
++    }
++    
++    @Test public void query2()
++    {
++        Dataset ds = dataset() ;
++        Model m = ds.getDefaultModel() ;
++        load1(m) ;
++        
++        String qs = "CONSTRUCT {?s ?p ?o } WHERE { GRAPH <http://example/graph/> {?s ?p ?o}}" ;
++        Query q = QueryFactory.create(qs) ;
++        QueryExecution qExec = QueryExecutionFactory.create(q, ds) ;
++        Model m2 = qExec.execConstruct() ;
++        assertTrue(m2.isEmpty()) ;
++    }
++    
++    static String defaultGraph = Quad.defaultGraphIRI.getURI() ; 
++    static String unionGraph = Quad.unionGraph.getURI() ;
++    
++    @Test public void special1()
++    {
++        Dataset ds = dataset() ;
++        Model m = ds.getDefaultModel() ;
++        load1(m) ;
++        
++        String qs = "CONSTRUCT {?s ?p ?o } WHERE { GRAPH <"+defaultGraph+"> {?s ?p ?o}}" ;
++        Query q = QueryFactory.create(qs) ;
++        QueryExecution qExec = QueryExecutionFactory.create(q, ds) ;
++        Model m2 = qExec.execConstruct() ;
++        assertTrue(m.isIsomorphicWith(m2)) ;
++    }
++    
++    @Test public void special2()
++    {
++        Dataset ds = dataset() ;
++
++        load1(ds.getDefaultModel()) ;
++        load2(ds.getNamedModel("http://example/graph1")) ;
++        load3(ds.getNamedModel("http://example/graph2")) ;
++        
++        Model m = ModelFactory.createDefaultModel() ;
++        load2(m) ;
++        load3(m) ;
++        
++        String qs = "CONSTRUCT {?s ?p ?o } WHERE { GRAPH <"+unionGraph+"> {?s ?p ?o}}" ;
++        Query q = QueryFactory.create(qs) ;
++        QueryExecution qExec = QueryExecutionFactory.create(q, ds) ;
++        Model m2 = qExec.execConstruct() ;
++        assertTrue(m.isIsomorphicWith(m2)) ;
++    }
++    
++    @Test public void special3()
++    {
++        Dataset ds = dataset() ;
++
++        load1(ds.getDefaultModel()) ;
++        load2(ds.getNamedModel("http://example/graph1")) ;
++        load3(ds.getNamedModel("http://example/graph2")) ;
++        
++        Model m = ModelFactory.createDefaultModel() ;
++        load2(m) ;
++        load3(m) ;
++        
++        String qs = "CONSTRUCT {?s ?p ?o } WHERE { ?s ?p ?o }" ;
++        Query q = QueryFactory.create(qs) ;
++        QueryExecution qExec = QueryExecutionFactory.create(q, ds) ;
++        qExec.getContext().set(TDB2.symUnionDefaultGraph, true) ;
++        Model m2 = qExec.execConstruct() ;
++        if ( ! m.isIsomorphicWith(m2) )
++        {
++            System.out.println("---- ----") ;
++            SSE.write(ds.asDatasetGraph()) ;
++            System.out.println("-- Expected") ;
++            m.write(System.out, "TTL") ;
++            System.out.println("-- Actual") ;
++            m2.write(System.out, "TTL") ;
++            System.out.println("---- ----") ;
++
++        }
++        assertTrue(m.isIsomorphicWith(m2)) ;
++    }
++
++    @Test public void special4()
++    {
++        Dataset ds = dataset() ;
++
++        load1(ds.getDefaultModel()) ;
++        load2(ds.getNamedModel("http://example/graph1")) ;
++        load3(ds.getNamedModel("http://example/graph2")) ;        
++        
++        Model m = ModelFactory.createDefaultModel() ;
++        load2(m) ;
++        load3(m) ;
++        
++        String qs = "PREFIX : <"+baseNS+"> SELECT (COUNT(?x) as ?c) WHERE { ?x (:p1|:p2) 'x1' }" ;
++        Query q = QueryFactory.create(qs) ;
++        
++        long c_m ;
++        // Model
++        try (QueryExecution qExec = QueryExecutionFactory.create(q, m)) {
++            c_m = qExec.execSelect().next().getLiteral("c").getLong() ;
++        }
++
++        // dataset
++        long c_ds ;
++        try (QueryExecution qExec = QueryExecutionFactory.create(q, ds)) {
++            qExec.getContext().set(TDB2.symUnionDefaultGraph, true) ;        
++            c_ds = qExec.execSelect().next().getLiteral("c").getLong() ;
++        }
++        
++//        String qs2 = "PREFIX : <"+baseNS+"> SELECT * WHERE { ?x (:p1|:p2) 'x1' }" ;
++//        Query q2 = QueryFactory.create(qs2) ;
++//        qExec = QueryExecutionFactory.create(q2, ds) ;
++//        qExec.getContext().set(TDB.symUnionDefaultGraph, true) ;
++//        ResultSetFormatter.out(qExec.execSelect()) ;
++//        
++//        qExec = QueryExecutionFactory.create(q2, m) ;
++//        ResultSetFormatter.out(qExec.execSelect()) ;
++        // --------
++        
++        assertEquals(c_m, c_ds) ; 
++    }
++    
++    @Test public void special5()
++    {
++        Dataset ds = dataset() ;
++
++        //load1(ds.getDefaultModel()) ;
++        load1(ds.getNamedModel("http://example/graph1")) ;  // Same triples, different graph
++        load1(ds.getNamedModel("http://example/graph2")) ;
++        
++        Model m = ds.getNamedModel(unionGraph) ;
++        assertEquals(2, m.size()) ;
++    }
++    
++    // Put a model into a general dataset and use it.
++    @Test public void generalDataset1()
++    {
++        Dataset ds = dataset() ;
++        load1(ds.getDefaultModel()) ;
++        load2(ds.getNamedModel("http://example/graph1")) ;
++        load3(ds.getNamedModel("http://example/graph2")) ;
++        Model m = ds.getNamedModel("http://example/graph2") ;
++        
++        // Use graph2 as default model.
++        Dataset ds2 = DatasetFactory.create() ;
++        ds2.setDefaultModel(ds.getNamedModel("http://example/graph2")) ;
++        
++        String qs = "CONSTRUCT {?s ?p ?o } WHERE { ?s ?p ?o}" ;
++        Query q = QueryFactory.create(qs) ;
++        QueryExecution qExec = QueryExecutionFactory.create(q, ds2) ;
++        Model m2 = qExec.execConstruct() ;
++        assertTrue(m.isIsomorphicWith(m2)) ;
++    }
++    
++    @Test public void generalDataset2()
++    {
++        Dataset ds = dataset() ;
++        load1(ds.getDefaultModel()) ;
++        load2(ds.getNamedModel("http://example/graph1")) ;
++        load3(ds.getNamedModel("http://example/graph2")) ;
++        Model m = ds.getNamedModel("http://example/graph2") ;
++        
++        // Use graph1 as a differently named model.
++        Dataset ds2 = DatasetFactory.create() ;
++        ds2.addNamedModel("http://example/graphOther", m) ;
++        
++        String qs = "CONSTRUCT {?s ?p ?o } WHERE { {?s ?p ?o} UNION { GRAPH <http://example/graphOther> {?s ?p ?o} } }" ;
++        Query q = QueryFactory.create(qs) ;
++        QueryExecution qExec = QueryExecutionFactory.create(q, ds2) ;
++        Model m2 = qExec.execConstruct() ;
++        if ( ! m.isIsomorphicWith(m2) )
++        {
++            System.out.println(m.getGraph().getClass().getSimpleName()+"/"+m.size()+" : "+m2.getGraph().getClass().getSimpleName()+"/"+m2.size()) ;
++            System.out.println("---- Different:" ) ;
++            RDFDataMgr.write(System.out, m, Lang.TTL) ;
++            System.out.println("---- ----" ) ;
++            RDFDataMgr.write(System.out, m2, Lang.TTL) ;
++            System.out.println("---- ----") ;
++        }
++        
++        assertTrue(m.isIsomorphicWith(m2)) ;
++    }
++    
++    @Test public void generalDataset3()
++    {
++        Dataset ds = dataset() ;
++        load1(ds.getDefaultModel()) ;
++        load2(ds.getNamedModel("http://example/graph1")) ;
++        load3(ds.getNamedModel("http://example/graph2")) ;
++        Model m = ds.getDefaultModel() ;
++        
++        // Use the default model in one dataset as a named model in another.
++        Dataset ds2 = DatasetFactory.create() ;
++        ds2.addNamedModel("http://example/graphOther", m) ;
++        
++        String qs = "CONSTRUCT {?s ?p ?o } WHERE { {?s ?p ?o} UNION { GRAPH <http://example/graphOther> {?s ?p ?o} } }" ;
++        Query q = QueryFactory.create(qs) ;
++        QueryExecution qExec = QueryExecutionFactory.create(q, ds2) ;
++        Model m2 = qExec.execConstruct() ;
++        assertTrue(m.isIsomorphicWith(m2)) ;
++    }
++
++    // removeAll
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestDatasetTDBPersist.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestDatasetTDBPersist.java
index 0000000,0000000..81a5dd5
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestDatasetTDBPersist.java
@@@ -1,0 -1,0 +1,111 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store;
++
++import static org.junit.Assert.assertFalse ;
++import static org.junit.Assert.assertTrue ;
++
++import org.apache.jena.graph.Graph ;
++import org.apache.jena.graph.Node ;
++import org.apache.jena.graph.Triple ;
++import org.apache.jena.sparql.util.NodeFactoryExtra ;
++import org.junit.Test ;
++import org.seaborne.tdb2.junit.TL ;
++
++/** Testing persistence  */ 
++public class TestDatasetTDBPersist
++{
++    static Node n0 = NodeFactoryExtra.parseNode("<http://example/n0>") ; 
++    static Node n1 = NodeFactoryExtra.parseNode("<http://example/n1>") ;
++    static Node n2 = NodeFactoryExtra.parseNode("<http://example/n2>") ;
++    
++    @Test
++    public void dataset1() {
++        TL.exec((ds) -> {
++            assertTrue(ds.getDefaultModel().getGraph() instanceof GraphViewSwitchable) ;
++            assertTrue(ds.getNamedModel("http://example/").getGraph() instanceof GraphViewSwitchable) ;
++        }) ;
++    }
++    
++    @Test
++    public void dataset2() {
++        TL.exec((ds) -> {
++            Graph g1 = ds.getDefaultModel().getGraph() ;
++            Graph g2 = ds.getNamedModel("http://example/").getGraph() ;
++
++            g1.add(new Triple(n0, n1, n2)) ;
++            assertTrue(g1.contains(n0, n1, n2)) ;
++            assertFalse(g2.contains(n0, n1, n2)) ;
++        }) ;
++    }
++
++//    @Test
++//    public void dataset3() {
++//        TL.exec((ds) -> {
++//            Graph g1 = ds.getDefaultModel().getGraph() ;
++//            // Sometimes, under windows, deleting the files by
++//            // clearDirectory does not work.
++//            // Needed for safe tests on windows.
++//            g1.clear() ;
++//
++//            Graph g2 = ds.getNamedModel("http://example/").getGraph() ;
++//            g2.add(new Triple(n0, n1, n2)) ;
++//            assertTrue(g2.contains(n0, n1, n2)) ;
++//            assertFalse(g1.contains(n0, n1, n2)) ;
++//        }) ;
++//    }
++//
++//    @Test
++//    public void dataset4() {
++//        String graphName = "http://example/" ;
++//        Triple triple = SSE.parseTriple("(<x> <y> <z>)") ;
++//        Node gn = org.apache.jena.graph.NodeFactory.createURI(graphName) ;
++//
++//        TL.exec((ds) -> {
++//            // ?? See TupleLib.
++//            ds.asDatasetGraph().deleteAny(gn, null, null, null) ;
++//
++//            Graph g2 = ds.asDatasetGraph().getGraph(gn) ;
++//
++//            // Graphs only exists if they have a triple in them
++//            assertFalse(ds.containsNamedModel(graphName)) ;
++//
++//            List<String> names = Iter.toList(ds.listNames()) ;
++//            assertEquals(0, names.size()) ;
++//            assertEquals(0, ds.asDatasetGraph().size()) ;
++//        }) ;
++//    }
++//
++//    @Test
++//    public void dataset5() {
++//        String graphName = "http://example/" ;
++//        Triple triple = SSE.parseTriple("(<x> <y> <z>)") ;
++//        TL.exec((ds) -> {
++//            Graph g2 = ds.asDatasetGraph().getGraph(org.apache.jena.graph.NodeFactory.createURI(graphName)) ;
++//            // Graphs only exists if they have a triple in them
++//            g2.add(triple) ;
++//
++//            assertTrue(ds.containsNamedModel(graphName)) ;
++//            List<String> x = Iter.toList(ds.listNames()) ;
++//            List<String> y = Arrays.asList(graphName) ;
++//            assertEquals(x, y) ;
++//
++//            assertEquals(1, ds.asDatasetGraph().size()) ;
++//        }) ;
++//    }
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestDynamicDatasetTDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestDynamicDatasetTDB.java
index 0000000,0000000..d0376e9
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestDynamicDatasetTDB.java
@@@ -1,0 -1,0 +1,44 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store;
++
++import org.apache.jena.query.Dataset ;
++import org.apache.jena.query.ReadWrite ;
++import org.apache.jena.sparql.core.AbstractTestDynamicDataset ;
++import org.seaborne.tdb2.TDB2 ;
++import org.seaborne.tdb2.junit.TL ;
++
++public class TestDynamicDatasetTDB extends AbstractTestDynamicDataset
++{
++    @Override
++    protected Dataset createDataset()
++    {
++        Dataset ds = TL.createTestDatasetMem() ;
++        ds.begin(ReadWrite.WRITE);
++        return ds;
++    }
++    
++    @Override
++    protected void releaseDataset(Dataset ds) { ds.abort(); TL.expel(ds); }
++
++    protected void startDynamicAndUnionTest()    { TDB2.getContext().setTrue(TDB2.symUnionDefaultGraph) ; }
++
++    protected void finishDynamicAndUnionTest()   { TDB2.getContext().unset(TDB2.symUnionDefaultGraph) ; }
++
++}
++

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestGraphNamedTDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestGraphNamedTDB.java
index 0000000,0000000..d844c5a
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestGraphNamedTDB.java
@@@ -1,0 -1,0 +1,60 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store;
++
++import org.apache.jena.graph.Graph ;
++import org.apache.jena.graph.Node ;
++import org.apache.jena.query.Dataset ;
++import org.apache.jena.query.ReadWrite ;
++import org.apache.jena.sparql.graph.AbstractTestGraph2 ;
++import org.apache.jena.sparql.util.NodeFactoryExtra ;
++import org.junit.After ;
++import org.junit.Before ;
++import org.seaborne.tdb2.junit.TL ;
++
++/** Programmatic tests on persistent graph */
++public class TestGraphNamedTDB extends AbstractTestGraph2
++{
++    static Node graphNode = NodeFactoryExtra.parseNode("<http://example/namedGraph>") ;
++    private Dataset dataset ;
++    private Graph   graph ;
++
++    @Before
++    public void before() {
++        dataset = TL.createTestDatasetMem() ;
++        dataset.begin(ReadWrite.WRITE);
++        graph = dataset.asDatasetGraph().getGraph(graphNode) ;
++    }
++
++    @After
++    public void after() {
++        dataset.abort();
++        dataset.end();
++        TL.expel(dataset) ;
++    }
++    
++    @Override
++    protected Graph emptyGraph() {
++        graph.clear() ;
++        return graph ;
++    }
++    
++    @Override
++    protected void returnGraph(Graph g)
++    {}
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestGraphTDB.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestGraphTDB.java
index 0000000,0000000..7f941eb
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestGraphTDB.java
@@@ -1,0 -1,0 +1,57 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store;
++
++import org.apache.jena.graph.Graph ;
++import org.apache.jena.query.Dataset ;
++import org.apache.jena.query.ReadWrite ;
++import org.apache.jena.sparql.graph.AbstractTestGraph2 ;
++import org.junit.After ;
++import org.junit.Before ;
++import org.seaborne.tdb2.junit.TL ;
++
++/** Programmatic tests on graphs */
++public class TestGraphTDB extends AbstractTestGraph2
++{
++    private Dataset dataset ;
++    private Graph   graph ;
++
++    @Before
++    public void before() {
++        dataset = TL.createTestDatasetMem() ;
++        dataset.begin(ReadWrite.WRITE);
++        graph = dataset.getDefaultModel().getGraph() ;
++    }
++
++    @After
++    public void after() {
++        dataset.abort();
++        dataset.end();
++        TL.expel(dataset) ;
++    }
++
++    @Override
++    protected Graph emptyGraph() {
++        graph.clear() ;
++        return graph ;
++    }
++    
++    @Override
++    protected void returnGraph(Graph g)
++    {}
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestNodeId.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestNodeId.java
index 0000000,0000000..56cf166
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestNodeId.java
@@@ -1,0 -1,0 +1,127 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store;
++
++import static org.junit.Assert.assertEquals;
++import static org.junit.Assert.assertFalse;
++
++import java.nio.ByteBuffer;
++
++import org.apache.jena.atlas.lib.BitsLong;
++import org.apache.jena.graph.Node;
++import org.apache.jena.sparql.util.NodeFactoryExtra;
++import org.junit.Test ;
++
++public class TestNodeId
++{
++    // Pointers.
++    @Test public void nodeId_ptr_01() {
++        NodeId nodeId = NodeIdFactory.createPtrLong(17, 37);
++        assertEquals(NodeIdType.PTR, nodeId.type());
++//        assertEquals(37L, nodeId.getPtrLo());
++//        assertEquals(17, nodeId.getPtrHi());
++        assertEquals(37L, nodeId.getValue2());
++        assertEquals(17, nodeId.getValue1());
++    }
++    
++    @Test public void nodeId_ptr_02() {
++        NodeId nodeId = NodeIdFactory.createPtr(37);
++        assertEquals(NodeIdType.PTR, nodeId.type());
++//        assertEquals(37L, nodeId.getPtrLo());
++//        assertEquals(0, nodeId.getPtrHi());
++        assertEquals(37L, nodeId.getPtrLocation());
++    }
++
++    @Test public void nodeId_ptr_03() {
++        NodeId nodeId = NodeIdFactory.createPtr(39);
++        // 64 bit
++        long x = nodeId.getValue2();
++        long t = BitsLong.unpack(x, 56, 64);
++        assertEquals(0, t);
++        assertEquals(NodeIdType.PTR.type(), t);
++    }
++    
++    // Specials.
++    @Test public void nodeId_special_01() {
++        assertFalse(NodeId.isConcrete(NodeId.NodeDoesNotExist));
++        assertEquals(NodeIdType.SPECIAL, NodeId.NodeDoesNotExist.type());
++    }
++    
++    @Test public void nodeId_special_02() {
++        assertFalse(NodeId.isConcrete(NodeId.NodeIdAny));
++        assertEquals(NodeIdType.SPECIAL, NodeId.NodeIdAny.type());
++    }
++    
++    // Storage
++    
++    @Test public void nodeId_codec_01() { testCodecArray(NodeIdFactory.createPtr(37)); }
++    
++    @Test public void nodeId_codec_02() { testCodecArray(NodeId.createRaw(NodeIdType.XSD_INTEGER, 1)); }
++    
++    // 56 bit -1.
++    @Test public void nodeId_codec_03() { testCodecArray(NodeId.createRaw(NodeIdType.XSD_INTEGER, BitsLong.clear(-1L, 56,64))); }
++
++    @Test public void nodeId_codec_04() { testCodecArray("12.34"); }
++    
++    @Test public void nodeId_codec_05() { testCodecArray("'2.2'^^xsd:float"); }
++
++    private static void testCodecArray(String str) {
++        Node n = NodeFactoryExtra.parseNode(str);
++        NodeId nid = NodeIdInline.inline(n);
++        testCodecArray(nid);
++    }
++
++    private static void testCodecArray(NodeId nid) {
++        testCodecArray(nid, nid);
++    }
++    
++    private static void testCodecArray(NodeId testNid,NodeId expected) {
++        byte[] b = new byte[8];
++        NodeIdFactory.set(testNid, b);
++        NodeId nid1 = NodeIdFactory.get(b);
++        assertEquals(expected, nid1);
++    }
++    
++    @Test public void nodeId_codec_11() { testCodecBuffer(NodeIdFactory.createPtr(37)); }
++    
++    @Test public void nodeId_codec_12() { testCodecBuffer(NodeId.createRaw(NodeIdType.XSD_INTEGER, 1)); }
++    
++    @Test public void nodeId_codec_13() { testCodecBuffer(NodeId.createRaw(NodeIdType.XSD_INTEGER, BitsLong.clear(-1L, 56,64))); }
++
++    @Test public void nodeId_codec_14() { testCodecBuffer("12.34"); }
++    
++    @Test public void nodeId_codec_15() { testCodecBuffer("'2.2'^^xsd:float"); }
++
++    private static void testCodecBuffer(String str) {
++        Node n = NodeFactoryExtra.parseNode(str);
++        NodeId nid = NodeIdInline.inline(n);
++        testCodecArray(nid);
++    }
++
++    private static void testCodecBuffer(NodeId nid) {
++        testCodecArray(nid, nid);
++    }
++    
++    private static void testCodecBuffer(NodeId testNid,NodeId expected) {
++        ByteBuffer b = ByteBuffer.allocate(8);
++        NodeIdFactory.set(testNid, b);
++        NodeId nid1 = NodeIdFactory.get(b);
++        assertEquals(expected, nid1);
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestQuadFilter.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestQuadFilter.java
index 0000000,0000000..78ee4c4
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestQuadFilter.java
@@@ -1,0 -1,0 +1,110 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store;
++
++import java.util.function.Predicate ;
++
++import static org.junit.Assert.*;
++import org.apache.jena.atlas.lib.tuple.Tuple ;
++import org.apache.jena.graph.NodeFactory ;
++import org.apache.jena.query.* ;
++import org.apache.jena.sparql.core.Quad ;
++import org.apache.jena.sparql.sse.SSE ;
++import org.apache.jena.system.Txn;
++import org.junit.AfterClass ;
++import org.junit.BeforeClass ;
++import org.junit.Ignore;
++import org.junit.Test ;
++import org.seaborne.tdb2.TDB2 ;
++import org.seaborne.tdb2.TDB2Factory ;
++import org.seaborne.tdb2.store.nodetable.NodeTable ;
++import org.seaborne.tdb2.sys.SystemTDB ;
++
++@Ignore("Quad filter tests not ready (transactions)")
++public class TestQuadFilter
++{
++    private static String graphToHide = "http://example/g2" ;
++    private static Dataset ds = setup() ;  
++    
++
++    @BeforeClass public static void beforeClass()
++    {
++        
++    }
++    
++    @AfterClass public static void afterClass() {}
++    
++    /** Example setup - in-memory dataset with two graphs, one triple in each */
++    private static Dataset setup()
++    {
++        Dataset ds = TDB2Factory.createDataset() ;
++        DatasetGraphTDB dsg = (DatasetGraphTDB)(ds.asDatasetGraph()) ;
++        Txn.executeWrite(dsg,  ()->{
++            Quad q1 = SSE.parseQuad("(<http://example/g1> <http://example/s> <http://example/p> <http://example/o1>)") ;
++            Quad q2 = SSE.parseQuad("(<http://example/g2> <http://example/s> <http://example/p> <http://example/o2>)") ;
++            dsg.add(q1) ;
++            dsg.add(q2) ;
++        });
++        return ds ;
++    }
++    
++    /** Create a filter to exclude the graph http://example/g2 */
++    private static Predicate<Tuple<NodeId>> createFilter(Dataset ds)
++    {
++        DatasetGraphTDB dsg = (DatasetGraphTDB)(ds.asDatasetGraph()) ;
++        final NodeTable nodeTable = dsg.getQuadTable().getNodeTupleTable().getNodeTable() ;
++        final NodeId target = nodeTable.getNodeIdForNode(NodeFactory.createURI(graphToHide)) ;
++        return item -> !( item.len() == 4 && item.get(0).equals(target) );
++    }            
++
++    @Test public void quad_filter_1()   { test("SELECT * { GRAPH ?g { ?s ?p ?o } }", 1, 2) ; }
++    @Test public void quad_filter_2()   { test("SELECT * { ?s ?p ?o }", 1, 2) ; }
++    @Test public void quad_filter_3()   { test("SELECT * { GRAPH ?g { } }", 1, 2) ; }
++    
++    private void test(String qs, int withFilter, int withoutFilter)
++    {
++        Predicate<Tuple<NodeId>> filter = createFilter(ds) ;
++        
++//    private static void example(Dataset ds, Filter<Tuple<NodeId>> filter)
++//    {
++//        String[] x = {
++//            "SELECT * { GRAPH ?g { ?s ?p ?o } }",
++//            "SELECT * { ?s ?p ?o }",
++//            // THis filter does not hide the graph itself, just the quads associated with the graph.
++//            "SELECT * { GRAPH ?g {} }"
++//            } ;
++        Query query = QueryFactory.create(qs) ;
++        
++        try(QueryExecution qExec = QueryExecutionFactory.create(query, ds)) {
++            // Install filter for this query only.
++            qExec.getContext().set(SystemTDB.symTupleFilter, filter) ;
++            qExec.getContext().setTrue(TDB2.symUnionDefaultGraph) ;
++            long x1 = ResultSetFormatter.consume(qExec.execSelect()) ;
++            assertEquals(withFilter, x1) ;
++        }
++        // No filter.
++        try(QueryExecution qExec = QueryExecutionFactory.create(query, ds)) {
++            qExec.getContext().setTrue(TDB2.symUnionDefaultGraph) ;
++            long x2 = ResultSetFormatter.consume(qExec.execSelect()) ;
++            assertEquals(withoutFilter, x2) ;
++        }
++
++    }
++        
++    
++}

http://git-wip-us.apache.org/repos/asf/jena/blob/d9da3592/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestStoreConnectionDirect.java
----------------------------------------------------------------------
diff --cc jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestStoreConnectionDirect.java
index 0000000,0000000..ccf1b41
new file mode 100644
--- /dev/null
+++ b/jena-db/tdb2/src/test/java/org/seaborne/tdb2/store/TestStoreConnectionDirect.java
@@@ -1,0 -1,0 +1,47 @@@
++/*
++ *  Licensed 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.
++ *
++ *  See the NOTICE file distributed with this work for additional
++ *  information regarding copyright ownership.
++ */
++
++package org.seaborne.tdb2.store ;
++
++import org.junit.AfterClass ;
++import org.junit.BeforeClass ;
++import org.seaborne.dboe.base.block.FileMode ;
++import org.seaborne.dboe.base.file.Location ;
++import org.seaborne.tdb2.ConfigTest ;
++import org.seaborne.tdb2.sys.SystemTDB ;
++import org.seaborne.tdb2.sys.TestOps ;
++
++/** Slow tests - complete cleaning of disk areas each time */
++public class TestStoreConnectionDirect extends AbstractTestStoreConnectionBasics {
++    static FileMode mode ;
++
++    @BeforeClass
++    public static void beforeClassFileMode() {
++        mode = SystemTDB.fileMode() ;
++        TestOps.setFileMode(FileMode.direct) ;
++    }
++
++    @AfterClass
++    public static void afterClassFileMode() {
++        TestOps.setFileMode(mode) ;
++    }
++
++    @Override
++    protected Location getLocation() {
++        return Location.create(ConfigTest.getCleanDir()) ;
++    }
++}


[12/50] [abbrv] jena git commit: Switch off DatasetControl

Posted by an...@apache.org.
Switch off DatasetControl


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

Branch: refs/heads/jena-tdb2
Commit: 4e8fc3b3a00016c68c2af5c5a887dc1e34f82d76
Parents: eee1a6a
Author: Andy Seaborne <an...@seaborne.org>
Authored: Sun Sep 3 22:08:23 2017 +0100
Committer: Andy Seaborne <an...@seaborne.org>
Committed: Sun Sep 3 22:08:23 2017 +0100

----------------------------------------------------------------------
 tdb2/src/main/java/org/seaborne/tdb2/setup/TDBBuilder.java         | 2 +-
 .../main/java/org/seaborne/tdb2/store/nodetable/NodeTableSSE.java  | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/4e8fc3b3/tdb2/src/main/java/org/seaborne/tdb2/setup/TDBBuilder.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/setup/TDBBuilder.java b/tdb2/src/main/java/org/seaborne/tdb2/setup/TDBBuilder.java
index 66c5c89..67ee708 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/setup/TDBBuilder.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/setup/TDBBuilder.java
@@ -143,7 +143,7 @@ public class TDBBuilder {
         return new TDBBuilder(txnCoord, location, params, new ComponentIdMgr(UUID.randomUUID())) ;
     }
 
-    private DatasetControl createPolicy() { return new DatasetControlMRSW() ; }
+    private DatasetControl createPolicy() { return new DatasetControlNone() ; }
 
     /** Look at a directory and see if it is a new area */
     private static boolean isNewDatabaseArea(Location location) {

http://git-wip-us.apache.org/repos/asf/jena/blob/4e8fc3b3/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableSSE.java
----------------------------------------------------------------------
diff --git a/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableSSE.java b/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableSSE.java
index 336ac09..ed4577f 100644
--- a/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableSSE.java
+++ b/tdb2/src/main/java/org/seaborne/tdb2/store/nodetable/NodeTableSSE.java
@@ -50,7 +50,7 @@ public class NodeTableSSE extends NodeTableNative {
     private ObjectFile objects ;
     //private final Index nodeToId ;
     
-    public NodeTableSSE(Index nodeToId, String filename)
+    private /*public*/ NodeTableSSE(Index nodeToId, String filename)
     {
         super(nodeToId);
         BufferChannel file = BufferChannelFile.create(filename) ;


[08/50] [abbrv] jena git commit: Remove use of BaseTest

Posted by an...@apache.org.
Remove use of BaseTest


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

Branch: refs/heads/jena-tdb2
Commit: 7624f54062ebda6ab9672b572e1a96a20cb01c78
Parents: 0d5a468
Author: Andy Seaborne <an...@seaborne.org>
Authored: Sat Sep 2 18:48:01 2017 +0100
Committer: Andy Seaborne <an...@seaborne.org>
Committed: Sat Sep 2 18:48:01 2017 +0100

----------------------------------------------------------------------
 tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseMgr.java     | 4 ++--
 tdb2/src/test/java/org/seaborne/tdb2/TestTDBFactory.java      | 4 ++--
 .../java/org/seaborne/tdb2/assembler/TestTDBAssembler.java    | 4 ++--
 tdb2/src/test/java/org/seaborne/tdb2/lib/TestNodeLib.java     | 4 ++--
 .../test/java/org/seaborne/tdb2/setup/TestStoreParams.java    | 4 ++--
 .../java/org/seaborne/tdb2/setup/TestStoreParamsChoose.java   | 4 ++--
 .../java/org/seaborne/tdb2/setup/TestStoreParamsCreate.java   | 4 ++--
 .../src/test/java/org/seaborne/tdb2/solver/TestSolverTDB.java | 4 ++--
 tdb2/src/test/java/org/seaborne/tdb2/solver/TestStats.java    | 4 ++--
 .../tdb2/store/AbstractTestStoreConnectionBasics.java         | 5 +++--
 .../src/test/java/org/seaborne/tdb2/store/TestDatasetTDB.java | 4 ++--
 .../src/test/java/org/seaborne/tdb2/store/TestQuadFilter.java | 4 ++--
 .../test/java/org/seaborne/tdb2/store/TestTransactions.java   | 4 ++--
 .../test/java/org/seaborne/tdb2/store/TestTripleTable.java    | 4 ++--
 .../test/java/org/seaborne/tdb2/store/Test_SPARQL_TDB.java    | 4 ++--
 .../seaborne/tdb2/store/nodetable/AbstractTestNodeTable.java  | 7 +++++--
 .../tdb2/store/tupletable/AbstractTestTupleIndex.java         | 4 ++--
 .../tdb2/store/tupletable/TestTupleIndexRecordDirect.java     | 4 ++--
 .../org/seaborne/tdb2/store/tupletable/TestTupleTable.java    | 4 ++--
 tdb2/src/test/java/org/seaborne/tdb2/sys/TestSys.java         | 4 ++--
 20 files changed, 44 insertions(+), 40 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/7624f540/tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseMgr.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseMgr.java b/tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseMgr.java
index 552aa56..da75494 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseMgr.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/TestDatabaseMgr.java
@@ -17,7 +17,7 @@
 
 package org.seaborne.tdb2;
 
-import org.apache.jena.atlas.junit.BaseTest ;
+import static org.junit.Assert.*;
 import org.apache.jena.atlas.lib.FileOps ;
 import org.apache.jena.sparql.core.DatasetGraph ;
 import org.apache.jena.sparql.core.Quad ;
@@ -28,7 +28,7 @@ import org.seaborne.dboe.jenax.Txn ;
 import org.seaborne.tdb2.sys.TDBInternal ;
 
 /** Test of DatabaseMgr - the DatasetGraph level API to TDB2 **/
-public class TestDatabaseMgr extends BaseTest
+public class TestDatabaseMgr
 {
 //    String DIRx = ConfigTest.getCleanDir() ;
 //    Location DIR = Location.create(DIRx);

http://git-wip-us.apache.org/repos/asf/jena/blob/7624f540/tdb2/src/test/java/org/seaborne/tdb2/TestTDBFactory.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/TestTDBFactory.java b/tdb2/src/test/java/org/seaborne/tdb2/TestTDBFactory.java
index 795c871..752a95c 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/TestTDBFactory.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/TestTDBFactory.java
@@ -17,7 +17,7 @@
 
 package org.seaborne.tdb2;
 
-import org.apache.jena.atlas.junit.BaseTest ;
+import static org.junit.Assert.*;
 import org.apache.jena.atlas.lib.FileOps ;
 import org.apache.jena.datatypes.xsd.XSDDatatype ;
 import org.apache.jena.query.Dataset ;
@@ -31,7 +31,7 @@ import org.seaborne.dboe.jenax.Txn ;
 import org.seaborne.tdb2.sys.TDBInternal ;
 
 /** Test of TDB2Factory - the Dataset level API to TDB2 **/
-public class TestTDBFactory extends BaseTest
+public class TestTDBFactory
 {
     static Resource s1 = ResourceFactory.createResource("http://example/s2");
     static Resource s2 = ResourceFactory.createResource();

http://git-wip-us.apache.org/repos/asf/jena/blob/7624f540/tdb2/src/test/java/org/seaborne/tdb2/assembler/TestTDBAssembler.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/assembler/TestTDBAssembler.java b/tdb2/src/test/java/org/seaborne/tdb2/assembler/TestTDBAssembler.java
index 581af56..f1e13e2 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/assembler/TestTDBAssembler.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/assembler/TestTDBAssembler.java
@@ -19,7 +19,7 @@ package org.seaborne.tdb2.assembler;
 
 import org.apache.jena.assembler.JA ;
 import org.apache.jena.assembler.exceptions.AssemblerException ;
-import org.apache.jena.atlas.junit.BaseTest ;
+import static org.junit.Assert.*;
 import org.apache.jena.atlas.lib.FileOps ;
 import org.apache.jena.graph.Graph ;
 import org.apache.jena.query.Dataset ;
@@ -36,7 +36,7 @@ import org.seaborne.tdb2.store.DatasetGraphSwitchable ;
 import org.seaborne.tdb2.store.GraphViewSwitchable ;
 import org.seaborne.tdb2.sys.TDBInternal ;
 
-public class TestTDBAssembler extends BaseTest
+public class TestTDBAssembler
 {
     // Can be slow - explicitly closes the dataset.
     static String dirAssem      = null ;

http://git-wip-us.apache.org/repos/asf/jena/blob/7624f540/tdb2/src/test/java/org/seaborne/tdb2/lib/TestNodeLib.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/lib/TestNodeLib.java b/tdb2/src/test/java/org/seaborne/tdb2/lib/TestNodeLib.java
index ee69570..3e85f41 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/lib/TestNodeLib.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/lib/TestNodeLib.java
@@ -18,12 +18,12 @@
 package org.seaborne.tdb2.lib;
 
 import static org.seaborne.tdb2.lib.NodeLib.hash ;
-import org.apache.jena.atlas.junit.BaseTest ;
+import static org.junit.Assert.*;
 import org.apache.jena.graph.Node ;
 import org.apache.jena.sparql.util.NodeFactoryExtra ;
 import org.junit.Test ;
 
-public class TestNodeLib extends BaseTest
+public class TestNodeLib
 {
     // Tests of TDBs NodeLib
     @Test public void hash1() 

http://git-wip-us.apache.org/repos/asf/jena/blob/7624f540/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParams.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParams.java b/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParams.java
index 5396733..0d8468c 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParams.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParams.java
@@ -19,7 +19,7 @@ package org.seaborne.tdb2.setup;
 
 import org.apache.jena.atlas.json.JSON ;
 import org.apache.jena.atlas.json.JsonObject ;
-import org.apache.jena.atlas.junit.BaseTest ;
+import static org.junit.Assert.*;
 import org.junit.Test ;
 import org.seaborne.dboe.base.block.FileMode ;
 import org.seaborne.tdb2.TDBException ;
@@ -27,7 +27,7 @@ import org.seaborne.tdb2.setup.StoreParams ;
 import org.seaborne.tdb2.setup.StoreParamsBuilder ;
 import org.seaborne.tdb2.setup.StoreParamsCodec ;
 
-public class TestStoreParams extends BaseTest {
+public class TestStoreParams {
 
     @Test public void store_params_01() {
         assertEqualsStoreParams(StoreParams.getDftStoreParams(), StoreParams.getDftStoreParams()) ; 

http://git-wip-us.apache.org/repos/asf/jena/blob/7624f540/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParamsChoose.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParamsChoose.java b/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParamsChoose.java
index 5bbf3c3..2b20d54 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParamsChoose.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParamsChoose.java
@@ -17,7 +17,7 @@
 
 package org.seaborne.tdb2.setup;
 
-import org.apache.jena.atlas.junit.BaseTest ;
+import static org.junit.Assert.*;
 import org.apache.jena.atlas.lib.FileOps ;
 import org.junit.Test ;
 import org.seaborne.dboe.base.file.Location ;
@@ -26,7 +26,7 @@ import org.seaborne.tdb2.ConfigTest ;
 
 //TestParamsCreate
 /** This test suite uses on-disk structures and can be slow */ 
-public class TestStoreParamsChoose extends BaseTest {
+public class TestStoreParamsChoose {
     private String DIR = ConfigTest.getCleanDir() ;
     
     static final StoreParams pApp = StoreParams.builder()

http://git-wip-us.apache.org/repos/asf/jena/blob/7624f540/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParamsCreate.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParamsCreate.java b/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParamsCreate.java
index fbac48e..44aacfb 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParamsCreate.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/setup/TestStoreParamsCreate.java
@@ -23,7 +23,7 @@ import java.nio.file.Paths ;
 
 import org.apache.jena.atlas.json.JSON ;
 import org.apache.jena.atlas.json.JsonObject ;
-import org.apache.jena.atlas.junit.BaseTest ;
+import static org.junit.Assert.*;
 import org.apache.jena.atlas.lib.FileOps ;
 import org.junit.After ;
 import org.junit.Before ;
@@ -39,7 +39,7 @@ import org.seaborne.tdb2.sys.TDBInternal ;
  * This test suite uses on-disk structures, does a lot of clean/create/sync
  * calls and can be noticably slow.
  */
-public class TestStoreParamsCreate extends BaseTest {
+public class TestStoreParamsCreate {
     private final String DB_DIR = ConfigTest.getCleanDir() ;
     private final Path db = Paths.get(DB_DIR) ;
     private final Path cfg = Paths.get(DB_DIR, Names.TDB_CONFIG_FILE) ;

http://git-wip-us.apache.org/repos/asf/jena/blob/7624f540/tdb2/src/test/java/org/seaborne/tdb2/solver/TestSolverTDB.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/solver/TestSolverTDB.java b/tdb2/src/test/java/org/seaborne/tdb2/solver/TestSolverTDB.java
index dcaa71e..d94252e 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/solver/TestSolverTDB.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/solver/TestSolverTDB.java
@@ -22,7 +22,7 @@ import java.util.ArrayList ;
 import java.util.Iterator ;
 import java.util.List ;
 
-import org.apache.jena.atlas.junit.BaseTest ;
+import static org.junit.Assert.*;
 import org.apache.jena.graph.Graph ;
 import org.apache.jena.graph.Node ;
 import org.apache.jena.graph.Triple ;
@@ -44,7 +44,7 @@ import org.junit.Test ;
 import org.seaborne.tdb2.ConfigTest ;
 import org.seaborne.tdb2.junit.TL ;
 
-public class TestSolverTDB extends BaseTest
+public class TestSolverTDB
 {
     static String graphData = null ;
     static Graph graph = null ;

http://git-wip-us.apache.org/repos/asf/jena/blob/7624f540/tdb2/src/test/java/org/seaborne/tdb2/solver/TestStats.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/solver/TestStats.java b/tdb2/src/test/java/org/seaborne/tdb2/solver/TestStats.java
index e31263b..8bb3ae7 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/solver/TestStats.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/solver/TestStats.java
@@ -19,7 +19,7 @@ package org.seaborne.tdb2.solver;
 
 import java.util.Iterator ;
 
-import org.apache.jena.atlas.junit.BaseTest ;
+import static org.junit.Assert.*;
 import org.apache.jena.atlas.lib.tuple.Tuple ;
 import org.apache.jena.graph.NodeFactory ;
 import org.apache.jena.sparql.core.Quad ;
@@ -34,7 +34,7 @@ import org.seaborne.tdb2.store.nodetable.NodeTable ;
 import org.seaborne.tdb2.store.nodetupletable.NodeTupleTable ;
 import org.seaborne.tdb2.sys.TDBInternal ;
 
-public class TestStats extends BaseTest
+public class TestStats
 {
     static DatasetGraphTDB dsg      = TDBInternal.getDatasetGraphTDB(TL.createTestDatasetGraphMem()) ;
     static NodeTupleTable quads     = dsg.getQuadTable().getNodeTupleTable() ;

http://git-wip-us.apache.org/repos/asf/jena/blob/7624f540/tdb2/src/test/java/org/seaborne/tdb2/store/AbstractTestStoreConnectionBasics.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/store/AbstractTestStoreConnectionBasics.java b/tdb2/src/test/java/org/seaborne/tdb2/store/AbstractTestStoreConnectionBasics.java
index c23dc0e..792fef4 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/store/AbstractTestStoreConnectionBasics.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/store/AbstractTestStoreConnectionBasics.java
@@ -17,7 +17,8 @@
 
 package org.seaborne.tdb2.store;
 
-import org.apache.jena.atlas.junit.BaseTest ;
+
+import static org.junit.Assert.*;
 import org.apache.jena.query.ReadWrite ;
 import org.apache.jena.sparql.core.Quad ;
 import org.apache.jena.sparql.sse.SSE ;
@@ -33,7 +34,7 @@ import org.seaborne.tdb2.sys.TDBInternal ;
  *  These tests are slow on rotational disk.
  *  Complet cleaning of file areas is done.
  */ 
-public abstract class AbstractTestStoreConnectionBasics extends BaseTest
+public abstract class AbstractTestStoreConnectionBasics
 {
     // Subclass to give direct and mapped versions.
     

http://git-wip-us.apache.org/repos/asf/jena/blob/7624f540/tdb2/src/test/java/org/seaborne/tdb2/store/TestDatasetTDB.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/store/TestDatasetTDB.java b/tdb2/src/test/java/org/seaborne/tdb2/store/TestDatasetTDB.java
index 9bc74bb..48fd7fb 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/store/TestDatasetTDB.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/store/TestDatasetTDB.java
@@ -17,7 +17,7 @@
 
 package org.seaborne.tdb2.store;
 
-import org.apache.jena.atlas.junit.BaseTest ;
+import static org.junit.Assert.*;
 import org.apache.jena.query.* ;
 import org.apache.jena.rdf.model.Model ;
 import org.apache.jena.rdf.model.ModelFactory ;
@@ -34,7 +34,7 @@ import org.seaborne.tdb2.TDB2 ;
 import org.seaborne.tdb2.junit.TL ;
 
 /** Tests of datasets, prefixes, special URIs etc (see also {@link org.apache.jena.sparql.graph.GraphsTests} */
-public class TestDatasetTDB extends BaseTest
+public class TestDatasetTDB
 {
     private Dataset dataset ;
     

http://git-wip-us.apache.org/repos/asf/jena/blob/7624f540/tdb2/src/test/java/org/seaborne/tdb2/store/TestQuadFilter.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/store/TestQuadFilter.java b/tdb2/src/test/java/org/seaborne/tdb2/store/TestQuadFilter.java
index a08b263..78ee4c4 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/store/TestQuadFilter.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/store/TestQuadFilter.java
@@ -19,7 +19,7 @@ package org.seaborne.tdb2.store;
 
 import java.util.function.Predicate ;
 
-import org.apache.jena.atlas.junit.BaseTest ;
+import static org.junit.Assert.*;
 import org.apache.jena.atlas.lib.tuple.Tuple ;
 import org.apache.jena.graph.NodeFactory ;
 import org.apache.jena.query.* ;
@@ -36,7 +36,7 @@ import org.seaborne.tdb2.store.nodetable.NodeTable ;
 import org.seaborne.tdb2.sys.SystemTDB ;
 
 @Ignore("Quad filter tests not ready (transactions)")
-public class TestQuadFilter extends BaseTest
+public class TestQuadFilter
 {
     private static String graphToHide = "http://example/g2" ;
     private static Dataset ds = setup() ;  

http://git-wip-us.apache.org/repos/asf/jena/blob/7624f540/tdb2/src/test/java/org/seaborne/tdb2/store/TestTransactions.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/store/TestTransactions.java b/tdb2/src/test/java/org/seaborne/tdb2/store/TestTransactions.java
index 5442c79..47bc3e5 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/store/TestTransactions.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/store/TestTransactions.java
@@ -21,7 +21,7 @@ import java.io.StringReader ;
 import java.util.Iterator;
 
 import org.apache.jena.atlas.iterator.Iter ;
-import org.apache.jena.atlas.junit.BaseTest ;
+import static org.junit.Assert.*;
 import org.apache.jena.atlas.lib.StrUtils ;
 import org.apache.jena.query.Dataset ;
 import org.apache.jena.query.ReadWrite;
@@ -48,7 +48,7 @@ import org.seaborne.tdb2.sys.TDBInternal;
  * @see TestStoreConnectionMapped
  * @see TestStoreConnectionMem
  */
-public class TestTransactions extends BaseTest
+public class TestTransactions
 {
     // Per-test unique-ish.
     static int count = 0 ;

http://git-wip-us.apache.org/repos/asf/jena/blob/7624f540/tdb2/src/test/java/org/seaborne/tdb2/store/TestTripleTable.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/store/TestTripleTable.java b/tdb2/src/test/java/org/seaborne/tdb2/store/TestTripleTable.java
index 5908498..98e48d1 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/store/TestTripleTable.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/store/TestTripleTable.java
@@ -19,7 +19,7 @@ package org.seaborne.tdb2.store;
 
 import java.util.Iterator ;
 
-import org.apache.jena.atlas.junit.BaseTest ;
+import static org.junit.Assert.*;
 import org.apache.jena.graph.Node ;
 import org.apache.jena.graph.Triple ;
 import org.apache.jena.query.ReadWrite ;
@@ -30,7 +30,7 @@ import org.junit.Test ;
 import org.seaborne.dboe.base.file.Location ;
 import org.seaborne.tdb2.setup.TDBBuilder ;
 
-public class TestTripleTable extends BaseTest
+public class TestTripleTable
 {
     static {
         Logger.getLogger("org.apache.jena.tdb.info").setLevel(Level.WARN) ;

http://git-wip-us.apache.org/repos/asf/jena/blob/7624f540/tdb2/src/test/java/org/seaborne/tdb2/store/Test_SPARQL_TDB.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/store/Test_SPARQL_TDB.java b/tdb2/src/test/java/org/seaborne/tdb2/store/Test_SPARQL_TDB.java
index e23875b..0ab4cb4 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/store/Test_SPARQL_TDB.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/store/Test_SPARQL_TDB.java
@@ -17,7 +17,7 @@
 
 package org.seaborne.tdb2.store;
 
-import org.apache.jena.atlas.junit.BaseTest ;
+import static org.junit.Assert.*;
 import org.apache.jena.atlas.lib.StrUtils ;
 import org.apache.jena.graph.Graph ;
 import org.apache.jena.graph.NodeFactory ;
@@ -35,7 +35,7 @@ import org.seaborne.tdb2.TDB2Factory ;
 /**
  * Test SPARQL
  */
-public class Test_SPARQL_TDB extends BaseTest
+public class Test_SPARQL_TDB
 {
     private static Dataset create() {
         return TDB2Factory.createDataset() ;

http://git-wip-us.apache.org/repos/asf/jena/blob/7624f540/tdb2/src/test/java/org/seaborne/tdb2/store/nodetable/AbstractTestNodeTable.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/store/nodetable/AbstractTestNodeTable.java b/tdb2/src/test/java/org/seaborne/tdb2/store/nodetable/AbstractTestNodeTable.java
index 8f0c279..158e2ff 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/store/nodetable/AbstractTestNodeTable.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/store/nodetable/AbstractTestNodeTable.java
@@ -17,13 +17,16 @@
 
 package org.seaborne.tdb2.store.nodetable;
 
-import org.apache.jena.atlas.junit.BaseTest ;
+import static org.junit.Assert.assertEquals ;
+import static org.junit.Assert.assertNotEquals ;
+import static org.junit.Assert.assertNotNull ;
+
 import org.apache.jena.graph.Node ;
 import org.apache.jena.sparql.util.NodeFactoryExtra ;
 import org.junit.Test ;
 import org.seaborne.tdb2.store.NodeId ;
 
-public abstract class AbstractTestNodeTable extends BaseTest
+public abstract class AbstractTestNodeTable
 {
     protected abstract NodeTable createEmptyNodeTable() ;
     

http://git-wip-us.apache.org/repos/asf/jena/blob/7624f540/tdb2/src/test/java/org/seaborne/tdb2/store/tupletable/AbstractTestTupleIndex.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/store/tupletable/AbstractTestTupleIndex.java b/tdb2/src/test/java/org/seaborne/tdb2/store/tupletable/AbstractTestTupleIndex.java
index 42651ad..6a2e138 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/store/tupletable/AbstractTestTupleIndex.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/store/tupletable/AbstractTestTupleIndex.java
@@ -23,7 +23,7 @@ import java.util.Iterator ;
 import java.util.Set ;
 
 import org.apache.jena.atlas.iterator.Iter ;
-import org.apache.jena.atlas.junit.BaseTest ;
+import static org.junit.Assert.*;
 import org.apache.jena.atlas.lib.tuple.Tuple ;
 import org.junit.Test ;
 import org.seaborne.tdb2.store.NodeId ;
@@ -31,7 +31,7 @@ import org.seaborne.tdb2.store.tupletable.TupleIndex ;
 import static org.seaborne.tdb2.store.tupletable.NData.*;
 
 /** Test TupleIndexes (general) */
-public abstract class AbstractTestTupleIndex extends BaseTest
+public abstract class AbstractTestTupleIndex
 {
     protected abstract TupleIndex create(String description) ;
     

http://git-wip-us.apache.org/repos/asf/jena/blob/7624f540/tdb2/src/test/java/org/seaborne/tdb2/store/tupletable/TestTupleIndexRecordDirect.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/store/tupletable/TestTupleIndexRecordDirect.java b/tdb2/src/test/java/org/seaborne/tdb2/store/tupletable/TestTupleIndexRecordDirect.java
index b6b9119..a48c87b 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/store/tupletable/TestTupleIndexRecordDirect.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/store/tupletable/TestTupleIndexRecordDirect.java
@@ -24,7 +24,7 @@ import java.util.Iterator ;
 import java.util.Set ;
 
 import org.apache.jena.atlas.iterator.Iter ;
-import org.apache.jena.atlas.junit.BaseTest ;
+import static org.junit.Assert.*;
 import org.apache.jena.atlas.lib.tuple.Tuple ;
 import org.apache.jena.atlas.lib.tuple.TupleMap ;
 import org.junit.Test ;
@@ -37,7 +37,7 @@ import org.seaborne.tdb2.setup.StoreParams ;
 import org.seaborne.tdb2.store.NodeId ;
 import org.seaborne.tdb2.sys.SystemTDB ;
 
-public class TestTupleIndexRecordDirect extends BaseTest
+public class TestTupleIndexRecordDirect
 {
     static RecordFactory factory = new RecordFactory(3*SystemTDB.SizeOfNodeId, 0) ;
     

http://git-wip-us.apache.org/repos/asf/jena/blob/7624f540/tdb2/src/test/java/org/seaborne/tdb2/store/tupletable/TestTupleTable.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/store/tupletable/TestTupleTable.java b/tdb2/src/test/java/org/seaborne/tdb2/store/tupletable/TestTupleTable.java
index f82b52d..4d27266 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/store/tupletable/TestTupleTable.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/store/tupletable/TestTupleTable.java
@@ -23,7 +23,7 @@ import java.util.Iterator ;
 import java.util.List ;
 
 import org.apache.jena.atlas.iterator.Iter ;
-import org.apache.jena.atlas.junit.BaseTest ;
+import static org.junit.Assert.*;
 import org.apache.jena.atlas.lib.tuple.Tuple ;
 import org.junit.Test ;
 import org.seaborne.dboe.base.record.RecordFactory ;
@@ -31,7 +31,7 @@ import org.seaborne.tdb2.store.NodeId ;
 import org.seaborne.tdb2.sys.SystemTDB ;
 import static org.seaborne.tdb2.store.tupletable.NData.*;
 
-public class TestTupleTable extends BaseTest
+public class TestTupleTable
 {
     static RecordFactory factory = new RecordFactory(3*SystemTDB.SizeOfNodeId, 0) ;
     

http://git-wip-us.apache.org/repos/asf/jena/blob/7624f540/tdb2/src/test/java/org/seaborne/tdb2/sys/TestSys.java
----------------------------------------------------------------------
diff --git a/tdb2/src/test/java/org/seaborne/tdb2/sys/TestSys.java b/tdb2/src/test/java/org/seaborne/tdb2/sys/TestSys.java
index 0a98d8d..b537655 100644
--- a/tdb2/src/test/java/org/seaborne/tdb2/sys/TestSys.java
+++ b/tdb2/src/test/java/org/seaborne/tdb2/sys/TestSys.java
@@ -18,11 +18,11 @@
 package org.seaborne.tdb2.sys;
 
 
-import org.apache.jena.atlas.junit.BaseTest ;
+import static org.junit.Assert.*;
 import org.junit.Test ;
 import org.seaborne.tdb2.TDB2 ;
 
-public class TestSys extends BaseTest
+public class TestSys
 {
     @Test public void sys1()
     {