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/10/03 19:34:01 UTC

[15/65] [abbrv] jena git commit: JENA-1397: Rename java packages

http://git-wip-us.apache.org/repos/asf/jena/blob/3d456654/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/value/DoubleNode62.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/value/DoubleNode62.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/value/DoubleNode62.java
new file mode 100644
index 0000000..3a98d7e
--- /dev/null
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/value/DoubleNode62.java
@@ -0,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.apache.jena.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;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jena/blob/3d456654/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/value/FloatNode.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/value/FloatNode.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/value/FloatNode.java
new file mode 100644
index 0000000..2bf9d86
--- /dev/null
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/value/FloatNode.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.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/3d456654/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/value/IntegerNode.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/value/IntegerNode.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/value/IntegerNode.java
new file mode 100644
index 0000000..ac7b644
--- /dev/null
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/value/IntegerNode.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.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/3d456654/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/ConnectionTracker.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/ConnectionTracker.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/ConnectionTracker.java
new file mode 100644
index 0000000..98287ed
--- /dev/null
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/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.apache.jena.tdb2.sys;
+
+abstract class ConnectionTracker<X> {
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/3d456654/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/CopyDSG.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/CopyDSG.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/CopyDSG.java
new file mode 100644
index 0000000..bb9f846
--- /dev/null
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/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.apache.jena.tdb2.sys;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.jena.atlas.iterator.Iter;
+import org.apache.jena.dboe.jenax.Txn;
+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;
+
+/** 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/3d456654/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/DatabaseConnection.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/DatabaseConnection.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/DatabaseConnection.java
new file mode 100644
index 0000000..69d1499
--- /dev/null
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/DatabaseConnection.java
@@ -0,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.apache.jena.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.dboe.base.file.Location;
+import org.apache.jena.dboe.base.file.ProcessFileLock;
+import org.apache.jena.dboe.sys.Names;
+import org.apache.jena.sparql.core.DatasetGraph ;
+import org.apache.jena.tdb2.TDBException;
+import org.apache.jena.tdb2.setup.StoreParams;
+import org.apache.jena.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/3d456654/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/DatabaseOps.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/DatabaseOps.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/DatabaseOps.java
new file mode 100644
index 0000000..86651f9
--- /dev/null
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/DatabaseOps.java
@@ -0,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.apache.jena.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.dboe.base.file.Location;
+import org.apache.jena.dboe.jenax.Txn;
+import org.apache.jena.dboe.transaction.txn.TransactionCoordinator;
+import org.apache.jena.dboe.transaction.txn.TransactionalSystem;
+import org.apache.jena.riot.Lang ;
+import org.apache.jena.riot.RDFDataMgr ;
+import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.tdb2.TDBException;
+import org.apache.jena.tdb2.setup.StoreParams;
+import org.apache.jena.tdb2.store.DatasetGraphSwitchable;
+import org.apache.jena.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/3d456654/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/EnvTDB.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/EnvTDB.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/EnvTDB.java
new file mode 100644
index 0000000..2c15d7d
--- /dev/null
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/EnvTDB.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.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.apache.jena.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/3d456654/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/FilenameUtils.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/FilenameUtils.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/FilenameUtils.java
new file mode 100644
index 0000000..3d3af62
--- /dev/null
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/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.apache.jena.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.apache.jena.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/3d456654/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/IOX.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/IOX.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/IOX.java
new file mode 100644
index 0000000..a38f8d0
--- /dev/null
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/IOX.java
@@ -0,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.apache.jena.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.apache.jena.dboe.DBOpEnvException;
+import org.apache.jena.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.apache.jena.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/3d456654/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/InitTDB2.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/InitTDB2.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/InitTDB2.java
new file mode 100644
index 0000000..4888533
--- /dev/null
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/InitTDB2.java
@@ -0,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.apache.jena.tdb2.sys;
+
+import org.apache.jena.system.JenaSubsystemLifecycle ;
+import org.apache.jena.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/3d456654/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/LockMRSWLite.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/LockMRSWLite.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/LockMRSWLite.java
new file mode 100644
index 0000000..43aa467
--- /dev/null
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/LockMRSWLite.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.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/3d456654/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/StoreConnection.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/StoreConnection.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/StoreConnection.java
new file mode 100644
index 0000000..7082bcf
--- /dev/null
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/StoreConnection.java
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.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.dboe.base.file.ChannelManager;
+import org.apache.jena.dboe.base.file.Location;
+import org.apache.jena.dboe.base.file.ProcessFileLock;
+import org.apache.jena.dboe.sys.Names;
+import org.apache.jena.dboe.transaction.txn.TransactionCoordinator;
+import org.apache.jena.dboe.transaction.txn.TransactionException;
+import org.apache.jena.sparql.core.DatasetGraph ;
+import org.apache.jena.tdb2.setup.StoreParams;
+import org.apache.jena.tdb2.setup.TDBBuilder;
+import org.apache.jena.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/3d456654/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/SystemTDB.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/SystemTDB.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/SystemTDB.java
new file mode 100644
index 0000000..43fae0b
--- /dev/null
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/sys/SystemTDB.java
@@ -0,0 +1,417 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.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.dboe.base.block.FileMode;
+import org.apache.jena.dboe.base.file.ProcessFileLock;
+import org.apache.jena.dboe.base.record.RecordFactory;
+import org.apache.jena.dboe.sys.Sys;
+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.apache.jena.tdb2.TDB2;
+import org.apache.jena.tdb2.TDBException;
+import org.apache.jena.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 ;
+//    }
+
+}