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 2016/12/22 17:55:07 UTC

[07/11] jena git commit: Isolation modes, inc documentation and tests.

Isolation modes, inc documentation and tests.


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

Branch: refs/heads/master
Commit: cdc9f143c599da4924f4052f9fbbd4cfcc29504d
Parents: 53758b5
Author: Andy Seaborne <an...@apache.org>
Authored: Sun Dec 18 19:55:09 2016 +0000
Committer: Andy Seaborne <an...@apache.org>
Committed: Sun Dec 18 20:04:22 2016 +0000

----------------------------------------------------------------------
 jena-rdfconnection/Documentation.md             | 23 ++++-
 .../apache/jena/rdfconnection/Isolation.java    |  5 ++
 .../jena/rdfconnection/RDFConnection.java       | 92 ++++++++++----------
 .../rdfconnection/RDFConnectionFactory.java     | 27 +++++-
 .../jena/rdfconnection/RDFConnectionLocal.java  | 77 ++++++++++------
 .../jena/rdfconnection/TS_RDFConnection.java    |  1 +
 .../jena/rdfconnection/TestLocalIsolation.java  | 84 ++++++++++++++++++
 7 files changed, 234 insertions(+), 75 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/cdc9f143/jena-rdfconnection/Documentation.md
----------------------------------------------------------------------
diff --git a/jena-rdfconnection/Documentation.md b/jena-rdfconnection/Documentation.md
index d12bf34..e64eb67 100644
--- a/jena-rdfconnection/Documentation.md
+++ b/jena-rdfconnection/Documentation.md
@@ -18,8 +18,8 @@ Protocol</a>).
 data in Java.  It provides support for try-resource and functional code
 passing styles, as well the more basic sequence of methods calls.
 
-`try-resources` to manage the connection, and two operations, one to load
-some data, and one to make a query:
+For example: using `try-resources` to manage the connection, and perform two operations, one to load
+some data, and one to make a query can be written as:
 
 ```
 try ( RDFConnection conn = RDFConnectionFactory.connect(...) ) {
@@ -113,7 +113,7 @@ handled by the transaction pattern within a single JVM.
 ## Graph Store Protocol
 
 The <a href="http://www.w3.org/TR/sparql11-http-rdf-update/">SPARQL Graph
-Store Protocol</a> is a set of operations to work on whole graphs in a
+Store Protocol</a> (GSP) is a set of operations to work on whole graphs in a
 dataset.  It provides a standardised way to manage the data in a dataset.
 
 The operations are to fetch a graph, set the RDF data in a graph,
@@ -141,6 +141,23 @@ provided).
     conn.loadDataset("data-complete.trig") ;
 ```
 
+### Local vs Remote
+
+GSP operations work on while models and datasets. When used on a remote connection, 
+the result of a GSP operation is a separate copy of the remote RDF data.  When working
+with local connections, 3 isolations modes are available:
+
+* Copy &ndash; the models and datasets returned are independent copies.
+Updates are made to the return copy only. This is most like
+a remote connectionand is useful for testing.
+* Read-only &ndash; the models and datasets are made read-only but any changes
+to the underlying RDF data by changes by another route will be visible.
+This provides a form of checking for large datasets when "copy" is impractical.
+* None &ndash; the models and datasets are passed back with no additional wrappers
+and they can be updated with the changes being made the underlying dataset.
+
+The default for a local `RDFConnection` is "none".  
+
 ## Query Usage
 
 `RDFConnection` provides methods for each of the SPARQL query forms (`SELECT`,

http://git-wip-us.apache.org/repos/asf/jena/blob/cdc9f143/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/Isolation.java
----------------------------------------------------------------------
diff --git a/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/Isolation.java b/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/Isolation.java
new file mode 100644
index 0000000..79c496f
--- /dev/null
+++ b/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/Isolation.java
@@ -0,0 +1,5 @@
+package org.apache.jena.rdfconnection;
+public enum Isolation { COPY, READONLY, NONE }
+
+// XXX Expose copy-mode?
+

http://git-wip-us.apache.org/repos/asf/jena/blob/cdc9f143/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnection.java
----------------------------------------------------------------------
diff --git a/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnection.java b/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnection.java
index c1b95ea..3adae39 100644
--- a/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnection.java
+++ b/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnection.java
@@ -18,15 +18,15 @@
 
 package org.apache.jena.rdfconnection;
 
-import java.util.function.Consumer ;
+import java.util.function.Consumer;
 
-import org.apache.jena.query.* ;
-import org.apache.jena.rdf.model.Model ;
-import org.apache.jena.sparql.core.Transactional ;
-import org.apache.jena.system.Txn ;
-import org.apache.jena.update.Update ;
-import org.apache.jena.update.UpdateFactory ;
-import org.apache.jena.update.UpdateRequest ;
+import org.apache.jena.query.*;
+import org.apache.jena.rdf.model.Model;
+import org.apache.jena.sparql.core.Transactional;
+import org.apache.jena.system.Txn;
+import org.apache.jena.update.Update;
+import org.apache.jena.update.UpdateFactory;
+import org.apache.jena.update.UpdateRequest;
 
 /**
  * Interface for SPARQL operations on a datasets, whether local or remote.
@@ -82,7 +82,7 @@ public interface RDFConnection extends
      */
     @Override
     public default void queryResultSet(String query, Consumer<ResultSet> resultSetAction) {
-        queryResultSet(QueryFactory.create(query), resultSetAction) ;
+        queryResultSet(QueryFactory.create(query), resultSetAction);
     }
     
     /**
@@ -93,14 +93,14 @@ public interface RDFConnection extends
     @Override
     public default void queryResultSet(Query query, Consumer<ResultSet> resultSetAction) {
         if ( ! query.isSelectType() )
-            throw new JenaConnectionException("Query is not a SELECT query") ;
+            throw new JenaConnectionException("Query is not a SELECT query");
 
         Txn.executeRead(this, ()->{ 
             try ( QueryExecution qExec = query(query) ) {
-                ResultSet rs = qExec.execSelect() ;
+                ResultSet rs = qExec.execSelect();
                 resultSetAction.accept(rs);
             }
-        } ) ; 
+        } ); 
     }
 
     /**
@@ -110,7 +110,7 @@ public interface RDFConnection extends
      */
     @Override
     public default void querySelect(String query, Consumer<QuerySolution> rowAction) {
-        querySelect(QueryFactory.create(query), rowAction) ;
+        querySelect(QueryFactory.create(query), rowAction);
     }
     
     /**
@@ -121,18 +121,18 @@ public interface RDFConnection extends
     @Override
     public default void querySelect(Query query, Consumer<QuerySolution> rowAction) {
         if ( ! query.isSelectType() )
-            throw new JenaConnectionException("Query is not a SELECT query") ;
+            throw new JenaConnectionException("Query is not a SELECT query");
         Txn.executeRead(this, ()->{ 
             try ( QueryExecution qExec = query(query) ) {
                 qExec.execSelect().forEachRemaining(rowAction);
             }
-        } ) ; 
+        } ); 
     }
 
     /** Execute a CONSTRUCT query and return as a Model */
     @Override
     public default Model queryConstruct(String query) {
-        return queryConstruct(QueryFactory.create(query)) ;
+        return queryConstruct(QueryFactory.create(query));
     }
     
     /** Execute a CONSTRUCT query and return as a Model */
@@ -141,15 +141,15 @@ public interface RDFConnection extends
         return 
             Txn.calculateRead(this, ()->{ 
                 try ( QueryExecution qExec = query(query) ) {
-                    return qExec.execConstruct() ;
+                    return qExec.execConstruct();
                 }
-            } ) ; 
+            } ); 
     }
 
     /** Execute a DESCRIBE query and return as a Model */
     @Override
     public default Model queryDescribe(String query) {
-        return queryDescribe(QueryFactory.create(query)) ;
+        return queryDescribe(QueryFactory.create(query));
     }
     
     /** Execute a DESCRIBE query and return as a Model */
@@ -158,15 +158,15 @@ public interface RDFConnection extends
         return 
             Txn.calculateRead(this, ()->{ 
                 try ( QueryExecution qExec = query(query) ) {
-                    return qExec.execDescribe() ;
+                    return qExec.execDescribe();
                 }
-            } ) ; 
+            } ); 
     }
     
     /** Execute a ASK query and return a boolean */
     @Override
     public default boolean queryAsk(String query) {
-        return queryAsk(QueryFactory.create(query)) ;
+        return queryAsk(QueryFactory.create(query));
     }
 
     /** Execute a ASK query and return a boolean */
@@ -175,9 +175,9 @@ public interface RDFConnection extends
         return 
             Txn.calculateRead(this, ()->{ 
                 try ( QueryExecution qExec = query(query) ) {
-                    return qExec.execAsk() ;
+                    return qExec.execAsk();
                 }
-            } ) ; 
+            } ); 
     }
 
     /** Setup a SPARQL query execution.
@@ -190,7 +190,7 @@ public interface RDFConnection extends
      * @return QueryExecution
      */
     @Override
-    public QueryExecution query(Query query) ;
+    public QueryExecution query(Query query);
 
     /** Setup a SPARQL query execution.
      * 
@@ -203,7 +203,7 @@ public interface RDFConnection extends
      */
     @Override
     public default QueryExecution query(String queryString) {
-        return query(QueryFactory.create(queryString)) ;
+        return query(QueryFactory.create(queryString));
     }
     
     // ---- SparqlUpdateConnection
@@ -214,7 +214,7 @@ public interface RDFConnection extends
      */
     @Override
     public default void update(Update update) {
-        update(new UpdateRequest(update)) ;
+        update(new UpdateRequest(update));
     }
 
     /** Execute a SPARQL Update.
@@ -222,7 +222,7 @@ public interface RDFConnection extends
      * @param update
      */
     @Override
-    public void update(UpdateRequest update) ; 
+    public void update(UpdateRequest update); 
     
     /** Execute a SPARQL Update.
      * 
@@ -230,7 +230,7 @@ public interface RDFConnection extends
      */
     @Override
     public default void update(String updateString) {
-        update(UpdateFactory.create(updateString)) ;
+        update(UpdateFactory.create(updateString));
     }
     
     // ---- RDFDatasetConnection
@@ -242,7 +242,7 @@ public interface RDFConnection extends
      * @param file File of the data.
      */
     @Override
-    public void load(String graphName, String file) ;
+    public void load(String graphName, String file);
     
     /** Load (add, append) RDF into the default graph of a dataset.
      * This is SPARQL Graph Store Protocol HTTP POST or equivalent. 
@@ -250,7 +250,7 @@ public interface RDFConnection extends
      * @param file File of the data.
      */
     @Override
-    public void load(String file) ;
+    public void load(String file);
 
     /** Load (add, append) RDF into a named graph in a dataset.
      * This is SPARQL Graph Store Protocol HTTP POST or equivalent. 
@@ -259,7 +259,7 @@ public interface RDFConnection extends
      * @param model Data.
      */
     @Override
-    public void load(String graphName, Model model) ;
+    public void load(String graphName, Model model);
     
     /** Load (add, append) RDF into the default graph of a dataset.
      * This is SPARQL Graph Store Protocol HTTP POST or equivalent. 
@@ -267,7 +267,7 @@ public interface RDFConnection extends
      * @param model Data.
      */
     @Override
-    public void load(Model model) ;
+    public void load(Model model);
 
     /** Set the contents of a named graph of a dataset.
      * Any existing data is lost. 
@@ -277,7 +277,7 @@ public interface RDFConnection extends
      * @param file File of the data.
      */
     @Override
-    public void put(String graphName, String file) ;
+    public void put(String graphName, String file);
     
     /** Set the contents of the default graph of a dataset.
      * Any existing data is lost. 
@@ -286,7 +286,7 @@ public interface RDFConnection extends
      * @param file File of the data.
      */
     @Override
-    public void put(String file) ;
+    public void put(String file);
         
     /** Set the contents of a named graph of a dataset.
      * Any existing data is lost. 
@@ -296,7 +296,7 @@ public interface RDFConnection extends
      * @param model Data.
      */
     @Override
-    public void put(String graphName, Model model) ;
+    public void put(String graphName, Model model);
     
     /** Set the contents of the default graph of a dataset.
      * Any existing data is lost. 
@@ -305,7 +305,7 @@ public interface RDFConnection extends
      * @param model Data.
      */
     @Override
-    public void put( Model model) ;
+    public void put( Model model);
         
     /**
      * Delete a graph from the dataset.
@@ -314,27 +314,27 @@ public interface RDFConnection extends
      * @param graphName
      */
     @Override
-    public void delete(String graphName) ;
+    public void delete(String graphName);
 
     /**
      * Remove all data from the default graph.
      */ 
     @Override
-    public void delete() ;
+    public void delete();
     
     /* Load (add, append) RDF triple or quad data into a dataset. Triples wil go into the default graph.
      * This is not a SPARQL Graph Store Protocol operation.
      * It is an HTTP POST equivalent to the dataset.
      */
     @Override
-    public void loadDataset(String file) ;
+    public void loadDataset(String file);
 
     /* Load (add, append) RDF triple or quad data into a dataset. Triples wil go into the default graph.
      * This is not a SPARQL Graph Store Protocol operation.
      * It is an HTTP POST equivalent to the dataset.
      */
     @Override
-    public void loadDataset(Dataset dataset) ;
+    public void loadDataset(Dataset dataset);
 
     /* Set RDF triple or quad data as the dataset contents.
      * Triples will go into the default graph, quads in named graphs.
@@ -342,7 +342,7 @@ public interface RDFConnection extends
      * It is an HTTP PUT equivalent to the dataset.
      */
     @Override
-    public void putDataset(String file) ;
+    public void putDataset(String file);
     
     /* Set RDF triple or quad data as the dataset contents.
      * Triples will go into the default graph, quads in named graphs.
@@ -350,18 +350,18 @@ public interface RDFConnection extends
      * It is an HTTP PUT equivalent to the dataset.
      */
     @Override
-    public void putDataset(Dataset dataset) ;
+    public void putDataset(Dataset dataset);
 
     //    /** Clear the dataset - remove all named graphs, clear the default graph. */
-    //    public void clearDataset() ;
+    //    public void clearDataset();
     
     
     /** Test whether this connection is closed or not */
     @Override
-    public boolean isClosed() ;
+    public boolean isClosed();
     
     /** Close this connection.  Use with try-resource. */ 
     @Override 
-    public void close() ;
+    public void close();
 }
 

http://git-wip-us.apache.org/repos/asf/jena/blob/cdc9f143/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnectionFactory.java
----------------------------------------------------------------------
diff --git a/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnectionFactory.java b/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnectionFactory.java
index e7cfb57..a8c0e45 100644
--- a/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnectionFactory.java
+++ b/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnectionFactory.java
@@ -77,11 +77,36 @@ public class RDFConnectionFactory {
 
     /**
      * Connect to a local (same JVM) dataset.
+     * The default isolation is {@code NONE}. 
+     * See {@link #connect(Dataset, Isolation)} to select an isolation mode.
+     * 
      * @param dataset
      * @return RDFConnection
+     * @see RDFConnectionLocal
      */
     public static RDFConnection connect(Dataset dataset) {
         return new RDFConnectionLocal(dataset);
     }
-
+    
+    /**
+     * Connect to a local (same JVM) dataset.
+     * <p>
+     * Multiple levels of {@link Isolation} are provided, The default {@code COPY} level makes a local
+     * {@link RDFConnection} behave like a remote conenction.
+     * See <a href="https://jena.apache.org/documentation/rdfconnection/">the documentation for more details.</a>
+     * <ul>
+     * <li>{@code COPY} &ndash; {@code Model}s and {@code Dataset}s are copied. 
+     *     This is most like a remote connection.
+     * <li>{@code READONLY} &ndash; Read-only wrappers are added but changes to
+     *     the underlying model or dataset will be seen.
+     * <li>{@code NONE} (default) &ndash; Changes to the returned {@code Model}s or {@code Dataset}s act on the original object.
+     * </ul>
+     * 
+     * @param dataset
+     * @param isolation
+     * @return RDFConnection
+     */
+    public static RDFConnection connect(Dataset dataset, Isolation isolation) {
+        return new RDFConnectionLocal(dataset, isolation);
+    }
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/cdc9f143/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnectionLocal.java
----------------------------------------------------------------------
diff --git a/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnectionLocal.java b/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnectionLocal.java
index d2fb4df..8be2704 100644
--- a/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnectionLocal.java
+++ b/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnectionLocal.java
@@ -20,6 +20,7 @@ package org.apache.jena.rdfconnection;
 
 import java.util.Objects;
 
+import org.apache.jena.atlas.lib.InternalErrorException;
 import org.apache.jena.graph.Graph;
 import org.apache.jena.query.*;
 import org.apache.jena.rdf.model.Model;
@@ -36,21 +37,36 @@ import org.apache.jena.system.Txn;
 import org.apache.jena.update.UpdateExecutionFactory;
 import org.apache.jena.update.UpdateRequest;
 
-/** 
+/**
  * Implement of {@link RDFConnection} over a {@link Dataset} in the same JVM.
+ * <p>
+ * Multiple levels of {@link Isolation} are provided, The default {@code COPY} level makes a local
+ * {@link RDFConnection} behave like a remote conenction. This should be the normal use in
+ * testing.
+ * <ul>
+ * <li>{@code COPY} &ndash; {@code Model}s and {@code Dataset}s are copied. 
+ *     This is most like a remote connection.
+ * <li>{@code READONLY} &ndash; Read-only wrappers are added but changes to
+ *     the underlying model or dataset will be seen.
+ * <li>{@code NONE} (default) &ndash; Changes to the returned {@code Model}s or {@code Dataset}s act on the original object.
+ * </ul>
  */
 
 public class RDFConnectionLocal implements RDFConnection {
-    // XXX Expose copy-mode?
-    
     private ThreadLocal<Boolean> transactionActive = ThreadLocal.withInitial(()->false);
-    private static boolean isolateByCopy = true; 
+    
     private Dataset dataset;
+    private final Isolation isolation;
     
     public RDFConnectionLocal(Dataset dataset) {
-        this.dataset = dataset;
+        this(dataset, Isolation.NONE);
     }
     
+    public RDFConnectionLocal(Dataset dataset, Isolation isolation) {
+        this.dataset = dataset;
+        this.isolation = isolation;
+    }
+
     @Override
     public QueryExecution query(Query query) {
         checkOpen();
@@ -176,35 +192,46 @@ public class RDFConnectionLocal implements RDFConnection {
     }
 
     /**
-     * Called to isolate a model from it's storage. Must be inside a
-     * transaction.
+     * Called to isolate a model from it's storage.
+     * Must be inside a transaction.
      */
     private Model isolate(Model model) {
-        if ( ! isolateByCopy ) {
-            // Half-way - read-only but dataset changes can be seen. 
-            Graph g = new GraphReadOnly(model.getGraph());
-            return ModelFactory.createModelForGraph(g);
+        switch(isolation) {
+            case COPY: {
+                // Copy - the model is completely isolated from the original. 
+                Model m2 = ModelFactory.createDefaultModel();
+                m2.add(model);
+                return m2;
+            }
+            case READONLY : {
+                Graph g = new GraphReadOnly(model.getGraph());
+                return ModelFactory.createModelForGraph(g);
+            }
+            case NONE :
+                return model;
         }
-        // Copy.
-        Model m2 = ModelFactory.createDefaultModel();
-        m2.add(model);
-        return m2;
+        throw new InternalErrorException();
     }
 
     /**
-     * Called to isolate a dataset from it's storage. Must be inside a
-     * transaction.
+     * Called to isolate a dataset from it's storage.
+     * Must be inside a transaction.
      */
     private Dataset isolate(Dataset dataset) {
-        if ( ! isolateByCopy ) {
-            DatasetGraph dsg = new DatasetGraphReadOnly(dataset.asDatasetGraph());
-            return DatasetFactory.wrap(dsg);
+        switch(isolation) {
+            case COPY: {
+                DatasetGraph dsg2 = DatasetGraphFactory.create();
+                dataset.asDatasetGraph().find().forEachRemaining(q -> dsg2.add(q) );
+                return DatasetFactory.wrap(dsg2);
+            }
+            case READONLY : {
+                DatasetGraph dsg = new DatasetGraphReadOnly(dataset.asDatasetGraph());
+                return DatasetFactory.wrap(dsg);
+            }
+            case NONE :
+                return dataset;
         }
-
-        // Copy.
-        DatasetGraph dsg2 = DatasetGraphFactory.create();
-        dataset.asDatasetGraph().find().forEachRemaining(q -> dsg2.add(q) );
-        return DatasetFactory.wrap(dsg2);
+        throw new InternalErrorException();
     }
 
     private Model modelFor(String graph) {

http://git-wip-us.apache.org/repos/asf/jena/blob/cdc9f143/jena-rdfconnection/src/test/java/org/apache/jena/rdfconnection/TS_RDFConnection.java
----------------------------------------------------------------------
diff --git a/jena-rdfconnection/src/test/java/org/apache/jena/rdfconnection/TS_RDFConnection.java b/jena-rdfconnection/src/test/java/org/apache/jena/rdfconnection/TS_RDFConnection.java
index 3f8af53..373bd52 100644
--- a/jena-rdfconnection/src/test/java/org/apache/jena/rdfconnection/TS_RDFConnection.java
+++ b/jena-rdfconnection/src/test/java/org/apache/jena/rdfconnection/TS_RDFConnection.java
@@ -26,6 +26,7 @@ import org.junit.runners.Suite;
     // Other tests are in jena-integration-tests
     TestRDFConnectionLocalTxnMem.class
     , TestRDFConnectionLocalMRSW.class
+    , TestLocalIsolation.class
 })
 
 public class TS_RDFConnection {}

http://git-wip-us.apache.org/repos/asf/jena/blob/cdc9f143/jena-rdfconnection/src/test/java/org/apache/jena/rdfconnection/TestLocalIsolation.java
----------------------------------------------------------------------
diff --git a/jena-rdfconnection/src/test/java/org/apache/jena/rdfconnection/TestLocalIsolation.java b/jena-rdfconnection/src/test/java/org/apache/jena/rdfconnection/TestLocalIsolation.java
new file mode 100644
index 0000000..bc4fc20
--- /dev/null
+++ b/jena-rdfconnection/src/test/java/org/apache/jena/rdfconnection/TestLocalIsolation.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.rdfconnection;
+
+import static org.junit.Assert.assertEquals;
+
+import org.apache.jena.query.Dataset;
+import org.apache.jena.query.DatasetFactory;
+import org.apache.jena.rdf.model.*;
+import org.apache.jena.shared.JenaException;
+import org.apache.jena.sparql.core.Quad;
+import org.apache.jena.sparql.sse.SSE;
+import org.junit.Test;
+
+public class TestLocalIsolation {
+
+    private static Resource subject = ResourceFactory.createResource();
+    private static Property property = ResourceFactory.createProperty("http://example/p");
+    private static Resource object = ResourceFactory.createResource("http://example/o");
+    
+    @Test public void localIsolation_model_1() {
+        isolationModel(Isolation.COPY,false);
+    }
+
+    @Test public void localIsolation_model_2() {
+        isolationModel(Isolation.NONE, true);
+    }
+
+    @Test(expected=JenaException.class)
+    public void localIsolation_model_3() {
+        isolationModel(Isolation.READONLY, true);
+    }
+
+    @Test public void localIsolation_dataset_1() {
+        isolationDataset(Isolation.COPY,false);
+    }
+
+    @Test public void localIsolation_dataset_2() {
+        isolationDataset(Isolation.NONE,true);
+    }
+
+    @Test(expected=UnsupportedOperationException.class)
+    public void localIsolation_dataset_3() {
+        isolationDataset(Isolation.READONLY, true);
+    }
+
+    private void isolationDataset(Isolation isolation, boolean expected) {
+        Dataset base = DatasetFactory.createTxnMem();
+        RDFConnection conn1 = RDFConnectionFactory.connect(base, isolation);
+        Quad quad = SSE.parseQuad("(:g :s :p :o)") ; 
+        try (RDFConnection conn2 = conn1;) {
+            Dataset ds = conn2.fetchDataset();
+            ds.asDatasetGraph().add(quad);
+        }
+        assertEquals(expected, base.asDatasetGraph().contains(quad));
+    }
+
+    private void isolationModel(Isolation level, boolean expected) {
+        Dataset base = DatasetFactory.createTxnMem();
+        Statement stmt = base.getDefaultModel().createStatement(subject, property, object); 
+        RDFConnection conn1 = RDFConnectionFactory.connect(base, level);
+        try (RDFConnection conn2 = conn1;) {
+            Model m = conn2.fetch();
+            m.add(stmt);
+        }
+        assertEquals(expected, base.getDefaultModel().contains(stmt));
+    }
+}