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

[01/27] jena git commit: JENA-1594: Initial machinery with SPARQL Query filtering

Repository: jena
Updated Branches:
  refs/heads/master 0a0b831b3 -> c8a31476d


http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterLocal.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterLocal.java b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterLocal.java
new file mode 100644
index 0000000..3c4ead4
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterLocal.java
@@ -0,0 +1,320 @@
+/*
+ * 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.fuseki.access;
+
+import static org.apache.jena.fuseki.access.GraphData.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+
+import org.apache.jena.atlas.iterator.Iter;
+import org.apache.jena.atlas.lib.Creator;
+import org.apache.jena.atlas.lib.SetUtils;
+import org.apache.jena.fuseki.access.DataAccessCtl;
+import org.apache.jena.fuseki.access.SecurityPolicy;
+import org.apache.jena.fuseki.access.SecurityRegistry;
+import org.apache.jena.graph.Graph;
+import org.apache.jena.graph.Node;
+import org.apache.jena.query.Dataset;
+import org.apache.jena.query.DatasetFactory;
+import org.apache.jena.query.QueryExecution;
+import org.apache.jena.query.QueryExecutionFactory;
+import org.apache.jena.query.QuerySolution;
+import org.apache.jena.rdf.model.Model;
+import org.apache.jena.rdf.model.ModelFactory;
+import org.apache.jena.rdf.model.RDFNode;
+import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.core.Quad;
+import org.apache.jena.system.Txn;
+import org.apache.jena.tdb.TDB;
+import org.apache.jena.tdb.TDBFactory;
+import org.apache.jena.tdb2.DatabaseMgr;
+import org.apache.jena.tdb2.TDB2;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TestSecurityFilterLocal {
+    @Parameters(name = "{index}: {0}")
+    public static Iterable<Object[]> data() {
+        Creator<DatasetGraph> c1 = TDBFactory::createDatasetGraph;
+        Object[] obj1 = { "TDB", c1};
+        Creator<DatasetGraph> c2 = DatabaseMgr::createDatasetGraph;
+        Object[] obj2 = { "TDB2", c2 };
+        return Arrays.asList(obj1, obj2);
+    }
+    
+    private DatasetGraph testdsg;
+    private SecurityRegistry reg = new SecurityRegistry();
+    
+    public TestSecurityFilterLocal(String name, Creator<DatasetGraph> source) {
+        testdsg = source.create();
+        fill(testdsg);
+        reg.put("userNone", SecurityPolicy.NONE);
+        reg.put("userDft", SecurityPolicy.DFT_GRAPH);
+        reg.put("user0", new SecurityPolicy(Quad.defaultGraphIRI.getURI()));
+        reg.put("user1", new SecurityPolicy("http://test/g1", Quad.defaultGraphIRI.getURI()));
+        reg.put("user2", new SecurityPolicy("http://test/g1", "http://test/g2", "http://test/g3"));
+        DataAccessCtl.controlledDataset(testdsg, reg);
+        //testdsg = DataAccessCtl.wrapControlledDataset(testdsg, reg);
+    }
+    
+    private static void assertSeen(Set<Node> visible, Node ... expected) {
+        Set<Node> expectedNodes = new HashSet<>(Arrays.asList(expected));
+        assertEquals(expectedNodes, visible);
+    }
+
+    private static String queryAll        = "SELECT * { { ?s ?p ?o } UNION { GRAPH ?g { ?s ?p ?o } } }";
+    private static String queryDft        = "SELECT * { ?s ?p ?o }";
+    private static String queryNamed      = "SELECT * { GRAPH ?g { ?s ?p ?o } }";
+
+    private static String queryG2         = "SELECT * { GRAPH <http://test/graph2> { ?s ?p ?o } }";
+    private static String queryGraphNames = "SELECT * { GRAPH ?g { } }";
+
+    private Set<Node> subjects(DatasetGraph dsg, String queryString, Consumer<QueryExecution> modifier) {
+        Dataset ds = DatasetFactory.wrap(dsg);
+        return
+            Txn.calculateRead(ds, ()->{
+                try(QueryExecution qExec = QueryExecutionFactory.create(queryString, ds)) {
+                    if ( modifier != null )
+                        modifier.accept(qExec);
+                    List<QuerySolution> results = Iter.toList(qExec.execSelect());
+                    Stream<Node> stream = results.stream()
+                        .map(qs->qs.get("s"))
+                        .filter(Objects::nonNull)
+                        .map(RDFNode::asNode);
+                    return SetUtils.toSet(stream);
+                }
+            });
+    }
+    
+    private Set<Node> graphs(DatasetGraph dsg, Consumer<QueryExecution> modifier) {
+        Dataset ds = DatasetFactory.wrap(dsg);
+        return
+            Txn.calculateRead(ds, ()->{
+                try(QueryExecution qExec = QueryExecutionFactory.create(queryGraphNames, ds)) {
+                    if ( modifier != null )
+                        modifier.accept(qExec);
+                    List<QuerySolution> results = Iter.toList(qExec.execSelect());
+                    Stream<Node> stream = results.stream().map(qs->qs.get("g")).filter(Objects::nonNull).map(RDFNode::asNode);
+                    return SetUtils.toSet(stream);
+                }
+            });
+    }
+
+    @Test public void filter_setup() {
+        Set<Node> visible = subjects(testdsg, queryAll, null);
+        assertEquals(5, visible.size());
+        assertSeen(visible, s0, s1, s2, s3, s4);
+    }
+
+    private static Consumer<QueryExecution> qExecAddFiler(DatasetGraph dsg, SecurityPolicy sCxt) {
+        return qExec->sCxt.filterTDB(dsg, qExec);
+    }
+
+    // QueryExecution
+    private void filter_user(String user, Node ... expected) {
+        SecurityPolicy sCxt = reg.get(user);
+        Set<Node> visible = subjects(testdsg, queryAll, qExecAddFiler(testdsg, sCxt));
+        assertSeen(visible, expected);
+    }
+
+    @Test public void filter_userNone() {
+        filter_user("userNone");
+    }
+
+    @Test public void filter_userDft() {
+        filter_user("userDft", s0);
+    }
+
+    @Test public void filter_user0() {
+        filter_user("user0", s0);
+    }
+
+    @Test public void filter_user1() {
+        filter_user("user1", s0, s1);
+    }
+
+    @Test public void filter_user2() {
+        filter_user("user2", s1, s2, s3);
+    }
+
+    @Test public void filter_userX() {
+        filter_user("userX");
+    }
+
+    // "Access Denied"
+    @Test public void no_access_user1() {
+        SecurityPolicy sCxt = reg.get("user1");
+        Set<Node> visible = subjects(testdsg, queryG2, qExecAddFiler(testdsg, sCxt));
+        assertTrue(visible.isEmpty());
+    }
+
+    @Test public void graph_names_userNone() {
+        SecurityPolicy sCxt = reg.get("userNone");
+        Set<Node> visible = graphs(testdsg, qExecAddFiler(testdsg, sCxt)); 
+        assertSeen(visible);
+    }
+    
+    @Test public void graph_names_userDft() {
+        SecurityPolicy sCxt = reg.get("userDft");
+        Set<Node> visible = graphs(testdsg, qExecAddFiler(testdsg, sCxt)); 
+        assertSeen(visible);
+    }
+    
+    @Test public void graph_names_user0() {
+        SecurityPolicy sCxt = reg.get("user0");
+        Set<Node> visible = graphs(testdsg, qExecAddFiler(testdsg, sCxt)); 
+        assertSeen(visible);
+    }
+    
+    @Test public void graph_names_user1() {
+        SecurityPolicy sCxt = reg.get("user1");
+        Set<Node> visible = graphs(testdsg, qExecAddFiler(testdsg, sCxt)); 
+        assertSeen(visible, g1);
+    }
+
+    @Test public void graph_names_user2() {
+        SecurityPolicy sCxt = reg.get("user2");
+        Set<Node> visible = graphs(testdsg, qExecAddFiler(testdsg, sCxt)); 
+        assertSeen(visible, g1, g2, g3);
+    }
+
+    @Test public void graph_names_userX() {
+        SecurityPolicy sCxt = reg.get("userX");
+        Set<Node> visible = graphs(testdsg, qExecAddFiler(testdsg, sCxt)); 
+        assertSeen(visible);
+    }
+
+    // QueryExecution w/ Union default graph
+    private void filter_union_user(String user, Node ... expected) {
+        SecurityPolicy sCxt = reg.get(user);
+        Consumer<QueryExecution> modifier = qExec-> {
+            qExec.getContext().set(TDB.symUnionDefaultGraph, true);
+            qExec.getContext().set(TDB2.symUnionDefaultGraph, true);    // Not strictly necessary.
+            sCxt.filterTDB(testdsg, qExec); 
+        };
+        Set<Node> visible = subjects(testdsg, queryDft, modifier);
+        assertSeen(visible, expected);
+    }
+    
+    @Test public void filter_union_userNone() {
+        filter_union_user("userNone");
+    }
+    
+    @Test public void filter_union_userDft() {
+        // Storage default graph not visible with a union query.
+        filter_union_user("userDft");
+    }
+
+    @Test public void filter_union_user0() {
+        // Storage default graph not visible with a union query.
+        filter_union_user("user0");
+    }
+    
+    @Test public void filter_union_user1() {
+        filter_union_user("user1", s1);
+    }
+    
+    @Test public void filter_union_user2() {
+        filter_union_user("user2", s1, s2, s3);
+    }
+    
+    @Test public void filter_union_userX() {
+        filter_union_user("userX");
+    }
+    
+    private Set<Node> subjects(Graph graph, String queryString, Consumer<QueryExecution> modifier) {
+        Model model = ModelFactory.createModelForGraph(graph);
+        return
+            Txn.calculateRead(testdsg, ()->{
+                try(QueryExecution qExec = QueryExecutionFactory.create(queryString, model)) {
+                    if ( modifier != null )
+                        modifier.accept(qExec);
+                    List<QuerySolution> results = Iter.toList(qExec.execSelect());
+                    Stream<Node> stream = results.stream().map(qs->qs.get("s")).filter(Objects::nonNull).map(RDFNode::asNode);
+                    return SetUtils.toSet(stream);
+                }
+            });
+    }
+
+    // Graph/Model
+    @Test public void query_model_userNone() {
+        query_model_user(testdsg.getDefaultGraph(), "userNone");
+    }
+    
+    @Test public void query_model_userDft() {
+        query_model_user(testdsg.getDefaultGraph(), "userDft", s0);
+    }
+
+    @Test public void query_model_user0() {
+        query_model_user(testdsg.getDefaultGraph(), "user0", s0);
+    }
+
+    @Test public void query_model_user1() {
+        query_model_user(testdsg.getDefaultGraph(), "user1", s0);
+    }
+
+    @Test public void query_model_user2() {
+        query_model_user(testdsg.getDefaultGraph(), "user2");
+    }
+
+    @Test public void query_model_ng_userNone() {
+        query_model_user(testdsg.getGraph(g1), "userNone");
+    }
+
+    @Test public void query_model_ng_user11() {
+        query_model_user(testdsg.getGraph(g1), "user1", s1);
+    }
+
+    @Test public void query_model_ng_user21() {
+        query_model_user(testdsg.getGraph(g1), "user2", s1);
+    }
+
+    @Test public void query_model_ng_user12() {
+        query_model_user(testdsg.getGraph(g2), "user1");
+    }
+
+    @Test public void query_model_ng_user22() {
+        query_model_user(testdsg.getGraph(g2), "user2", s2);
+    }
+    
+    @Test public void query_model_userXa() {
+        query_model_user(testdsg.getDefaultGraph(), "userX");
+    }
+
+    @Test public void query_model_userXb() {
+        query_model_user(testdsg.getGraph(g1), "userX");
+    }
+
+    private void query_model_user(Graph g, String user, Node ... expected) {
+        SecurityPolicy sCxt = reg.get(user);
+        Set<Node> visible = subjects(g, queryDft, qExecAddFiler(testdsg, sCxt));
+        assertSeen(visible, expected);
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/jena-fuseki2/jena-fuseki-access/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/test/resources/log4j.properties b/jena-fuseki2/jena-fuseki-access/src/test/resources/log4j.properties
new file mode 100644
index 0000000..e84e60e
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/test/resources/log4j.properties
@@ -0,0 +1,40 @@
+# Licensed under the terms of http://www.apache.org/licenses/LICENSE-2.0
+
+# Plain output to stdout
+log4j.appender.jena.plainstdout=org.apache.log4j.ConsoleAppender
+log4j.appender.jena.plainstdout.target=System.out
+log4j.appender.jena.plainstdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.jena.plainstdout.layout.ConversionPattern=[%d{yyyy-MM-dd HH:mm:ss}] %-10c{1} %-5p %m%n
+## %d{ISO8601} -- includes "ss,sss"
+## log4j.appender.jena.plainstdout.layout.ConversionPattern=[%d{ISO8601}] %-10c{1} %-5p %m%n
+
+# Unadorned, for the NCSA requests log.
+log4j.appender.fuseki.plain=org.apache.log4j.ConsoleAppender
+log4j.appender.fuseki.plain.target=System.out
+log4j.appender.fuseki.plain.layout=org.apache.log4j.PatternLayout
+log4j.appender.fuseki.plain.layout.ConversionPattern=%m%n
+
+log4j.rootLogger=INFO, jena.plainstdout
+log4j.logger.org.apache.jena=WARN
+log4j.logger.org.apache.jena.fuseki=INFO
+
+# Others
+log4j.logger.org.eclipse.jetty=WARN
+log4j.logger.org.apache.shiro=WARN
+
+# Fuseki System logs.
+log4j.logger.org.apache.jena.fuseki.Server=INFO
+log4j.logger.org.apache.jena.fuseki.Fuseki=INFO
+log4j.logger.org.apache.jena.fuseki.Admin=INFO
+log4j.logger.org.apache.jena.fuseki.Validate=INFO
+log4j.logger.org.apache.jena.fuseki.Config=INFO
+
+# NCSA Request log.
+log4j.additivity.org.apache.jena.fuseki.Request=false
+log4j.logger.org.apache.jena.fuseki.Request=OFF, fuseki.plain
+
+# TDB
+log4j.logger.org.apache.jena.tdb.loader=INFO
+## Parser output
+log4j.additivity.org.apache.jena.riot=false
+log4j.logger.org.apache.jena.riot=INFO, jena.plainstdout

http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/jena-fuseki2/jena-fuseki-access/testing/Access/assem-security-shared.ttl
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/testing/Access/assem-security-shared.ttl b/jena-fuseki2/jena-fuseki-access/testing/Access/assem-security-shared.ttl
new file mode 100644
index 0000000..5372c81
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/testing/Access/assem-security-shared.ttl
@@ -0,0 +1,58 @@
+PREFIX :        <#>
+PREFIX fuseki:  <http://jena.apache.org/fuseki#>
+PREFIX rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
+PREFIX rdfs:    <http://www.w3.org/2000/01/rdf-schema#>
+PREFIX tdb2:    <http://jena.apache.org/2016/tdb#>
+PREFIX ja:      <http://jena.hpl.hp.com/2005/11/Assembler#>
+PREFIX access:  <http://jena.apache.org/access#>
+
+[] rdf:type fuseki:Server ;
+   fuseki:services (
+     <#service_tdb2>
+     <#service_plain>
+   ) .
+
+<#service_tdb2> rdf:type fuseki:Service ;
+    rdfs:label                      "Access controlled dataset" ;
+    fuseki:name                     "database" ;
+    ## Read-only operations.
+    fuseki:serviceQuery             "query" ;
+    fuseki:serviceQuery             "sparql" ;
+    fuseki:serviceReadGraphStore    "get" ;
+    fuseki:dataset                  <#access_dataset>;
+    .
+
+## Dataset 1
+## Access control
+<#access_dataset>  rdf:type access:AccessControlledDataset ;
+    access:registry   <#securityRegistry> ;
+    access:dataset    <#tdb_dataset_shared> ;
+    .
+
+<#securityRegistry> rdf:type access:SecurityRegistry ;
+    access:entry ("user1" <http://host/graphname1>  <http://host/graphname2> ) ;
+    access:entry ("user1" <http://host/graphname3> ) ;
+    access:entry ("user2" <http://host/graphname9> ) ;
+    access:entry [ access:user "user3" ; access:graphs (<http://host/graphname3> <http://host/graphname4> ) ] ;
+    access:entry [ access:user "user3" ; access:graphs <http://host/graphname5> ] ;
+    access:entry [ access:user "userZ" ; access:graphs <http://host/graphnameZ> ] ;
+    .
+## Dataset 2
+## No data access control.
+<#service_plain> rdf:type fuseki:Service ;
+    fuseki:name                  "plain";
+    fuseki:serviceQuery          "query";
+    fuseki:serviceQuery          "sparql";
+    fuseki:serviceUpdate         "update";
+    fuseki:serviceUpload         "upload" ;
+    fuseki:serviceReadGraphStore "data" ;
+    fuseki:serviceReadGraphStore "get" ;
+    fuseki:dataset <#tdb_dataset_shared> ;
+    .
+
+## Shared database.
+<#tdb_dataset_shared> rdf:type      tdb2:DatasetTDB2 ;
+    tdb2:location "--mem--/DB" ;
+    tdb2:unionDefaultGraph true ;
+    .
+

http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/jena-fuseki2/jena-fuseki-access/testing/Access/assem-security.ttl
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/testing/Access/assem-security.ttl b/jena-fuseki2/jena-fuseki-access/testing/Access/assem-security.ttl
new file mode 100644
index 0000000..8d04b8e
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/testing/Access/assem-security.ttl
@@ -0,0 +1,63 @@
+PREFIX :        <#>
+PREFIX fuseki:  <http://jena.apache.org/fuseki#>
+PREFIX rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
+PREFIX rdfs:    <http://www.w3.org/2000/01/rdf-schema#>
+PREFIX tdb2:    <http://jena.apache.org/2016/tdb#>
+PREFIX ja:      <http://jena.hpl.hp.com/2005/11/Assembler#>
+PREFIX access:  <http://jena.apache.org/access#>
+
+[] rdf:type fuseki:Server ;
+   fuseki:services (
+     <#service_tdb2>
+     <#service_plain>
+   ) .
+
+<#service_tdb2> rdf:type fuseki:Service ;
+    rdfs:label                      "Access controlled dataset" ;
+    fuseki:name                     "database" ;
+    ## Read-only operations.
+    fuseki:serviceQuery             "query" ;
+    fuseki:serviceQuery             "sparql" ;
+    fuseki:serviceReadGraphStore    "get" ;
+    fuseki:dataset                  <#access_dataset>;
+    .
+
+## Dataset 1
+## Access control
+<#access_dataset>  rdf:type access:AccessControlledDataset ;
+    access:registry   <#securityRegistry> ;
+    access:dataset    <#tdb_dataset_read> ;
+    .
+
+## Own database
+<#tdb_dataset_read> rdf:type      tdb2:DatasetTDB2 ;
+    tdb2:location "--mem--" ;
+    tdb2:unionDefaultGraph true ;
+    .
+
+<#securityRegistry> rdf:type access:SecurityRegistry ;
+    access:entry ("user1" <http://host/graphname1>  <http://host/graphname2> ) ;
+    access:entry ("user1" <http://host/graphname3> ) ;
+    access:entry ("user2" <http://host/graphname9> ) ;
+    access:entry [ access:user "user3" ; access:graphs (<http://host/graphname3> <http://host/graphname4> ) ] ;
+    access:entry [ access:user "user3" ; access:graphs <http://host/graphname5> ] ;
+    access:entry [ access:user "userZ" ; access:graphs <http://host/graphnameZ> ] ;
+    .
+
+## Dataset 2
+## No data access control.
+<#service_plain> rdf:type fuseki:Service ;
+    fuseki:name                  "plain";
+    fuseki:serviceQuery          "query";
+    fuseki:serviceQuery          "sparql";
+    fuseki:serviceUpdate         "update";
+    fuseki:serviceUpload         "upload" ;
+    fuseki:serviceReadGraphStore "data" ;
+    fuseki:serviceReadGraphStore "get" ;
+    fuseki:dataset <#tdb_dataset> ;
+    .
+    
+<#tdb_dataset> rdf:type      tdb2:DatasetTDB2 ;
+    tdb2:location "--mem--" ;
+    .
+    
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/jena-fuseki2/jena-fuseki-core/pom.xml
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/pom.xml b/jena-fuseki2/jena-fuseki-core/pom.xml
index fe62823..f49d4b5 100644
--- a/jena-fuseki2/jena-fuseki-core/pom.xml
+++ b/jena-fuseki2/jena-fuseki-core/pom.xml
@@ -106,11 +106,13 @@
     <dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-log4j12</artifactId>
+      <optional>true</optional>
     </dependency>
 
     <dependency>
       <groupId>log4j</groupId>
       <artifactId>log4j</artifactId>
+      <optional>true</optional>
     </dependency>
 
     <!-- Testing -->

http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/jetty/JettyLib.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/jetty/JettyLib.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/jetty/JettyLib.java
new file mode 100644
index 0000000..0d03ab4
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/jetty/JettyLib.java
@@ -0,0 +1,169 @@
+/*
+ * 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.fuseki.jetty;
+
+import java.util.Objects;
+
+import org.apache.jena.riot.WebContent;
+import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.security.*;
+import org.eclipse.jetty.security.authentication.BasicAuthenticator;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.handler.HandlerList;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.util.security.Constraint;
+import org.eclipse.jetty.util.security.Credential;
+import org.eclipse.jetty.util.security.Password;
+
+/** Helpers for working with Jetty.
+ * <p>
+ * <h3>SecurityHandler</h3>
+ *  <pre>
+ *     UserStore userStore = JettyLib.makeUserStore(...);
+ *     SecurityHandler securityHandler = JettyLib.makeSecurityHandler(String pathSpec, String realm, UserStore userStore);
+ *  </pre>
+ */
+public class JettyLib {
+    
+    /** Create a Jetty {@link SecurityHandler} for basic authentication. */
+    public static SecurityHandler makeSecurityHandler(String pathSpec, String realm, UserStore userStore) {
+        return makeSecurityHandler(pathSpec, realm, userStore, "**");
+    }
+    
+    /** Create a Jetty {@link SecurityHandler} for basic authentication. */
+    public static SecurityHandler makeSecurityHandler(String pathSpec, String realm, UserStore userStore, String role) {
+        // role can be "**" for any authenticated user.
+        Objects.requireNonNull(pathSpec);
+        Objects.requireNonNull(userStore);
+        Objects.requireNonNull(role);
+        
+        Constraint constraint = new Constraint();
+        constraint.setName(Constraint.__BASIC_AUTH);
+        String[] roles = new String[]{role};
+        constraint.setRoles(roles);
+        constraint.setAuthenticate(true);
+
+        ConstraintMapping mapping = new ConstraintMapping();
+        mapping.setConstraint(constraint);
+        mapping.setPathSpec(pathSpec);
+
+        IdentityService identService = new DefaultIdentityService();
+        ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler();
+        securityHandler.addConstraintMapping(mapping);
+        securityHandler.setIdentityService(identService);
+        
+        // ---- HashLoginService
+        
+        HashLoginService loginService = new HashLoginService("Authentication");
+        loginService.setUserStore(userStore);
+        loginService.setIdentityService(identService);
+        
+        // ----
+        securityHandler.setLoginService(loginService);
+        securityHandler.setAuthenticator(new BasicAuthenticator());
+        if ( realm != null )
+            securityHandler.setRealmName(realm);
+        
+        return securityHandler;
+    }
+
+    /**
+     * Make a {@link UserStore} from a password file.
+     * {@link PropertyUserStore} for details.  
+     */
+    public static UserStore makeUserStore(String passwordFile) {
+        PropertyUserStore propertyUserStore = new PropertyUserStore();
+        propertyUserStore.setConfig(passwordFile);
+        propertyUserStore.setHotReload(false);
+        try { propertyUserStore.start(); }
+        catch (Exception ex) { throw new RuntimeException("UserStore", ex); }
+        return propertyUserStore;
+    }
+
+    /** Make a {@link UserStore} for a single user,password in any role. */
+    public static UserStore makeUserStore(String user, String password) {
+        return makeUserStore(user, password, "**");
+    }
+    
+    /** Make a {@link UserStore} for a sigle user,password,role*/
+    public static UserStore makeUserStore(String user, String password, String role) {
+        Objects.requireNonNull(user);
+        Objects.requireNonNull(password);
+        Objects.requireNonNull(role);
+        PropertyUserStore propertyUserStore = new PropertyUserStore();
+        String[] roles = role == null ? null : new String[]{role};
+        Credential cred  = new Password(password);
+        propertyUserStore.addUser(user, cred, roles);
+        
+        try { propertyUserStore.start(); }
+        catch (Exception ex) { throw new RuntimeException("UserStore", ex); }
+        return propertyUserStore;
+    }
+    
+    /** Add or append a {@link Handler} to a Jetty {@link Server}. */
+    public static void addHandler(Server server, Handler handler) {
+        final Handler currentHandler = server.getHandler();
+        if (currentHandler == null) {
+            server.setHandler(handler);
+        } else {
+            if (currentHandler instanceof HandlerList) {
+                ((HandlerList) currentHandler).addHandler(handler);
+            } else {
+                // Singleton handler. Convert to list.
+                final HandlerList handlerList = new HandlerList();
+                handlerList.addHandler(currentHandler);
+                handlerList.addHandler(handler);
+                server.setHandler(handlerList);
+            }
+        }
+    }
+
+    /** Add the RDF MIME Type mappins */
+    public static void setMimeTypes(ServletContextHandler context) {
+        MimeTypes mimeTypes = new MimeTypes();
+        // RDF syntax
+        mimeTypes.addMimeMapping("nt",      WebContent.contentTypeNTriples);
+        mimeTypes.addMimeMapping("nq",      WebContent.contentTypeNQuads);
+        mimeTypes.addMimeMapping("ttl",     WebContent.contentTypeTurtle+";charset=utf-8");
+        mimeTypes.addMimeMapping("trig",    WebContent.contentTypeTriG+";charset=utf-8");
+        mimeTypes.addMimeMapping("rdf",     WebContent.contentTypeRDFXML);
+        mimeTypes.addMimeMapping("jsonld",  WebContent.contentTypeJSONLD);
+        mimeTypes.addMimeMapping("rj",      WebContent.contentTypeRDFJSON);
+        mimeTypes.addMimeMapping("rt",      WebContent.contentTypeRDFThrift);
+        mimeTypes.addMimeMapping("trdf",    WebContent.contentTypeRDFThrift);
+
+        // SPARQL syntax
+        mimeTypes.addMimeMapping("rq",      WebContent.contentTypeSPARQLQuery);
+        mimeTypes.addMimeMapping("ru",      WebContent.contentTypeSPARQLUpdate);
+
+        // SPARQL Result set
+        mimeTypes.addMimeMapping("rsj",     WebContent.contentTypeResultsJSON);
+        mimeTypes.addMimeMapping("rsx",     WebContent.contentTypeResultsXML);
+        mimeTypes.addMimeMapping("srt",     WebContent.contentTypeResultsThrift);
+
+        // Other
+        mimeTypes.addMimeMapping("txt",     WebContent.contentTypeTextPlain);
+        mimeTypes.addMimeMapping("csv",     WebContent.contentTypeTextCSV);
+        mimeTypes.addMimeMapping("tsv",     WebContent.contentTypeTextTSV);
+        context.setMimeTypes(mimeTypes);
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/Operation.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/Operation.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/Operation.java
index 06331df..7037f0d 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/Operation.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/Operation.java
@@ -29,7 +29,9 @@ public class Operation {
     
     // Create intern'ed symbols. 
     static private NameMgr<Operation> mgr = new NameMgr<>(); 
-    static public Operation register(String name, String description) { return mgr.register(name, (x)->new Operation(x, description)); }
+    static public Operation register(String name, String description) {
+        return mgr.register(name, (x)->new Operation(x, description));
+    }
     
     public static final Operation Query          = register("Query", "SPARQL Query");
     public static final Operation Update         = register("Update", "SPARQL Update");
@@ -39,7 +41,7 @@ public class Operation {
     public static final Operation Quads_R        = register("Quads_R", "HTTP Quads (Read)");
     public static final Operation Quads_RW       = register("Quads_RW", "HTTP Quads");
     
-    // Plain REST operations on the datset URL 
+    // Plain REST operations on the dataset URL 
     public static final Operation DatasetRequest_R  = Quads_R;
     public static final Operation DatasetRequest_RW = Quads_RW;
     

http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionService.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionService.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionService.java
index a22b097..c8268e0 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionService.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionService.java
@@ -79,7 +79,7 @@ public abstract class ActionService extends ActionBase {
         if ( !endpointName.isEmpty() ) {
             operation = chooseOperation(action, dSrv, endpointName);
             if ( operation == null )
-                ServletOps.errorNotFound(format("dataset=%s, service=%s", dataAccessPoint.getName(), endpointName));
+                ServletOps.errorBadRequest(format("dataset=%s, service=%s", dataAccessPoint.getName(), endpointName));
 
         } else {
             operation = chooseOperation(action, dSrv);

http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ServiceDispatchRegistry.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ServiceDispatchRegistry.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ServiceDispatchRegistry.java
index 198eb5c..4e66872 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ServiceDispatchRegistry.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ServiceDispatchRegistry.java
@@ -116,8 +116,16 @@ public class ServiceDispatchRegistry {
         operationToHandler.put(operation, action);
     }
     
+    /**
+     * Remove the registration for an operation.
+     */
+    public void unregister(Operation operation) {
+        Objects.requireNonNull(operation);
+        operationToHandler.remove(operation);
+        contentTypeToOperation.values().remove(operation);
+    }
+
     // The server DataAccessPointRegistry is held in the ServletContext for the server.
-    
     public static ServiceDispatchRegistry get(ServletContext servletContext) {
         ServiceDispatchRegistry registry = (ServiceDispatchRegistry)servletContext.getAttribute(Fuseki.attrServiceRegistry) ;
         if ( registry == null )

http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/jena-fuseki2/jena-fuseki-embedded/src/main/java/org/apache/jena/fuseki/embedded/FusekiServer.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-embedded/src/main/java/org/apache/jena/fuseki/embedded/FusekiServer.java b/jena-fuseki2/jena-fuseki-embedded/src/main/java/org/apache/jena/fuseki/embedded/FusekiServer.java
index 2406e33..ef016f7 100644
--- a/jena-fuseki2/jena-fuseki-embedded/src/main/java/org/apache/jena/fuseki/embedded/FusekiServer.java
+++ b/jena-fuseki2/jena-fuseki-embedded/src/main/java/org/apache/jena/fuseki/embedded/FusekiServer.java
@@ -39,6 +39,7 @@ import org.apache.jena.fuseki.build.FusekiConfig;
 import org.apache.jena.fuseki.ctl.ActionPing;
 import org.apache.jena.fuseki.ctl.ActionStats;
 import org.apache.jena.fuseki.jetty.FusekiErrorHandler1;
+import org.apache.jena.fuseki.jetty.JettyLib;
 import org.apache.jena.fuseki.server.DataAccessPoint;
 import org.apache.jena.fuseki.server.DataAccessPointRegistry;
 import org.apache.jena.fuseki.server.DataService;
@@ -50,11 +51,9 @@ import org.apache.jena.fuseki.servlets.ServiceDispatchRegistry;
 import org.apache.jena.query.Dataset;
 import org.apache.jena.rdf.model.Model;
 import org.apache.jena.rdf.model.Resource;
-import org.apache.jena.riot.WebContent;
 import org.apache.jena.sparql.core.DatasetGraph;
 import org.apache.jena.sparql.core.assembler.AssemblerUtils;
 import org.apache.jena.sparql.util.graph.GraphUtils;
-import org.eclipse.jetty.http.MimeTypes;
 import org.eclipse.jetty.security.SecurityHandler;
 import org.eclipse.jetty.server.HttpConnectionFactory;
 import org.eclipse.jetty.server.Server;
@@ -102,10 +101,21 @@ public class FusekiServer {
             .build();
     }
 
+    /** Return a builder, with the default choices of actions available. */   
     public static Builder create() {
         return new Builder();
     }
 
+    /**
+     * Return a builder, with a custom set of operation-action mappings. An endpoint must
+     * still be created for the server to be able to provide the action. An endpoint
+     * dispatches to an operation, and an operation maps to an implementation. This is a
+     * specialised operation - normal use is the operation {@link #create()}.
+     */
+    public static Builder create(ServiceDispatchRegistry serviceDispatchRegistry) {
+        return new Builder(serviceDispatchRegistry);
+    }
+
     public final Server server;
     private int port;
 
@@ -179,10 +189,10 @@ public class FusekiServer {
     /** FusekiServer.Builder */
     public static class Builder {
         private DataAccessPointRegistry  dataAccessPoints   = new DataAccessPointRegistry();
-        private ServiceDispatchRegistry  serviceDispatch    = new ServiceDispatchRegistry(true);
+        private final ServiceDispatchRegistry  serviceDispatch;
         // Default values.
-        private int                      port               = 3330;
-        private boolean                  loopback           = false;
+        private int                      serverPort         = 3330;
+        private boolean                  networkLoopback    = false;
         private boolean                  verbose            = false;
         private boolean                  withStats          = false;
         private boolean                  withPing           = false;
@@ -195,6 +205,17 @@ public class FusekiServer {
         private SecurityHandler          securityHandler    = null;
         private Map<String, Object>      servletAttr        = new HashMap<>();
 
+        // Builder with standard operation-action mapping.  
+        Builder() {
+            this.serviceDispatch = new ServiceDispatchRegistry(true);
+        }
+
+        // Builder with provided operation-action mapping.  
+        Builder(ServiceDispatchRegistry  serviceDispatch) {
+            // Isolate.
+            this.serviceDispatch = new ServiceDispatchRegistry(serviceDispatch);
+        }
+
         /** Set the port to run on.
          * @deprecated Use {@link #port}.
          */
@@ -207,7 +228,7 @@ public class FusekiServer {
         public Builder port(int port) {
             if ( port < 0 )
                 throw new IllegalArgumentException("Illegal port="+port+" : Port must be greater than or equal to zero.");
-            this.port = port;
+            this.serverPort = port;
             return this;
         }
 
@@ -230,7 +251,7 @@ public class FusekiServer {
         }
 
         /** Restrict the server to only responding to the localhost interface.
-         *  @deprecated Use {@link #loopback}.
+         *  @deprecated Use {@link #networkLoopback}.
          */
         @Deprecated
         public Builder setLoopback(boolean loopback) {
@@ -239,7 +260,7 @@ public class FusekiServer {
 
         /** Restrict the server to only responding to the localhost interface. */
         public Builder loopback(boolean loopback) {
-            this.loopback = loopback;
+            this.networkLoopback = loopback;
             return this;
         }
 
@@ -311,13 +332,21 @@ public class FusekiServer {
             this.withPing = withPing;
             return this;
         }
-        /** Add the dataset with given name and a default set of services including update */
+
+        /**
+         * Add the dataset with given name and a default set of services including update.
+         * This is equivalent to {@code add(name, dataset, true)}.
+         */
         public Builder add(String name, Dataset dataset) {
             requireNonNull(name, "name");
             requireNonNull(dataset, "dataset");
             return add(name, dataset.asDatasetGraph());
         }
 
+        /**
+         * Add the {@link DatasetGraph} with given name and a default set of services including update.
+         * This is equivalent to {@code add(name, dataset, true)}.
+         */
         /** Add the dataset with given name and a default set of services including update */
         public Builder add(String name, DatasetGraph dataset) {
             requireNonNull(name, "name");
@@ -325,14 +354,20 @@ public class FusekiServer {
             return add(name, dataset, true);
         }
 
-        /** Add the dataset with given name and a default set of services. */
+        /**
+         * Add the dataset with given name and a default set of services and enabling
+         * update if allowUpdate=true.
+         */
         public Builder add(String name, Dataset dataset, boolean allowUpdate) {
             requireNonNull(name, "name");
             requireNonNull(dataset, "dataset");
             return add(name, dataset.asDatasetGraph(), allowUpdate);
         }
 
-        /** Add the dataset with given name and a default set of services. */
+        /**
+         * Add the dataset with given name and a default set of services and enabling
+         * update if allowUpdate=true.
+         */
         public Builder add(String name, DatasetGraph dataset, boolean allowUpdate) {
             requireNonNull(name, "name");
             requireNonNull(dataset, "dataset");
@@ -358,11 +393,12 @@ public class FusekiServer {
             return this;
         }
 
-        /** Read and parse a Fuseki services/datasets file.
-         *  <p>
-         *  The application is responsible for ensuring a correct classpath. For example,
-         *  including a dependency on {@code jena-text} if the configuration file
-         *  includes a text index.
+        /**
+         * Configure using a Fuseki services/datasets assembler file.
+         * <p>
+         * The application is responsible for ensuring a correct classpath. For example,
+         * including a dependency on {@code jena-text} if the configuration file includes
+         * a text index.
          */
         public Builder parseConfigFile(String filename) {
             requireNonNull(filename, "filename");
@@ -381,9 +417,10 @@ public class FusekiServer {
         }
 
         /**
-         * Add the given servlet with the pathSpec. These are added so that they are
-         * checked after the Fuseki filter for datasets and before the static content
-         * handler (which is the last servlet) used for {@link #setStaticFileBase(String)}.
+         * Add the given servlet with the {@code pathSpec}. These servlets are added so
+         * that they are checked after the Fuseki filter for datasets and before the
+         * static content handler (which is the last servlet) used for
+         * {@link #setStaticFileBase(String)}.
          */
         public Builder addServlet(String pathSpec, HttpServlet servlet) {
             requireNonNull(pathSpec, "pathSpec");
@@ -395,7 +432,6 @@ public class FusekiServer {
         /**
          * Add a servlet attribute. Pass a value of null to remove any existing binding.
          */
-
         public Builder addServletAttribute(String attrName, Object value) {
             requireNonNull(attrName, "attrName");
             if ( value != null )
@@ -406,7 +442,8 @@ public class FusekiServer {
         }
 
         /**
-         * Add a filter with the pathSpec.
+         * Add a filter with the pathSpec. Note that Fuseki dispatch uses a servlet filter
+         * which is the last in the filter chain.
          */
         public Builder addFilter(String pathSpec, Filter filter) {
             requireNonNull(pathSpec, "pathSpec");
@@ -436,10 +473,10 @@ public class FusekiServer {
          */
         public Builder registerOperation(Operation operation, String contentType, ActionService handler) {
             Objects.requireNonNull(operation, "operation");
-            Objects.requireNonNull(handler, "handler");
-            if ( serviceDispatch.isRegistered(operation) )
-                throw new FusekiConfigException("Handler for operation already registered: "+operation.getName());
-            serviceDispatch.register(operation, contentType, handler);
+            if ( handler == null )
+                serviceDispatch.unregister(operation);
+            else    
+                serviceDispatch.register(operation, contentType, handler);
             return this;
         }
 
@@ -470,9 +507,9 @@ public class FusekiServer {
         public FusekiServer build() {
             ServletContextHandler handler = buildFusekiContext();
             // Use HandlerCollection for several ServletContextHandlers and thus several ServletContext.
-            Server server = jettyServer(port, loopback);
+            Server server = jettyServer(serverPort, networkLoopback);
             server.setHandler(handler);
-            return new FusekiServer(port, server);
+            return new FusekiServer(serverPort, server);
         }
 
         /** Build one configured Fuseki in one unit - same ServletContext, same dispatch ContextPath */  
@@ -484,8 +521,8 @@ public class FusekiServer {
             // Clone to isolate from any future changes.
             ServiceDispatchRegistry.set(cxt, new ServiceDispatchRegistry(serviceDispatch));
             DataAccessPointRegistry.set(cxt, new DataAccessPointRegistry(dataAccessPoints));
-            setMimeTypes(handler);
-            servlets(handler);
+            JettyLib.setMimeTypes(handler);
+            servletsAndFilters(handler);
             // Start services.
             DataAccessPointRegistry.get(cxt).forEach((name, dap)->dap.getDataService().goActive());
             return handler;
@@ -506,37 +543,8 @@ public class FusekiServer {
             return context;
         }
 
-        private static void setMimeTypes(ServletContextHandler context) {
-            MimeTypes mimeTypes = new MimeTypes();
-            // RDF syntax
-            mimeTypes.addMimeMapping("nt",      WebContent.contentTypeNTriples);
-            mimeTypes.addMimeMapping("nq",      WebContent.contentTypeNQuads);
-            mimeTypes.addMimeMapping("ttl",     WebContent.contentTypeTurtle+";charset=utf-8");
-            mimeTypes.addMimeMapping("trig",    WebContent.contentTypeTriG+";charset=utf-8");
-            mimeTypes.addMimeMapping("rdfxml",  WebContent.contentTypeRDFXML);
-            mimeTypes.addMimeMapping("jsonld",  WebContent.contentTypeJSONLD);
-            mimeTypes.addMimeMapping("rj",      WebContent.contentTypeRDFJSON);
-            mimeTypes.addMimeMapping("rt",      WebContent.contentTypeRDFThrift);
-            mimeTypes.addMimeMapping("trdf",    WebContent.contentTypeRDFThrift);
-
-            // SPARQL syntax
-            mimeTypes.addMimeMapping("rq",      WebContent.contentTypeSPARQLQuery);
-            mimeTypes.addMimeMapping("ru",      WebContent.contentTypeSPARQLUpdate);
-
-            // SPARQL Result set
-            mimeTypes.addMimeMapping("rsj",     WebContent.contentTypeResultsJSON);
-            mimeTypes.addMimeMapping("rsx",     WebContent.contentTypeResultsXML);
-            mimeTypes.addMimeMapping("srt",     WebContent.contentTypeResultsThrift);
-
-            // Other
-            mimeTypes.addMimeMapping("txt",     WebContent.contentTypeTextPlain);
-            mimeTypes.addMimeMapping("csv",     WebContent.contentTypeTextCSV);
-            mimeTypes.addMimeMapping("tsv",     WebContent.contentTypeTextTSV);
-            context.setMimeTypes(mimeTypes);
-        }
-
         /** Add servlets and servlet filters, including the {@link FusekiFilter} */ 
-        private void servlets(ServletContextHandler context) {
+        private void servletsAndFilters(ServletContextHandler context) {
             // Fuseki dataset services filter
             // This goes as the filter at the end of any filter chaining.
             FusekiFilter ff = new FusekiFilter();

http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/jena-fuseki2/jena-fuseki-embedded/src/main/java/org/apache/jena/fuseki/embedded/JettyServer.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-embedded/src/main/java/org/apache/jena/fuseki/embedded/JettyServer.java b/jena-fuseki2/jena-fuseki-embedded/src/main/java/org/apache/jena/fuseki/embedded/JettyServer.java
new file mode 100644
index 0000000..704060c
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-embedded/src/main/java/org/apache/jena/fuseki/embedded/JettyServer.java
@@ -0,0 +1,369 @@
+/*
+ * 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.fuseki.embedded;
+
+import static java.lang.String.format;
+import static java.util.Objects.requireNonNull;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.Filter;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.jena.atlas.lib.Pair;
+import org.apache.jena.fuseki.Fuseki;
+import org.apache.jena.fuseki.server.DataAccessPointRegistry;
+import org.apache.jena.fuseki.servlets.ActionBase;
+import org.apache.jena.fuseki.servlets.ServiceDispatchRegistry;
+import org.apache.jena.riot.web.HttpNames;
+import org.apache.jena.web.HttpSC;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.MimeTypes;
+import org.eclipse.jetty.security.SecurityHandler;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Response;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.handler.ErrorHandler;
+import org.eclipse.jetty.servlet.DefaultServlet;
+import org.eclipse.jetty.servlet.FilterHolder;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Jetty server for servlets, including being able to run Fuseki {@link ActionBase} derived servlets.
+ * Static RDF types by file extension can be enabled.
+ */
+public class JettyServer {
+    // Use this for the super class of FusekiServer or as implementation inheritance.
+    // Caution : there are small differences e.g. in building where order matters.
+
+    private static Logger LOG = LoggerFactory.getLogger("HTTP");
+
+    protected final Server server;
+    protected int port;
+
+    public static Builder create() {
+        return new Builder();
+    }
+
+    protected JettyServer(int port, Server server) {
+        this.server = server;
+        this.port = port;
+    }
+
+    /**
+     * Return the port begin used.
+     * This will be the give port, which defaults to 3330, or
+     * the one actually allocated if the port was 0 ("choose a free port").
+     */
+    public int getPort() {
+        return port;
+    }
+
+    /** Get the underlying Jetty server which has also been set up. */
+    public Server getJettyServer() {
+        return server;
+    }
+
+    /** Get the {@link ServletContext}.
+     * Adding new servlets is possible with care.
+     */
+    public ServletContext getServletContext() {
+        return ((ServletContextHandler)server.getHandler()).getServletContext();
+    }
+
+    /** Start the server - the server continues to run after this call returns.
+     *  To synchronise with the server stopping, call {@link #join}.
+     */
+    public JettyServer start() {
+        try { server.start(); }
+        catch (Exception e) { throw new RuntimeException(e); }
+        if ( port == 0 )
+            port = ((ServerConnector)server.getConnectors()[0]).getLocalPort();
+        logStart();
+        return this;
+    }
+
+    protected void logStart() {
+        LOG.info("Start (port="+port+")");
+    }
+
+    /** Stop the server. */
+    public void stop() {
+        logStop();
+        try { server.stop(); }
+        catch (Exception e) { throw new RuntimeException(e); }
+    }
+
+    protected void logStop() {
+        LOG.info("Stop (port="+port+")");
+    }
+
+    /** Wait for the server to exit. This call is blocking. */
+    public void join() {
+        try { server.join(); }
+        catch (Exception e) { throw new RuntimeException(e); }
+    }
+
+    /** One line error handler */
+    public static class PlainErrorHandler extends ErrorHandler {
+        // c.f. FusekiErrorHandler1
+        public PlainErrorHandler() {}
+
+        @Override
+        public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
+        {
+            String method = request.getMethod();
+
+            if ( !method.equals(HttpMethod.GET.asString())
+                 && !method.equals(HttpMethod.POST.asString())
+                 && !method.equals(HttpMethod.HEAD.asString()) )
+                return ;
+
+            response.setContentType(MimeTypes.Type.TEXT_PLAIN_UTF_8.asString()) ;
+            response.setHeader(HttpNames.hCacheControl, "must-revalidate,no-cache,no-store");
+            response.setHeader(HttpNames.hPragma, "no-cache");
+            int code = response.getStatus() ;
+            String message=(response instanceof Response)?((Response)response).getReason(): HttpSC.getMessage(code) ;
+            response.getOutputStream().print(format("Error %d: %s\n", code, message)) ;
+        }
+    }
+
+    protected static class Builder {
+        private int                      port               = -1;
+        private boolean                  loopback           = false;
+        protected boolean                verbose            = false;
+        // Other servlets to add.
+        private List<Pair<String, HttpServlet>> servlets    = new ArrayList<>();
+        private List<Pair<String, Filter>> filters          = new ArrayList<>();
+
+        private String                   contextPath        = "/";
+        private String                   servletContextName = "Jetty";
+        private String                   staticContentDir   = null;
+        private SecurityHandler          securityHandler    = null;
+        private ErrorHandler             errorHandler       = new PlainErrorHandler();
+        private Map<String, Object>      servletAttr        = new HashMap<>();
+
+        /** Set the port to run on. */
+        public Builder port(int port) {
+            if ( port < 0 )
+                throw new IllegalArgumentException("Illegal port="+port+" : Port must be greater than or equal to zero.");
+            this.port = port;
+            return this;
+        }
+
+        /**
+         * Context path.  If it's "/" then Server URL will look like
+         * "http://host:port/" else "http://host:port/path/"
+         * (or no port if :80).
+         */
+        public Builder contextPath(String path) {
+            requireNonNull(path, "path");
+            this.contextPath = path;
+            return this;
+        }
+
+        /**
+         * ServletContextName.
+         */
+        public Builder servletContextName(String name) {
+            requireNonNull(name, "name");
+            this.servletContextName = name;
+            return this;
+        }
+
+        /** Restrict the server to only responding to the localhost interface. */
+        public Builder loopback(boolean loopback) {
+            this.loopback = loopback;
+            return this;
+        }
+
+        /** Set the location (filing system directory) to serve static file from. */
+        public Builder staticFileBase(String directory) {
+            requireNonNull(directory, "directory");
+            this.staticContentDir = directory;
+            return this;
+        }
+
+        /** Set a Jetty SecurityHandler.
+         * <p>
+         *  By default, the server runs with no security.
+         *  This is more for using the basic server for testing.
+         *  The full Fuseki server provides security with Apache Shiro
+         *  and a defensive reverse proxy (e.g. Apache httpd) in front of the Jetty server
+         *  can also be used, which provides a wide varity of proven security options.
+         */
+        public Builder securityHandler(SecurityHandler securityHandler) {
+            requireNonNull(securityHandler, "securityHandler");
+            this.securityHandler = securityHandler;
+            return this;
+        }
+
+        /** Set an {@link ErrorHandler}.
+         * <p>
+         *  By default, the server runs with error handle that prints the code and message.
+         */
+        public Builder errorHandler(ErrorHandler errorHandler) {
+            requireNonNull(errorHandler, "securityHandler");
+            this.errorHandler = errorHandler;
+            return this;
+        }
+
+        /** Set verbose logging */
+        public Builder verbose(boolean verbose) {
+            this.verbose = verbose;
+            return this;
+        }
+
+        /**
+         * Add the given servlet with the pathSpec. These are added so that they are
+         * before the static content handler (which is the last servlet)
+         * used for {@link #staticFileBase(String)}.
+         */
+        public Builder addServlet(String pathSpec, HttpServlet servlet) {
+            requireNonNull(pathSpec, "pathSpec");
+            requireNonNull(servlet, "servlet");
+            servlets.add(Pair.create(pathSpec, servlet));
+            return this;
+        }
+
+        /**
+         * Add a servlet attribute. Pass a value of null to remove any existing binding.
+         */
+        public Builder addServletAttribute(String attrName, Object value) {
+            requireNonNull(attrName, "attrName");
+            if ( value != null )
+                servletAttr.put(attrName, value);
+            else
+                servletAttr.remove(attrName);
+            return this;
+        }
+
+        /**
+         * Add the given filter with the pathSpec.
+         * It is applied to all dispatch types.
+         */
+        public Builder addFilter(String pathSpec, Filter filter) {
+            requireNonNull(pathSpec, "pathSpec");
+            requireNonNull(filter, "filter");
+            filters.add(Pair.create(pathSpec, filter));
+            return this;
+        }
+
+        /**
+         * Build a server according to the current description.
+         */
+        public JettyServer build() {
+            ServletContextHandler handler = buildServletContext();
+            // Use HandlerCollection for several ServletContextHandlers and thus several ServletContext.
+            Server server = jettyServer(port, loopback);
+            server.setHandler(handler);
+            return new JettyServer(port, server);
+        }
+
+        /** Build a ServletContextHandler : one servlet context */
+        private ServletContextHandler buildServletContext() {
+            ServletContextHandler handler = buildServletContext(contextPath);
+            ServletContext cxt = handler.getServletContext();
+            adjustForFuseki(cxt);
+            servletAttr.forEach((n,v)->cxt.setAttribute(n, v));
+            servletsAndFilters(handler);
+            return handler;
+        }
+
+        private void adjustForFuseki(ServletContext cxt) {
+            // For Fuseki servlets added directly.
+            // This enables servlets inheriting from {@link ActionBase} to work in the
+            // plain Jetty server, e.g. to use Fuseki logging.
+            try {
+                Fuseki.setVerbose(cxt, verbose);
+                ServiceDispatchRegistry.set(cxt, new ServiceDispatchRegistry(false));
+                DataAccessPointRegistry.set(cxt, new DataAccessPointRegistry());
+            } catch (NoClassDefFoundError err) {
+                LOG.info("Fuseki classes not found");
+            }
+        }
+
+        /** Build a ServletContextHandler. */
+        private ServletContextHandler buildServletContext(String contextPath) {
+            if ( contextPath == null || contextPath.isEmpty() )
+                contextPath = "/";
+            else if ( !contextPath.startsWith("/") )
+                contextPath = "/" + contextPath;
+            ServletContextHandler context = new ServletContextHandler();
+            context.setDisplayName(servletContextName);
+            context.setErrorHandler(errorHandler);
+            context.setContextPath(contextPath);
+            if ( securityHandler != null )
+                context.setSecurityHandler(securityHandler);
+
+            return context;
+        }
+
+        /** Add servlets and servlet filters */
+        private void servletsAndFilters(ServletContextHandler context) {
+            servlets.forEach(p-> addServlet(context, p.getLeft(), p.getRight()) );
+            filters.forEach (p-> addFilter (context, p.getLeft(), p.getRight()) );
+
+            if ( staticContentDir != null ) {
+                DefaultServlet staticServlet = new DefaultServlet();
+                ServletHolder staticContent = new ServletHolder(staticServlet);
+                staticContent.setInitParameter("resourceBase", staticContentDir);
+                context.addServlet(staticContent, "/");
+            }
+        }
+
+        protected static void addServlet(ServletContextHandler context, String pathspec, HttpServlet httpServlet) {
+            ServletHolder sh = new ServletHolder(httpServlet);
+            context.addServlet(sh, pathspec);
+        }
+
+        protected void addFilter(ServletContextHandler context, String pathspec, Filter filter) {
+            FilterHolder h = new FilterHolder(filter);
+            context.addFilter(h, pathspec, null);
+        }
+
+        /** Jetty server */
+        private static Server jettyServer(int port, boolean loopback) {
+            Server server = new Server();
+            HttpConnectionFactory f1 = new HttpConnectionFactory();
+
+            //f1.getHttpConfiguration().setRequestHeaderSize(512 * 1024);
+            //f1.getHttpConfiguration().setOutputBufferSize(1024 * 1024);
+            f1.getHttpConfiguration().setSendServerVersion(false);
+            ServerConnector connector = new ServerConnector(server, f1);
+            connector.setPort(port);
+            server.addConnector(connector);
+            if ( loopback )
+                connector.setHost("localhost");
+            return server;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/jena-fuseki2/pom.xml
----------------------------------------------------------------------
diff --git a/jena-fuseki2/pom.xml b/jena-fuseki2/pom.xml
index 1b13a9c..5a464d6 100644
--- a/jena-fuseki2/pom.xml
+++ b/jena-fuseki2/pom.xml
@@ -60,6 +60,7 @@
   <modules>
     <module>jena-fuseki-core</module>
     <module>jena-fuseki-embedded</module>
+    <module>jena-fuseki-access</module>
     <module>jena-fuseki-war</module>
     <module>jena-fuseki-server</module>
     <module>jena-fuseki-basic</module>

http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/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 2091157..776817c 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
@@ -18,6 +18,12 @@
 
 package org.apache.jena.rdfconnection;
 
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.Credentials;
+import org.apache.http.auth.UsernamePasswordCredentials;
+import org.apache.http.client.HttpClient;
+import org.apache.http.impl.client.BasicCredentialsProvider;
+import org.apache.http.impl.client.HttpClients;
 import org.apache.jena.query.Dataset;
 import org.apache.jena.sys.JenaSystem;
 
@@ -82,6 +88,25 @@ public class RDFConnectionFactory {
             .gspEndpoint(graphStoreProtocolEndpoint)
             .build();
     }
+    
+    /** Make a remote RDFConnection to the URL, with user and password for the client access using basic auth.
+     *  Use with care.  Basic auth over plain HTTP reveals the password on the network. 
+     * @param URL
+     * @param user
+     * @param password
+     * @return RDFConnection
+     */
+    public static RDFConnection connectPW(String URL, String user, String password) {
+        BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
+        Credentials credentials = new UsernamePasswordCredentials(user, password);
+        credsProvider.setCredentials(AuthScope.ANY, credentials);
+        HttpClient client = HttpClients.custom().setDefaultCredentialsProvider(credsProvider).build();
+        return RDFConnectionRemote.create()
+            .destination(URL)
+            .httpClient(client)
+            .build();
+    }
+
 
     /**
      * Connect to a local (same JVM) dataset.


[26/27] jena git commit: JENA-1594: Interface AuthorizationService; SecurityRegsitry is an impl

Posted by an...@apache.org.
JENA-1594: Interface AuthorizationService; SecurityRegsitry is an impl


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

Branch: refs/heads/master
Commit: 3b01252f9d767af3e3b119d3b34f3699a9017fdc
Parents: 20eb07c
Author: Andy Seaborne <an...@apache.org>
Authored: Tue Aug 28 16:17:03 2018 +0100
Committer: Andy Seaborne <an...@apache.org>
Committed: Tue Aug 28 16:17:03 2018 +0100

----------------------------------------------------------------------
 .../sparql/core/DatasetGraphFilteredView.java   |  2 +-
 .../fuseki/access/AssemblerAccessDataset.java   |  2 +-
 .../access/AssemblerSecurityRegistry.java       |  2 +-
 .../fuseki/access/AuthorizationService.java     | 30 ++++++++++++++++++++
 .../jena/fuseki/access/DataAccessCtl.java       | 22 +++++++-------
 .../jena/fuseki/access/DataAccessLib.java       | 21 ++++++++++----
 .../access/DatasetGraphAccessControl.java       | 10 +++----
 .../jena/fuseki/access/SecurityRegistry.java    | 15 ++--------
 .../fuseki/access/TS_SecurityFiltering.java     | 10 +++----
 .../access/TestSecurityAssemblerBuild.java      |  2 +-
 .../java/org/apache/jena/fuseki/Fuseki.java     |  2 +-
 11 files changed, 74 insertions(+), 44 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/3b01252f/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphFilteredView.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphFilteredView.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphFilteredView.java
index 4c49bd1..be2cc38 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphFilteredView.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphFilteredView.java
@@ -70,7 +70,7 @@ public class DatasetGraphFilteredView extends DatasetGraphReadOnly implements Da
         super(dsg);
         this.quadFilter = filter;
         if ( visibleGraphs.contains(Quad.defaultGraphIRI) || visibleGraphs.contains(Quad.defaultGraphNodeGenerated) ) {
-            Log.warn(DatasetGraphFilteredView.class, "default graph Node in visibleGraphs colelction - fix up applied");
+            Log.warn(DatasetGraphFilteredView.class, "default graph Node in visibleGraphs collection - fix up applied");
             visibleGraphs = new HashSet<>(visibleGraphs);
             visibleGraphs.remove(Quad.defaultGraphIRI);
             visibleGraphs.remove(Quad.defaultGraphNodeGenerated);

http://git-wip-us.apache.org/repos/asf/jena/blob/3b01252f/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AssemblerAccessDataset.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AssemblerAccessDataset.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AssemblerAccessDataset.java
index 75cb155..71a89db 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AssemblerAccessDataset.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AssemblerAccessDataset.java
@@ -47,7 +47,7 @@ public class AssemblerAccessDataset extends AssemblerBase {
         RDFNode rnRegistry = root.getProperty(VocabSecurity.pSecurityRegistry).getObject();
         RDFNode rnDataset = root.getProperty(VocabSecurity.pDataset).getObject();
         
-        SecurityRegistry sr = (SecurityRegistry)a.open(rnRegistry.asResource()) ;
+        AuthorizationService sr = (AuthorizationService)a.open(rnRegistry.asResource()) ;
         Dataset ds = (Dataset)a.open(rnDataset.asResource()) ;
         
         DatasetGraph dsg = new DatasetGraphAccessControl(ds.asDatasetGraph(), sr);

http://git-wip-us.apache.org/repos/asf/jena/blob/3b01252f/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AssemblerSecurityRegistry.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AssemblerSecurityRegistry.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AssemblerSecurityRegistry.java
index dc66768..00c27b9 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AssemblerSecurityRegistry.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AssemblerSecurityRegistry.java
@@ -52,7 +52,7 @@ public class AssemblerSecurityRegistry extends AssemblerBase {
      */
     
     @Override
-    public SecurityRegistry open(Assembler a, Resource root, Mode mode) {
+    public AuthorizationService open(Assembler a, Resource root, Mode mode) {
         SecurityRegistry registry = new SecurityRegistry();
         // Java walking gives better error messages.
         StmtIterator sIter = root.listProperties(VocabSecurity.pEntry);

http://git-wip-us.apache.org/repos/asf/jena/blob/3b01252f/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AuthorizationService.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AuthorizationService.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AuthorizationService.java
new file mode 100644
index 0000000..dd8e7c6
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AuthorizationService.java
@@ -0,0 +1,30 @@
+/*
+ * 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.fuseki.access;
+
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * A {@link AuthorizationService} implemented with a {@link ConcurrentHashMap}.
+ */ 
+public interface AuthorizationService {
+
+    /** Return the security context  for a geiven actor (user) */
+    public SecurityContext get(String actor);
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/3b01252f/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessCtl.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessCtl.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessCtl.java
index f1bf7d3..f1399f2 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessCtl.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessCtl.java
@@ -38,7 +38,7 @@ import org.apache.jena.sparql.util.Symbol;
 import org.apache.jena.sys.JenaSystem;
 import org.eclipse.jetty.security.SecurityHandler;
 
-/** A library of operations related to data acess sexurity for Fuseki */  
+/** A library of operations related to data access security for Fuseki */  
 public class DataAccessCtl {
     static { JenaSystem.init(); }
     
@@ -46,14 +46,14 @@ public class DataAccessCtl {
      * Flag for whether this is data access controlled or not - boolean false or undef for "not
      * controlled". This is an alternative to {@link DatasetGraphAccessControl}.
      */
-    public static final Symbol   symControlledAccess      = Symbol.create(VocabSecurity.getURI() + "controlled");
+    public static final Symbol   symControlledAccess        = Symbol.create(VocabSecurity.getURI() + "controlled");
     
     /**
-     * Symbol for the {@link SecurityRegistry}. Must be present if
+     * Symbol for the {@link AuthorizationService}. Must be present if
      * {@link #symControlledAccess} indicates data access control.
      * This is an alternative to {@link DatasetGraphAccessControl}.
      */
-    public static final Symbol   symSecurityRegistry      = Symbol.create(VocabSecurity.getURI() + "registry");
+    public static final Symbol   symAuthorizationService    = Symbol.create(VocabSecurity.getURI() + "authService");
 
     /** Get the user from the servlet context via {@link HttpServletRequest#getRemoteUser} */ 
     public static final Function<HttpAction, String> requestUserServlet = (action)->action.request.getRemoteUser();
@@ -68,16 +68,16 @@ public class DataAccessCtl {
      * Add data access control information on a {@link DatasetGraph}. This modifies the
      * {@link DatasetGraph}'s {@link Context}.
      */
-    private static void addSecurityRegistry(DatasetGraph dsg, SecurityRegistry reg) {
+    private static void addAuthorizatonService(DatasetGraph dsg, AuthorizationService authService) {
         dsg.getContext().set(symControlledAccess, true);
-        dsg.getContext().set(symSecurityRegistry, reg);
+        dsg.getContext().set(symAuthorizationService, authService);
     }
 
     /**
      * Return a {@link DatasetGraph} with added data access control. 
      * Use of the original {@code DatasetGraph} is not controlled.
      */
-    public static Dataset controlledDataset(Dataset dsBase, SecurityRegistry reg) {
+    public static Dataset controlledDataset(Dataset dsBase, AuthorizationService reg) {
         DatasetGraph dsg = controlledDataset(dsBase.asDatasetGraph(), reg);
         return DatasetFactory.wrap(dsg);
     }
@@ -86,12 +86,12 @@ public class DataAccessCtl {
      * Return a {@link DatasetGraph} with added data access control. Use of the original
      * {@code DatasetGraph} is not controlled.
      */
-    public static DatasetGraph controlledDataset(DatasetGraph dsgBase, SecurityRegistry reg) {
+    public static DatasetGraph controlledDataset(DatasetGraph dsgBase, AuthorizationService reg) {
         if ( dsgBase instanceof DatasetGraphAccessControl ) {
             DatasetGraphAccessControl dsgx = (DatasetGraphAccessControl)dsgBase;
-            if ( reg == dsgx.getRegistry() )
+            if ( reg == dsgx.getAuthService() )
                 return dsgx;
-            throw new IllegalArgumentException("DatasetGraph is alerady wrapped on a DatasetGraphAccessControl with a different SecurityRegistry");
+            throw new IllegalArgumentException("DatasetGraph is alerady wrapped on a DatasetGraphAccessControl with a different AuthorizationService");
         }
         
         DatasetGraphAccessControl dsg1 = new DatasetGraphAccessControl(dsgBase, reg);
@@ -149,7 +149,7 @@ public class DataAccessCtl {
             return true;
         if ( dsg.getContext().isDefined(DataAccessCtl.symControlledAccess) )
             return true;
-        if ( dsg.getContext().isDefined(DataAccessCtl.symSecurityRegistry) )
+        if ( dsg.getContext().isDefined(DataAccessCtl.symAuthorizationService) )
             return true;
         return false;
     }

http://git-wip-us.apache.org/repos/asf/jena/blob/3b01252f/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessLib.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessLib.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessLib.java
index c9b98bf..9b99f27 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessLib.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessLib.java
@@ -20,6 +20,9 @@ package org.apache.jena.fuseki.access;
 
 import java.util.function.Function;
 
+import javax.servlet.ServletContext;
+
+import org.apache.jena.fuseki.Fuseki;
 import org.apache.jena.fuseki.servlets.HttpAction;
 import org.apache.jena.fuseki.servlets.ServletOps;
 import org.apache.jena.sparql.core.DatasetGraph;
@@ -29,7 +32,7 @@ class DataAccessLib {
     
     /** Determine the {@link SecurityContext} for this request */  
     static SecurityContext getSecurityContext(HttpAction action, DatasetGraph dataset, Function<HttpAction, String> requestUser) {
-        SecurityRegistry registry = getSecurityRegistry(action, dataset);
+        AuthorizationService registry = getAuthorizationService(action, dataset);
         if ( registry == null )
             ServletOps.errorOccurred("Internal Server Error");
 
@@ -41,11 +44,11 @@ class DataAccessLib {
         return sCxt;
     }
     
-    /** Get the {@link SecurityRegistry} for an action/query/dataset */
-    static SecurityRegistry getSecurityRegistry(HttpAction action, DatasetGraph dsg) {
+    /** Get the {@link AuthorizationService} for an action/query/dataset */
+    static AuthorizationService getAuthorizationService(HttpAction action, DatasetGraph dsg) {
         if ( dsg instanceof DatasetGraphAccessControl )
-            return ((DatasetGraphAccessControl)dsg).getRegistry();
-        return dsg.getContext().get(DataAccessCtl.symSecurityRegistry);
+            return ((DatasetGraphAccessControl)dsg).getAuthService();
+        return dsg.getContext().get(DataAccessCtl.symAuthorizationService);
     }
 
     static SecurityContext noSecurityPolicy() {
@@ -66,5 +69,13 @@ class DataAccessLib {
         dsg = DataAccessCtl.filteredDataset(dsg, sCxt);
         return dsg;
     }
+
+    static void set(ServletContext cxt, AuthorizationService authorizationService) {
+        cxt.setAttribute(Fuseki.attrAuthorizationService, authorizationService);
+    }
+
+    static AuthorizationService get(ServletContext cxt) {
+        return (AuthorizationService)cxt.getAttribute(Fuseki.attrAuthorizationService);
+    }
 }
 

http://git-wip-us.apache.org/repos/asf/jena/blob/3b01252f/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DatasetGraphAccessControl.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DatasetGraphAccessControl.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DatasetGraphAccessControl.java
index f58bdcd..a518f75 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DatasetGraphAccessControl.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DatasetGraphAccessControl.java
@@ -23,17 +23,17 @@ import java.util.Objects;
 import org.apache.jena.sparql.core.DatasetGraph;
 import org.apache.jena.sparql.core.DatasetGraphWrapper;
 
-/** DatasetGraph layer that carries a SecurityRegistry. */ 
+/** DatasetGraph layer that carries an {@link AuthorizationService}. */ 
 class DatasetGraphAccessControl extends DatasetGraphWrapper {
     
-    private SecurityRegistry registry = null; 
+    private AuthorizationService registry = null; 
 
-    public DatasetGraphAccessControl(DatasetGraph dsg, SecurityRegistry registry) {
+    public DatasetGraphAccessControl(DatasetGraph dsg, AuthorizationService authService) {
         super(Objects.requireNonNull(dsg));
-        this.registry = Objects.requireNonNull(registry); 
+        this.registry = Objects.requireNonNull(authService); 
     }
     
-    public SecurityRegistry getRegistry() {
+    public AuthorizationService getAuthService() {
         return registry;
     }
 

http://git-wip-us.apache.org/repos/asf/jena/blob/3b01252f/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityRegistry.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityRegistry.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityRegistry.java
index 10b9a08..7629cd2 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityRegistry.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityRegistry.java
@@ -20,26 +20,15 @@ package org.apache.jena.fuseki.access;
 
 import java.util.StringJoiner;
 
-import javax.servlet.ServletContext;
-
 import org.apache.jena.atlas.lib.Registry;
-import org.apache.jena.fuseki.Fuseki;
 
 /**
- * A {@link SecurityRegistry} is mapping from a string (typically a user name or role
+ * Am {@link AuthorizationService} implements as a mapping from a string (typically a user name or role
  * name) to a {@link SecurityContext}, where the {@link SecurityContext}
  * is the access control operations for the user/role.
  */ 
-public class SecurityRegistry extends Registry<String, SecurityContext>{
-    
-    public static SecurityRegistry get(ServletContext cxt) {
-        return (SecurityRegistry)cxt.getAttribute(Fuseki.attrSecurityRegistry);
-    }
+public class SecurityRegistry extends Registry<String, SecurityContext> implements AuthorizationService {
     
-    public static void set(ServletContext cxt, SecurityRegistry securityRegistry) {
-        cxt.setAttribute(Fuseki.attrSecurityRegistry, securityRegistry);
-    }
-
     public SecurityRegistry() {}
     
     @Override

http://git-wip-us.apache.org/repos/asf/jena/blob/3b01252f/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TS_SecurityFiltering.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TS_SecurityFiltering.java b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TS_SecurityFiltering.java
index 981a205..715054d 100644
--- a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TS_SecurityFiltering.java
+++ b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TS_SecurityFiltering.java
@@ -35,10 +35,10 @@ import org.junit.runners.Suite;
 
 public class TS_SecurityFiltering {
     @BeforeClass public static void setupForFusekiServer() {
-        LogCtl.setLevel(Fuseki.serverLogName,        "WARN");
-        LogCtl.setLevel(Fuseki.actionLogName,        "WARN");
-        LogCtl.setLevel(Fuseki.requestLogName,       "WARN");
-        LogCtl.setLevel(Fuseki.adminLogName,         "WARN");
-        LogCtl.setLevel("org.eclipse.jetty",         "WARN");
+        LogCtl.setLevel(Fuseki.serverLogName,   "WARN");
+        LogCtl.setLevel(Fuseki.actionLogName,   "WARN");
+        LogCtl.setLevel(Fuseki.requestLogName,  "WARN");
+        LogCtl.setLevel(Fuseki.adminLogName,    "WARN");
+        LogCtl.setLevel("org.eclipse.jetty",    "WARN");
     }
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/3b01252f/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityAssemblerBuild.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityAssemblerBuild.java b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityAssemblerBuild.java
index 14610b1..54cab0d 100644
--- a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityAssemblerBuild.java
+++ b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityAssemblerBuild.java
@@ -40,7 +40,7 @@ public class TestSecurityAssemblerBuild {
     private void assemblerFile(String assemblerFile) { 
         Dataset ds = (Dataset)AssemblerUtils.build(assemblerFile, VocabSecurity.tAccessControlledDataset);
         DatasetGraphAccessControl dsg = (DatasetGraphAccessControl)ds.asDatasetGraph();
-        SecurityRegistry securityRegistry = dsg.getRegistry();
+        AuthorizationService securityRegistry = dsg.getAuthService();
         assertNotNull(securityRegistry);
     }
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/3b01252f/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/Fuseki.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/Fuseki.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/Fuseki.java
index ec1126a..4e5f1ac 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/Fuseki.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/Fuseki.java
@@ -189,7 +189,7 @@ public class Fuseki {
     public static final String attrVerbose                 = "org.apache.jena.fuseki:verbose";
     public static final String attrNameRegistry            = "org.apache.jena.fuseki:DataAccessPointRegistry";
     public static final String attrServiceRegistry         = "org.apache.jena.fuseki:ServiceDispatchRegistry";
-    public static final String attrSecurityRegistry        = "org.apache.jena.fuseki:SecurityRegistry";
+    public static final String attrAuthorizationService    = "org.apache.jena.fuseki:AuthorizationService";
 
     public static void setVerbose(ServletContext cxt, boolean verbose) {
         cxt.setAttribute(attrVerbose, Boolean.valueOf(verbose));


[05/27] jena git commit: JENA-1585: Refactoring webapp code to separate from the core server.

Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/jena/blob/06f59125/jena-fuseki2/jena-fuseki-core/src/main/webapp/WEB-INF/web.xml
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/webapp/WEB-INF/web.xml b/jena-fuseki2/jena-fuseki-core/src/main/webapp/WEB-INF/web.xml
index 48e49e8..b9f1ff1 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/webapp/WEB-INF/web.xml
+++ b/jena-fuseki2/jena-fuseki-core/src/main/webapp/WEB-INF/web.xml
@@ -185,12 +185,12 @@
 
   <servlet>
     <servlet-name>ActionBackup</servlet-name>
-    <servlet-class>org.apache.jena.fuseki.ctl.ActionBackup</servlet-class>
+    <servlet-class>org.apache.jena.fuseki.mgt.ActionBackup</servlet-class>
   </servlet>
 
   <servlet>
     <servlet-name>BackupListServlet</servlet-name>
-    <servlet-class>org.apache.jena.fuseki.ctl.ActionBackupList</servlet-class>
+    <servlet-class>org.apache.jena.fuseki.mgt.ActionBackupList</servlet-class>
   </servlet>
 
   <!-- An action that only creates a background task that sleeps. -->

http://git-wip-us.apache.org/repos/asf/jena/blob/06f59125/jena-fuseki2/jena-fuseki-core/src/main/webapp/js/app/controllers/.svnkeep
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/webapp/js/app/controllers/.svnkeep b/jena-fuseki2/jena-fuseki-core/src/main/webapp/js/app/controllers/.svnkeep
deleted file mode 100644
index e69de29..0000000

http://git-wip-us.apache.org/repos/asf/jena/blob/06f59125/jena-fuseki2/jena-fuseki-core/src/main/webapp/js/app/layouts/.svnkeep
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/webapp/js/app/layouts/.svnkeep b/jena-fuseki2/jena-fuseki-core/src/main/webapp/js/app/layouts/.svnkeep
deleted file mode 100644
index e69de29..0000000

http://git-wip-us.apache.org/repos/asf/jena/blob/06f59125/jena-fuseki2/jena-fuseki-core/src/main/webapp/js/app/routers/.svnkeep
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/webapp/js/app/routers/.svnkeep b/jena-fuseki2/jena-fuseki-core/src/main/webapp/js/app/routers/.svnkeep
deleted file mode 100644
index e69de29..0000000

http://git-wip-us.apache.org/repos/asf/jena/blob/06f59125/jena-fuseki2/jena-fuseki-core/src/main/webapp/js/app/views/.svnkeep
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/webapp/js/app/views/.svnkeep b/jena-fuseki2/jena-fuseki-core/src/main/webapp/js/app/views/.svnkeep
deleted file mode 100644
index e69de29..0000000

http://git-wip-us.apache.org/repos/asf/jena/blob/06f59125/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/ServerCtl.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/ServerCtl.java b/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/ServerCtl.java
index 048ecf0..8fc4513 100644
--- a/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/ServerCtl.java
+++ b/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/ServerCtl.java
@@ -30,7 +30,7 @@ import org.apache.http.client.HttpClient ;
 import org.apache.http.impl.client.CloseableHttpClient ;
 import org.apache.jena.atlas.io.IO ;
 import org.apache.jena.atlas.lib.FileOps ;
-import org.apache.jena.fuseki.jetty.JettyFuseki ;
+import org.apache.jena.fuseki.cmd.JettyFuseki;
 import org.apache.jena.fuseki.jetty.JettyServerConfig ;
 import org.apache.jena.fuseki.server.* ;
 import org.apache.jena.fuseki.webapp.FusekiEnv;

http://git-wip-us.apache.org/repos/asf/jena/blob/06f59125/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/TestAdmin.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/TestAdmin.java b/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/TestAdmin.java
index cf9e192..8ce0947 100644
--- a/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/TestAdmin.java
+++ b/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/TestAdmin.java
@@ -18,11 +18,11 @@
 
 package org.apache.jena.fuseki;
 
-import static org.apache.jena.fuseki.mgt.MgtConst.opDatasets ;
-import static org.apache.jena.fuseki.mgt.MgtConst.opListBackups ;
-import static org.apache.jena.fuseki.mgt.MgtConst.opPing ;
-import static org.apache.jena.fuseki.mgt.MgtConst.opServer ;
-import static org.apache.jena.fuseki.mgt.MgtConst.opStats ;
+import static org.apache.jena.fuseki.mgt.ServerMgtConst.opDatasets ;
+import static org.apache.jena.fuseki.mgt.ServerMgtConst.opListBackups ;
+import static org.apache.jena.fuseki.mgt.ServerMgtConst.opServer ;
+import static org.apache.jena.fuseki.server.ServerConst.opPing;
+import static org.apache.jena.fuseki.server.ServerConst.opStats;
 import static org.apache.jena.riot.web.HttpOp.execHttpDelete ;
 import static org.apache.jena.riot.web.HttpOp.execHttpGet ;
 import static org.apache.jena.riot.web.HttpOp.execHttpPost ;
@@ -41,7 +41,8 @@ import org.apache.jena.atlas.json.JsonValue ;
 import org.apache.jena.atlas.lib.Lib ;
 import org.apache.jena.atlas.web.HttpException ;
 import org.apache.jena.atlas.web.TypedInputStream ;
-import org.apache.jena.fuseki.mgt.MgtConst;
+import org.apache.jena.fuseki.mgt.ServerMgtConst;
+import org.apache.jena.fuseki.server.ServerConst;
 import org.apache.jena.riot.WebContent ;
 import org.apache.jena.riot.web.HttpOp ;
 import org.apache.jena.riot.web.HttpResponseHandler ;
@@ -71,9 +72,9 @@ public class TestAdmin extends AbstractFusekiTest {
         JsonValue jv = httpGetJson(ServerCtl.urlRoot()+"$/"+opServer) ;
         JsonObject obj = jv.getAsObject() ;
         // Now optional : assertTrue(obj.hasKey(JsonConst.admin)) ;
-        assertTrue(obj.hasKey(MgtConst.datasets)) ;
-        assertTrue(obj.hasKey(MgtConst.uptime)) ;
-        assertTrue(obj.hasKey(MgtConst.startDT)) ;
+        assertTrue(obj.hasKey(ServerConst.datasets)) ;
+        assertTrue(obj.hasKey(ServerMgtConst.uptime)) ;
+        assertTrue(obj.hasKey(ServerMgtConst.startDT)) ;
     }
 
     @Test public void server_2() {


[14/27] jena git commit: JENA-1594: GSP and Quads filtered access. Tests.

Posted by an...@apache.org.
JENA-1594: GSP and Quads filtered access. Tests.

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

Branch: refs/heads/master
Commit: 689c4cf886facae3eec26208de059905db40408e
Parents: aa65883
Author: Andy Seaborne <an...@apache.org>
Authored: Fri Aug 24 17:38:42 2018 +0100
Committer: Andy Seaborne <an...@apache.org>
Committed: Fri Aug 24 17:38:42 2018 +0100

----------------------------------------------------------------------
 .../fuseki/access/DatasetGraphFiltered.java     | 144 ++++++++
 .../fuseki/access/Filtered_REST_Quads_R.java    |  40 ++-
 .../fuseki/access/Filtered_SPARQL_GSP_R.java    |  47 +--
 .../access/Filtered_SPARQL_QueryDataset.java    |   2 +
 .../jena/fuseki/access/SecurityPolicy.java      |  17 +
 .../jena/fuseki/access/SecurityRegistry.java    |   6 +-
 .../access/AbstractTestSecurityAssembler.java   | 331 +++++++++++++++++++
 .../fuseki/access/TS_SecurityFiltering.java     |   4 +-
 .../fuseki/access/TestAssemblerSeparate.java    |  26 ++
 .../jena/fuseki/access/TestAssemblerShared.java |  26 ++
 .../fuseki/access/TestSecurityAssembler.java    | 193 -----------
 .../access/TestSecurityAssemblerBuild.java      |  46 +++
 .../fuseki/access/TestSecurityFilterFuseki.java | 138 +++++++-
 .../apache/jena/fuseki/build/FusekiBuilder.java |   3 +
 .../org/apache/jena/fuseki/jetty/JettyLib.java  |   2 +-
 .../jena/fuseki/servlets/REST_Quads_R.java      |  10 +-
 .../apache/jena/fuseki/servlets/SPARQL_GSP.java |  19 +-
 .../jena/fuseki/servlets/SPARQL_GSP_R.java      |  16 +-
 .../jena/fuseki/servlets/SPARQL_Query.java      |  10 +-
 19 files changed, 818 insertions(+), 262 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/689c4cf8/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DatasetGraphFiltered.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DatasetGraphFiltered.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DatasetGraphFiltered.java
new file mode 100644
index 0000000..a7842ba
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DatasetGraphFiltered.java
@@ -0,0 +1,144 @@
+/*
+ * 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.fuseki.access;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.function.Predicate;
+
+import org.apache.jena.atlas.iterator.Iter;
+import org.apache.jena.graph.Graph;
+import org.apache.jena.graph.Node;
+import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.core.DatasetGraphReadOnly;
+import org.apache.jena.sparql.core.GraphView;
+import org.apache.jena.sparql.core.Quad;
+import org.apache.jena.sparql.graph.GraphReadOnly;
+import org.apache.jena.sparql.graph.GraphUnionRead;
+
+/**
+ * A read-only {@link DatasetGraph} that applies a filter testing all triples and quads
+ * returned by accessing the data. Only quads where the filter tests for "true" are exposed. 
+ * 
+ */
+public class DatasetGraphFiltered extends DatasetGraphReadOnly {
+    
+    // Or DatasetGraphTriplesQuads
+    
+    // Core operations at the triple/quad level.
+    // This would work for TDB where the root access is triples/quads
+    // but not for graph-style root access. 
+    /*
+  Write operations
+    add(Quad)
+    delete(Quad)
+    add(Node, Node, Node, Node)
+    delete(Node, Node, Node, Node)
+    deleteAny(Node, Node, Node, Node)
+    clear()
+    
+  Read operations  
+    listGraphNodes()
+    isEmpty()
+    find()
+    find(Quad)
+    find(Node, Node, Node, Node)
+    findNG(Node, Node, Node, Node)
+    contains(Quad)
+    contains(Node, Node, Node, Node)
+    size()
+    toString()
+     */
+    /*
+    getGraph(Node)
+    getDefaultGraph()
+    containsGraph(Node)
+     */
+
+    private final Predicate<Quad> quadFilter;
+    private final Collection<Node> visibleGraphs;
+
+    public DatasetGraphFiltered(DatasetGraph dsg, Predicate<Quad> filter, Collection<Node> visibleGraphs) {
+        super(dsg);
+        this.quadFilter = filter;
+        this.visibleGraphs = visibleGraphs;
+    }
+    
+    private boolean filter(Quad quad) {
+        return quadFilter.test(quad);
+    }
+
+    private Iterator<Quad> filter(Iterator<Quad> iter) {
+        return Iter.filter(iter, this::filter);
+    }
+    
+    // Need to intercept these because otherwise that are a GraphView of the wrapped "dsg", not this one.  
+
+    @Override
+    public Graph getDefaultGraph()
+    {
+        Graph g = GraphView.createDefaultGraph(this);
+        return new GraphReadOnly(g);
+    }
+    
+    @Override
+    public Graph getGraph(Node graphNode) {
+        if ( Quad.isUnionGraph(graphNode)) 
+            return getUnionGraph(); 
+        Graph g = GraphView.createNamedGraph(this, graphNode);
+        return new GraphReadOnly(g);
+   }
+
+    @Override
+    public Graph getUnionGraph() {
+        return new GraphUnionRead(get(), visibleGraphs);
+   }
+
+    @Override
+    public Iterator<Quad> find() {
+        return filter(super.find());
+    }
+
+    @Override public Iterator<Quad> find(Node g, Node s, Node p, Node o) {
+        // Need union handling if for general API use.
+        return filter(super.find(g, s, p, o));
+    }
+    
+    @Override public Iterator<Quad> find(Quad quad) {
+        // union
+        return filter(super.find(quad));
+    }
+    
+    @Override public Iterator<Quad> findNG(Node g, Node s, Node p , Node o) {
+        // union
+        return filter(super.findNG(g, s, p, o));
+    }
+
+    @Override public boolean contains(Node g, Node s, Node p , Node o) {
+        return filter(super.find(g, s, p, o)).hasNext();
+    }
+
+    @Override public boolean contains(Quad quad) {
+        return filter(super.find(quad)).hasNext();
+    }
+    
+    @Override public boolean isEmpty() {
+        return ! this.find().hasNext(); 
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/689c4cf8/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_REST_Quads_R.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_REST_Quads_R.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_REST_Quads_R.java
index d698ecb..9a7fdd0 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_REST_Quads_R.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_REST_Quads_R.java
@@ -19,33 +19,43 @@
 package org.apache.jena.fuseki.access;
 
 import java.util.function.Function;
+import java.util.function.Predicate;
 
 import org.apache.jena.fuseki.servlets.HttpAction;
 import org.apache.jena.fuseki.servlets.REST_Quads_R;
 import org.apache.jena.sparql.core.DatasetGraph;
-import org.apache.jena.sparql.core.DatasetGraphWrapper;
-import org.apache.jena.sparql.util.Context;
+import org.apache.jena.sparql.core.Quad;
 
+/**
+ * Filter for {@link REST_Quads_R} that inserts a security filter on read-access to the
+ * {@link DatasetGraph}.
+ */
 public class Filtered_REST_Quads_R extends REST_Quads_R {
-    public Filtered_REST_Quads_R(Function<HttpAction, String> determineUser) {}
+    
+    private final Function<HttpAction, String> requestUser;
+    
+    public Filtered_REST_Quads_R(Function<HttpAction, String> determineUser) {
+        this.requestUser = determineUser; 
+    }
 
     @Override
     protected void validate(HttpAction action) {
         super.validate(action);
     }
 
+    // Where? REST_Quads_R < REST_Quads < ActionREST < ActionService
     @Override
-    protected void doGet(HttpAction action) {
-        
-        DatasetGraph dsg0 = action.getActiveDSG();
-        DatasetGraph dsg = new DatasetGraphWrapper(dsg0) {
-            @Override public Context getContext() { return super.getContext(); }
-        };
-        // Replace datasetGraph
-        // XXX Implement access control for REST_Quads_R
-
-        HttpAction action2 = action;
-        
-        super.doGet(action2);
+    protected DatasetGraph actOn(HttpAction action) {
+        DatasetGraph dsg = action.getDataset();
+        if ( dsg == null )
+            return dsg;//super.actOn(action);
+        if ( ! DataAccessCtl.isAccessControlled(dsg) )
+            // Not access controlled.
+            return dsg;//super.actOn(action);
+        SecurityPolicy sCxt = DataAccessLib.getSecurityPolicy(action, dsg, requestUser);
+        dsg = DatasetGraphAccessControl.unwrap(dsg);
+        Predicate<Quad> filter = sCxt.predicateQuad();
+        dsg = new DatasetGraphFiltered(dsg, filter, sCxt.visibleGraphs());
+        return dsg;
     }
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/689c4cf8/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_GSP_R.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_GSP_R.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_GSP_R.java
index 1eae1d1..d2803cc 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_GSP_R.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_GSP_R.java
@@ -19,47 +19,34 @@
 package org.apache.jena.fuseki.access;
 
 import java.util.function.Function;
+import java.util.function.Predicate;
 
 import org.apache.jena.fuseki.servlets.HttpAction;
 import org.apache.jena.fuseki.servlets.SPARQL_GSP_R;
-import org.apache.jena.sparql.util.Context;
+import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.core.Quad;
 
 public class Filtered_SPARQL_GSP_R extends SPARQL_GSP_R {
     
     private final Function<HttpAction, String> requestUser;
 
     public Filtered_SPARQL_GSP_R(Function<HttpAction, String> determineUser) {
-        this.requestUser = determineUser; 
+        this.requestUser = determineUser;
     }
 
+    // Where? SPARQL_GSP_R < SPARQL_GSP < ActionREST < ActionService
     @Override
-    protected void doGet(HttpAction action) {
-        // For this, mask on target.
-        
-        // Avoid doing twice?
-        Target target = determineTarget(action);
-        
-//        Node gn = target.graphName;
-//        boolean dftGraph = target.isDefault;
-        // Yes/no based on graph.
-        
-        // 1:: DatsetGraphWrapper and modify  action.activeDGS;
-        // 2:: action.getContext().set(...
-
-        
-        DataAccessLib.getSecurityPolicy(action, action.getActiveDSG(), requestUser);
-        
-        Context context = action.getActiveDSG().getContext();
-        //action.getContext(); // Is this the DGS? No. copied.
-        action.getContext().set(DataAccessCtl.symControlledAccess, true);
-        // XXX Implement access control for GSP_R
-        SecurityRegistry securityRegistry = null;
-        String user = requestUser.apply(action);
-        
-        
-        
-        action.getContext().set(DataAccessCtl.symSecurityRegistry, securityRegistry); 
-        super.doGet(action);
+    protected DatasetGraph actOn(HttpAction action) {
+        DatasetGraph dsg = action.getDataset();
+        if ( dsg == null )
+            return dsg;//super.actOn(action);
+        if ( ! DataAccessCtl.isAccessControlled(dsg) )
+            // Not access controlled.
+            return dsg;//super.actOn(action);
+        SecurityPolicy sCxt = DataAccessLib.getSecurityPolicy(action, dsg, requestUser);
+        dsg = DatasetGraphAccessControl.unwrap(dsg);
+        Predicate<Quad> filter = sCxt.predicateQuad();
+        dsg = new DatasetGraphFiltered(dsg, filter, sCxt.visibleGraphs());
+        return dsg;
     }
-
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/689c4cf8/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_QueryDataset.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_QueryDataset.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_QueryDataset.java
index cc260f2..596bef1 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_QueryDataset.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_QueryDataset.java
@@ -41,6 +41,7 @@ public class Filtered_SPARQL_QueryDataset extends SPARQL_QueryDataset {
     @Override
     protected QueryExecution createQueryExecution(HttpAction action, Query query, Dataset dataset) {
         // Server database, not the possibly dynamically built "dataset"
+        // ---- XXX DRY
         DatasetGraph dsg = action.getDataset();
         if ( dsg == null )
             return super.createQueryExecution(action, query, dataset);
@@ -54,6 +55,7 @@ public class Filtered_SPARQL_QueryDataset extends SPARQL_QueryDataset {
             // Add back the Dataset for the createQueryExecution call.
             dataset = DatasetFactory.wrap(dsg);
         }
+        // ----
         
         QueryExecution qExec = super.createQueryExecution(action, query, dataset);
         if ( sCxt != null )

http://git-wip-us.apache.org/repos/asf/jena/blob/689c4cf8/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityPolicy.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityPolicy.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityPolicy.java
index 808e980..8d59911 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityPolicy.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityPolicy.java
@@ -20,6 +20,7 @@ package org.apache.jena.fuseki.access;
 
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Predicate;
 
@@ -65,6 +66,10 @@ public class SecurityPolicy {
         this.matchDefaultGraph = graphNames.stream().anyMatch(Quad::isDefaultGraph);
     }
     
+    public Collection<Node> visibleGraphs() {
+        return Collections.unmodifiableCollection(graphNames);
+    }
+    
     /**
      * Apply a filter suitable for the TDB-backed {@link DatasetGraph}, to the {@link Context} of the
      * {@link QueryExecution}. This does not modify the {@link DatasetGraph}
@@ -85,6 +90,18 @@ public class SecurityPolicy {
         return "dft:"+matchDefaultGraph+" / "+graphNames.toString();
     }
 
+    public Predicate<Quad> predicateQuad() {
+        return quad -> {
+            if ( quad.isDefaultGraph() )
+                return matchDefaultGraph;
+            if ( quad.isUnionGraph() ) 
+                // XXX What to do here.
+                // Need special union graph?
+                return true;
+            return graphNames.contains(quad.getGraph());
+        };
+    }
+
     /**
      * Create a GraphFilter for a TDB backed dataset.
      * 

http://git-wip-us.apache.org/repos/asf/jena/blob/689c4cf8/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityRegistry.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityRegistry.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityRegistry.java
index 1ad4098..490a785 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityRegistry.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityRegistry.java
@@ -54,8 +54,10 @@ public class SecurityRegistry extends Registry<String, SecurityPolicy>{
     
     @Override 
     public String toString() {
-        if ( true ) 
-            return "SecurityRegistry"+keys();
+        return "SecurityRegistry"+keys();
+    }        
+ 
+    public String toLongString() {
         // Long form.
         StringJoiner sj1 = new StringJoiner("\n", "{ SecurityRegistry\n", "\n}");
         super.keys().forEach(u->{

http://git-wip-us.apache.org/repos/asf/jena/blob/689c4cf8/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/AbstractTestSecurityAssembler.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/AbstractTestSecurityAssembler.java b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/AbstractTestSecurityAssembler.java
new file mode 100644
index 0000000..c062012
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/AbstractTestSecurityAssembler.java
@@ -0,0 +1,331 @@
+/*
+ * 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.fuseki.access;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.jena.atlas.iterator.Iter;
+import org.apache.jena.atlas.lib.SetUtils;
+import org.apache.jena.atlas.lib.StrUtils;
+import org.apache.jena.atlas.web.HttpException;
+import org.apache.jena.fuseki.FusekiLib;
+import org.apache.jena.fuseki.embedded.FusekiServer;
+import org.apache.jena.graph.Node;
+import org.apache.jena.query.Dataset;
+import org.apache.jena.query.QuerySolution;
+import org.apache.jena.rdf.model.Model;
+import org.apache.jena.rdf.model.RDFNode;
+import org.apache.jena.rdf.model.Resource;
+import org.apache.jena.rdfconnection.RDFConnection;
+import org.apache.jena.rdfconnection.RDFConnectionFactory;
+import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.core.Quad;
+import org.apache.jena.sparql.sse.SSE;
+import org.apache.jena.sys.JenaSystem;
+import org.apache.jena.system.Txn;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test on the assembler for data access control.
+ * <ul>
+ * <li>assem-security.ttl - two services "/database" and "/plain" each with their own dataset. 
+ * <li>assem-security-shared.ttl - two services "/database" and "/plain" with a shared dataset.
+ * </ul>
+ * 
+ * @see TestSecurityFilterFuseki TestSecurityFilterFuseki for other HTTP tests.
+ */
+
+public abstract class AbstractTestSecurityAssembler {
+    static { JenaSystem.init(); }
+    static final String DIR = "testing/Access/";
+
+//    @Parameters(name = "{index}: {0}")
+//    public static Iterable<Object[]> data() {
+//        Object[] obj1 = { DIR+"assem-security.ttl" , false };
+//        Object[] obj2 = { DIR+"assem-security-shared.ttl" , true };
+//        return Arrays.asList(obj1, obj2);
+//    }
+
+    private final String assemblerFile;
+    private static AtomicReference<String> user = new AtomicReference<>();
+
+    private boolean sharedDatabase;
+
+    // Parameterized tests don't provide a convenient way to run code at the start and end of each parameter run and access the parameters. 
+    private static FusekiServer server;
+    private FusekiServer getServer() {
+        if ( server == null )
+            server = setup(assemblerFile, false);
+        return server;
+    }
+    @AfterClass public static void afterClass() {
+        server.stop();
+        server = null;
+        user.set(null);
+    }
+    
+    @Before
+    public void before() {
+        user.set(null);
+    }
+    
+    private String getURL() {
+        getServer();
+        int port = server.getPort();
+        return "http://localhost:"+port+"/database";
+    }
+    
+    private static FusekiServer setup(String assembler, boolean sharedDatabase) {
+        int port = FusekiLib.choosePort();
+        FusekiServer server = DataAccessCtl.fusekiBuilder((a)->user.get())
+            .port(port)
+            .parseConfigFile(assembler)
+            .build();
+        server.start();
+        
+        if ( sharedDatabase ) {
+            String data = StrUtils.strjoinNL
+                ("PREFIX : <http://example/>"
+                ,"INSERT DATA {"
+                ,"   :s0 :p :o ."
+                ,"   GRAPH <http://host/graphname1> {:s1 :p :o}"
+                ,"   GRAPH <http://host/graphname3> {:s3 :p :o}"
+                ,"   GRAPH <http://host/graphname9> {:s9 :p :o}"
+                ,"}"
+                );
+            String plainUrl = "http://localhost:"+server.getPort()+"/plain";
+            try(RDFConnection conn = RDFConnectionFactory.connect(plainUrl)) {
+                conn.update(data);
+            }
+        } else {
+            DatasetGraph dsg = server.getDataAccessPointRegistry().get("/database").getDataService().getDataset();
+            Txn.executeWrite(dsg,  ()->{
+                dsg.add(SSE.parseQuad("(<http://host/graphname1> :s1 :p :o)"));
+                dsg.add(SSE.parseQuad("(<http://host/graphname3> :s3 :p :o)"));
+                dsg.add(SSE.parseQuad("(<http://host/graphname9> :s9 :p :o)"));
+            });
+        }
+        return server;
+    }
+    
+    protected AbstractTestSecurityAssembler(String assemberFile, boolean sharedDatabase) {
+        this.assemblerFile = assemberFile;
+        this.sharedDatabase = sharedDatabase ;
+    }
+
+    private static Node s1 = SSE.parseNode(":s1"); 
+    private static Node s2 = SSE.parseNode(":s2"); 
+    private static Node s3 = SSE.parseNode(":s3");
+    private static Node s9 = SSE.parseNode(":s9"); 
+
+        // The access controlled dataset.
+
+//        { SecurityRegistry
+//            user1 -> dft:false / [http://host/graphname2, http://host/graphname1, http://host/graphname3]
+//            user2 -> dft:false / [http://host/graphname9]
+//            userZ -> dft:false / [http://host/graphnameZ]
+//            user3 -> dft:false / [http://host/graphname4, http://host/graphname3, http://host/graphname5]
+//          }
+        
+        
+    @Test public void query_user1() {         
+        user.set("user1");
+        try(RDFConnection conn = RDFConnectionFactory.connect(getURL())) {
+            Set<Node> visible = query(conn, "SELECT * { GRAPH ?g { ?s ?p ?o }}");
+            assertSeen(visible, s1, s3);
+        }
+    }
+
+    @Test public void query_userX() {
+        user.set("userX"); // No such user in the registry
+        try(RDFConnection conn = RDFConnectionFactory.connect(getURL())) {
+            Set<Node> visible = query(conn, "SELECT * { GRAPH ?g { ?s ?p ?o }}");
+            assertSeen(visible);
+        }
+    }
+    
+    @Test public void query_no_user() {
+        user.set(null); // No user.
+        try(RDFConnection conn = RDFConnectionFactory.connect(getURL())) {
+            Set<Node> visible = query(conn, "SELECT * { GRAPH ?g { ?s ?p ?o }}");
+            assertSeen(visible);
+        }
+    }
+    
+    @Test public void query_user2() {
+        user.set("user2");
+        try(RDFConnection conn = RDFConnectionFactory.connect(getURL())) {
+            Set<Node> visible = query(conn, "SELECT * { GRAPH ?g { ?s ?p ?o }}");
+            assertSeen(visible, s9);
+        }
+    }
+    
+    @Test public void query_userZ() {
+        user.set("userZ"); // No graphs with data.
+        try(RDFConnection conn = RDFConnectionFactory.connect(getURL())) {
+            Set<Node> visible = query(conn, "SELECT * { GRAPH ?g { ?s ?p ?o }}");
+            assertSeen(visible);
+        }
+    }
+    
+    // GSP. "http://host/graphname1"
+    @Test public void gsp_dft_user1() {
+        user.set("user1");
+        try(RDFConnection conn = RDFConnectionFactory.connect(getURL())) {
+            Set<Node> visible = gsp(conn, null);
+            assertSeen(visible);
+        }
+    }
+    
+    @Test public void gsp_ng_user1() {
+        user.set("user1");
+        try(RDFConnection conn = RDFConnectionFactory.connect(getURL())) {
+            Set<Node> visible = gsp(conn, "http://host/graphname1");
+            assertSeen(visible, s1);
+        }
+    }
+    
+    @Test public void gsp_dft_user2() {
+        user.set("user2");
+        try(RDFConnection conn = RDFConnectionFactory.connect(getURL())) {
+            gsp404(conn, null);
+        }
+    }
+    
+    @Test public void gsp_ng_user2() {
+        user.set("user2");
+        try(RDFConnection conn = RDFConnectionFactory.connect(getURL())) {
+            gsp404(conn, "http://host/graphname1");
+        }
+    }
+    
+    @Test public void gsp_dft_userX() {
+        user.set("userX");
+        try(RDFConnection conn = RDFConnectionFactory.connect(getURL())) {
+            gsp404(conn, null);
+        }
+    }
+    
+    @Test public void gsp_ng_userX() {
+        user.set("userX");
+        try(RDFConnection conn = RDFConnectionFactory.connect(getURL())) {
+            gsp404(conn, "http://host/graphname1");
+        }
+    }
+
+    @Test public void gsp_dft_user_null() {
+        user.set(null);
+        try(RDFConnection conn = RDFConnectionFactory.connect(getURL())) {
+            gsp404(conn, null);
+        }
+    }
+    
+    @Test public void gsp_ng_user_null() {
+        try(RDFConnection conn = RDFConnectionFactory.connect(getURL())) {
+            gsp404(conn, "http://host/graphname1");
+        }
+    }
+    
+//        // Quads
+//        user.set("user1");
+//        try(RDFConnection conn = RDFConnectionFactory.connect(getURL())) {
+//            Set<Node> visible = dataset(conn);
+//            assertSeen(visible, s1, s3);
+//        }
+//        user.set("user2");
+//        try(RDFConnection conn = RDFConnectionFactory.connect(getURL())) {
+//            Set<Node> visible = dataset(conn);
+//            assertSeen(visible, s9);
+//        }
+//        user.set("userX");
+//        try(RDFConnection conn = RDFConnectionFactory.connect(getURL())) {
+//            Set<Node> visible = dataset(conn);
+//            assertSeen(visible);
+//        }
+//        user.set(null);
+//        try(RDFConnection conn = RDFConnectionFactory.connect(getURL())) {
+//            Set<Node> visible = dataset(conn);
+//            assertSeen(visible);
+//        }
+
+
+    private Set<Node> gsp(RDFConnection conn, String graphName) {
+        Set<Node> results = new HashSet<>();
+        Model model = graphName == null ? conn.fetch() : conn.fetch(graphName);
+        // Extract subjects.
+        Set<Node> seen = 
+            SetUtils.toSet(
+                Iter.asStream(model.listSubjects())
+                    .map(Resource::asNode)
+                );
+        return seen;
+    }
+
+    private void gsp404(RDFConnection conn, String graphName) {
+        gspHttp(conn, 404, graphName);
+    }
+
+    private void gspHttp(RDFConnection conn, int statusCode, String graphName) {
+        try {
+            gsp(conn, graphName);
+            if ( statusCode < 200 && statusCode > 299 ) 
+                fail("Should have responded with "+statusCode);
+        } catch (HttpException ex) {
+            assertEquals(statusCode, ex.getResponseCode());
+        }
+    }
+    
+    private Set<Node> dataset(RDFConnection conn) {
+        Dataset ds = conn.fetchDataset();
+        Set<Node> seen = 
+            SetUtils.toSet(
+                Iter.asStream(ds.asDatasetGraph().find())
+                    .map(Quad::getSubject)
+                    );
+        return seen;     
+    }
+
+    private Set<Node> query(RDFConnection conn, String queryString) {
+        Set<Node> results = new HashSet<>();
+        conn.queryResultSet(queryString, rs->{
+            List<QuerySolution> list = Iter.toList(rs);
+            list.stream()
+                .map(qs->qs.get("s"))
+                .filter(Objects::nonNull)
+                .map(RDFNode::asNode)
+                .forEach(n->results.add(n));
+        });
+        return results;
+    }
+
+    private static void assertSeen(Set<Node> visible, Node ... expected) {
+        Set<Node> expectedNodes = new HashSet<>(Arrays.asList(expected));
+        assertEquals(expectedNodes, visible);
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/689c4cf8/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TS_SecurityFiltering.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TS_SecurityFiltering.java b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TS_SecurityFiltering.java
index 35cb7ed..981a205 100644
--- a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TS_SecurityFiltering.java
+++ b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TS_SecurityFiltering.java
@@ -28,7 +28,9 @@ import org.junit.runners.Suite;
 @Suite.SuiteClasses( {
     TestSecurityFilterLocal.class
     , TestSecurityFilterFuseki.class
-    , TestSecurityAssembler.class
+    , TestSecurityAssemblerBuild.class
+    , TestAssemblerSeparate.class
+    , TestAssemblerShared.class
 })
 
 public class TS_SecurityFiltering {

http://git-wip-us.apache.org/repos/asf/jena/blob/689c4cf8/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestAssemblerSeparate.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestAssemblerSeparate.java b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestAssemblerSeparate.java
new file mode 100644
index 0000000..78281f1
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestAssemblerSeparate.java
@@ -0,0 +1,26 @@
+/*
+ * 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.fuseki.access;
+
+public class TestAssemblerSeparate extends AbstractTestSecurityAssembler {
+
+    public TestAssemblerSeparate() {
+        super(DIR+"assem-security.ttl", false);
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/689c4cf8/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestAssemblerShared.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestAssemblerShared.java b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestAssemblerShared.java
new file mode 100644
index 0000000..32d182b
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestAssemblerShared.java
@@ -0,0 +1,26 @@
+/*
+ * 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.fuseki.access;
+
+public class TestAssemblerShared extends AbstractTestSecurityAssembler {
+
+    public TestAssemblerShared() {
+        super(DIR+"assem-security-shared.ttl", true);
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/689c4cf8/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityAssembler.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityAssembler.java b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityAssembler.java
deleted file mode 100644
index eefdcd7..0000000
--- a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityAssembler.java
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.jena.fuseki.access;
-
-import static org.junit.Assert.assertEquals;
-
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicReference;
-
-import org.apache.jena.atlas.iterator.Iter;
-import org.apache.jena.atlas.lib.StrUtils;
-import org.apache.jena.fuseki.FusekiLib;
-import org.apache.jena.fuseki.embedded.FusekiServer;
-import org.apache.jena.graph.Node;
-import org.apache.jena.query.Dataset;
-import org.apache.jena.query.QuerySolution;
-import org.apache.jena.query.ResultSetFormatter;
-import org.apache.jena.rdf.model.RDFNode;
-import org.apache.jena.rdfconnection.RDFConnection;
-import org.apache.jena.rdfconnection.RDFConnectionFactory;
-import org.apache.jena.sparql.core.DatasetGraph;
-import org.apache.jena.sparql.core.assembler.AssemblerUtils;
-import org.apache.jena.sparql.sse.SSE;
-import org.apache.jena.system.Txn;
-import org.junit.Test;
-
-/**
- * Test on the assembler for data access control.
- * <ul>
- * <li>assem-security.ttl - two services "/database" and "/plain" each with their own dataset. 
- * <li>assem-security-shared.ttl - two services "/database" and "/plain" with a shared dataset.
- * </ul>
- */
-public class TestSecurityAssembler {
-    static final String DIR = "testing/Access/";
-    
-    // Check the main test inputs. 
-    @Test public void assembler1() { 
-        Dataset ds = (Dataset)AssemblerUtils.build(DIR+"assem-security.ttl", VocabSecurity.tAccessControlledDataset);
-    }
-    
-    @Test public void assembler2() { 
-        Dataset ds = (Dataset)AssemblerUtils.build(DIR+"assem-security-shared.ttl", VocabSecurity.tAccessControlledDataset);
-        SecurityRegistry securityRegistry = ds.getContext().get(DataAccessCtl.symSecurityRegistry);
-    }
-    
-    private static FusekiServer setup(String assembler, AtomicReference<String> user) {
-        int port = FusekiLib.choosePort();
-        FusekiServer server = DataAccessCtl.fusekiBuilder((a)->user.get())
-            .port(port)
-            .parseConfigFile(assembler)
-            .build();
-                
-        return server;
-    }
-    
-    // Two separate datasets  
-    @Test public void assembler3() {
-        AtomicReference<String> user = new AtomicReference<>();
-        FusekiServer server = setup(DIR+"assem-security.ttl", user);
-        // Add data directly to the datasets.
-        DatasetGraph dsg = server.getDataAccessPointRegistry().get("/database").getDataService().getDataset();
-        //System.out.println(dsg.getContext());
-        Txn.executeWrite(dsg,  ()->{
-            dsg.add(SSE.parseQuad("(<http://host/graphname1> :s1 :p :o)"));
-            dsg.add(SSE.parseQuad("(<http://host/graphname3> :s3 :p :o)"));
-            dsg.add(SSE.parseQuad("(<http://host/graphname9> :s9 :p :o)"));
-        });
-        server.start();
-        try {
-            testAssembler(server.getPort(), user);
-
-            // Access the uncontrolled dataset.
-            user.set(null);
-            String plainUrl = "http://localhost:"+server.getPort()+"/plain";
-            try(RDFConnection conn = RDFConnectionFactory.connect(plainUrl)) {
-                conn.update("INSERT DATA { <x:s> <x:p> 123 , 456 }");
-                conn.queryResultSet("SELECT * { ?s ?p ?o }",
-                    rs->{
-                        int x = ResultSetFormatter.consume(rs);
-                        assertEquals(2, x);
-                    });
-            }
-        } finally { server.stop(); }
-    }
-    
-    // Shared dataset
-    @Test public void assembler4() {
-        AtomicReference<String> user = new AtomicReference<>();
-        FusekiServer server = setup(DIR+"assem-security-shared.ttl", user);
-    
-        String x = StrUtils.strjoinNL
-            ("PREFIX : <http://example/>"
-            ,"INSERT DATA {"
-            ,"   GRAPH <http://host/graphname1> {:s1 :p :o}"
-            ,"   GRAPH <http://host/graphname3> {:s3 :p :o}"
-            ,"   GRAPH <http://host/graphname9> {:s9 :p :o}"
-            ,"}"
-            );
-        
-        server.start();
-        try {
-            user.set(null);
-            String plainUrl = "http://localhost:"+server.getPort()+"/plain";
-            try(RDFConnection conn = RDFConnectionFactory.connect(plainUrl)) {
-                conn.update(x);
-                conn.queryResultSet("SELECT * { GRAPH ?g { ?s ?p ?o } }",
-                    rs->{
-                        int c = ResultSetFormatter.consume(rs);
-                        assertEquals(3, c);
-                    });
-            }
-            testAssembler(server.getPort(), user);
-        } finally { server.stop(); }
-    }
-
-    private void testAssembler(int port, AtomicReference<String> user) {
-        // The access controlled dataset.
-        String url = "http://localhost:"+port+"/database";
-
-        Node s1 = SSE.parseNode(":s1"); 
-        Node s3 = SSE.parseNode(":s3");
-        Node s9 = SSE.parseNode(":s9"); 
-
-        user.set("user1");
-        try(RDFConnection conn = RDFConnectionFactory.connect(url)) {
-            Set<Node> visible = query(conn, "SELECT * { GRAPH ?g { ?s ?p ?o }}");
-            assertSeen(visible, s1, s3);
-        }
-
-        user.set("userX"); // No such user in the registry
-        try(RDFConnection conn = RDFConnectionFactory.connect(url)) {
-            Set<Node> visible = query(conn, "SELECT * { GRAPH ?g { ?s ?p ?o }}");
-            assertSeen(visible);
-        }
-        user.set(null); // No user.
-        try(RDFConnection conn = RDFConnectionFactory.connect(url)) {
-            Set<Node> visible = query(conn, "SELECT * { GRAPH ?g { ?s ?p ?o }}");
-            assertSeen(visible);
-        }
-
-        user.set("user2");
-        try(RDFConnection conn = RDFConnectionFactory.connect(url)) {
-            Set<Node> visible = query(conn, "SELECT * { GRAPH ?g { ?s ?p ?o }}");
-            assertSeen(visible, s9);
-        }
-
-        user.set("userZ"); // No graphs with data.
-        try(RDFConnection conn = RDFConnectionFactory.connect(url)) {
-            Set<Node> visible = query(conn, "SELECT * { GRAPH ?g { ?s ?p ?o }}");
-            assertSeen(visible);
-        }
-
-    }
-
-    private static void assertSeen(Set<Node> visible, Node ... expected) {
-        Set<Node> expectedNodes = new HashSet<>(Arrays.asList(expected));
-        assertEquals(expectedNodes, visible);
-    }
-    
-    private Set<Node> query(RDFConnection conn, String queryString) {
-        Set<Node> results = new HashSet<>();
-        conn.queryResultSet(queryString, rs->{
-            List<QuerySolution> list = Iter.toList(rs);
-            list.stream()
-            .map(qs->qs.get("s"))
-            .filter(Objects::nonNull)
-            .map(RDFNode::asNode)
-            .forEach(n->results.add(n));
-        });
-        return results;
-    }
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/689c4cf8/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityAssemblerBuild.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityAssemblerBuild.java b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityAssemblerBuild.java
new file mode 100644
index 0000000..14610b1
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityAssemblerBuild.java
@@ -0,0 +1,46 @@
+/*
+ * 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.fuseki.access;
+
+import static org.junit.Assert.assertNotNull;
+
+import org.apache.jena.query.Dataset;
+import org.apache.jena.sparql.core.assembler.AssemblerUtils;
+import org.apache.jena.sys.JenaSystem;
+import org.junit.Test;
+
+public class TestSecurityAssemblerBuild {
+    static { JenaSystem.init(); }
+    static final String DIR = "testing/Access/";
+
+    @Test public void assembler1() {
+        assemblerFile(DIR+"assem-security.ttl");
+    }
+    
+    @Test public void assembler2() {
+        assemblerFile(DIR+"assem-security-shared.ttl");
+    }
+    
+    private void assemblerFile(String assemblerFile) { 
+        Dataset ds = (Dataset)AssemblerUtils.build(assemblerFile, VocabSecurity.tAccessControlledDataset);
+        DatasetGraphAccessControl dsg = (DatasetGraphAccessControl)ds.asDatasetGraph();
+        SecurityRegistry securityRegistry = dsg.getRegistry();
+        assertNotNull(securityRegistry);
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/689c4cf8/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterFuseki.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterFuseki.java b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterFuseki.java
index 4eb8dd4..c44540c 100644
--- a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterFuseki.java
+++ b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterFuseki.java
@@ -30,11 +30,14 @@ import java.util.Objects;
 import java.util.Set;
 
 import org.apache.jena.atlas.iterator.Iter;
+import org.apache.jena.atlas.lib.SetUtils;
+import org.apache.jena.atlas.web.HttpException;
 import org.apache.jena.fuseki.FusekiLib;
 import org.apache.jena.fuseki.embedded.FusekiServer;
 import org.apache.jena.fuseki.jetty.JettyLib;
 import org.apache.jena.graph.Node;
 import org.apache.jena.query.QuerySolution;
+import org.apache.jena.rdf.model.Model;
 import org.apache.jena.rdf.model.RDFNode;
 import org.apache.jena.rdfconnection.RDFConnection;
 import org.apache.jena.rdfconnection.RDFConnectionFactory;
@@ -105,9 +108,11 @@ public class TestSecurityFilterFuseki {
     private static UserStore userStore() {
         PropertyUserStore propertyUserStore = new PropertyUserStore();
         String[] roles = new String[]{"**"};
-        addUserPassword(propertyUserStore, "user0", "pw0", roles);
-        addUserPassword(propertyUserStore, "user1", "pw1", roles);
-        addUserPassword(propertyUserStore, "user2", "pw2", roles);
+        addUserPassword(propertyUserStore, "userNone", "pwNone", roles);
+        addUserPassword(propertyUserStore, "userDft",  "pwDft",  roles);
+        addUserPassword(propertyUserStore, "user0",    "pw0",    roles);
+        addUserPassword(propertyUserStore, "user1",    "pw1",    roles);
+        addUserPassword(propertyUserStore, "user2",    "pw2",    roles);
         return propertyUserStore;
     }
 
@@ -166,6 +171,16 @@ public class TestSecurityFilterFuseki {
         }
     }
     
+    @Test public void query_userDft() {
+        Set<Node> results = query("userDft", "pwDft", queryAll);
+        assertSeen(results, s0);
+    }
+
+    @Test public void query_userNone() {
+        Set<Node> results = query("userNone", "pwNone", queryAll);
+        assertSeen(results);
+    }
+
     @Test public void query_user0() {
         Set<Node> results = query("user0", "pw0", queryAll);
         assertSeen(results, s0);
@@ -176,10 +191,6 @@ public class TestSecurityFilterFuseki {
         assertSeen(results, s0, s1);
     }
     
-    @Test public void query_userX() {
-        query401("userX", "pwX", queryAll);
-    }
-    
     @Test public void query_bad_user() {
         query401("userX", "pwX", queryAll);
     }
@@ -188,4 +199,117 @@ public class TestSecurityFilterFuseki {
         query401("user0", "not-the-password", queryAll);
     }
 
+    private Set<Node> gsp(String user, String password, String graphName) {
+        Set<Node> results = new HashSet<>();
+        try (RDFConnection conn = RDFConnectionFactory.connectPW(baseUrl, user, password)) {
+            Model model = graphName == null ? conn.fetch() : conn.fetch(graphName);
+            // Extract subjects.
+            Set<Node> seen = 
+                SetUtils.toSet(
+                    Iter.asStream(model.listSubjects())
+                        .map(r->r.asNode())
+                        );
+            return seen;
+        }
+    }
+
+    private void gsp401(String user, String password, String graphName) {
+        gspHttp(401, user, password, graphName); 
+    }
+    
+    private void gsp403(String user, String password, String graphName) {
+        gspHttp(403, user, password, graphName);
+    }
+
+    private void gsp404(String user, String password, String graphName) {
+        gspHttp(404, user, password, graphName);
+    }
+
+    private void gspHttp(int statusCode, String user, String password, String queryString) {
+        try {
+            gsp(user, password, queryString);
+            if ( statusCode < 200 && statusCode > 299 ) 
+                fail("Should have responded with "+statusCode);
+        } catch (HttpException ex) {
+            assertEquals(statusCode, ex.getResponseCode());
+        }
+    }
+    
+    // When a graph is not visible, it should return 404 except 
+    // for the default graph which should be empty.
+
+    @Test public void gsp_dft_userDft() {
+        Set<Node> results = gsp("userDft", "pwDft", null);
+        assertSeen(results, s0);
+    }
+    
+    @Test public void gsp_dft_userNone() {
+        Set<Node> results = gsp("userNone", "pwNone", null);
+        assertSeen(results);
+    }
+    
+    @Test public void gsp_dft_user0() {
+        Set<Node> results = gsp("user0", "pw0", null);
+        assertSeen(results, s0);
+    }
+
+    @Test public void gsp_dft_user1() {
+        Set<Node> results = gsp("user1", "pw1", null);
+        assertSeen(results, s0);
+    }
+    
+    @Test public void gsp_dft_user2() {
+        Set<Node> results = gsp("user2", "pw2", null);
+        assertSeen(results);
+    }
+    
+    @Test public void gsp_graph1_userDft() {
+        gsp404("userDft", "pwDft", "http://test/g1");
+    }
+    
+    @Test public void gsp_graph1_userNone() {
+        gsp404("userNone", "pwNone", "http://test/g1");
+    }
+    
+    @Test public void gsp_graph1_user0() {
+        gsp404("user0", "pw0", "http://test/g1");
+    }
+
+    @Test public void gsp_graph1_user1() {
+        Set<Node> results = gsp("user1", "pw1", "http://test/g1");
+        assertSeen(results, s1);
+    }
+    
+    @Test public void gsp_graph1_user2() {
+        gsp404("user2", "pw2", "http://test/g1");
+    }
+    
+    // No such graph.
+    @Test public void gsp_graphX_userDft() {
+        gsp404("userDft", "pwDft", "http://test/gX");
+    }
+    
+    @Test public void gsp_graphX_userNone() {
+        gsp404("userNone", "pwNone", "http://test/gX");
+    }
+    
+    @Test public void gsp_graphX_user0() {
+        gsp404("user0", "pw0", "http://test/gX");
+    }
+
+    @Test public void gsp_graphX_user1() {
+        gsp404("user1", "pw1", "http://test/g1X");
+    }
+    
+    @Test public void gsp_graphX_user2() {
+        gsp404("user2", "pw2", "http://test/gX");
+    }
+    
+    @Test public void gsp_bad_user() {
+        gsp401("userX", "pwX", null);
+    }
+
+    @Test public void gsp_bad_password() {
+        gsp401("user0", "not-the-password", null);
+    }
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/689c4cf8/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiBuilder.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiBuilder.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiBuilder.java
index 74eb2d4..2a673c7 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiBuilder.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiBuilder.java
@@ -45,6 +45,9 @@ import org.apache.jena.sparql.core.DatasetGraph ;
 import org.apache.jena.sparql.util.FmtUtils ;
 import org.apache.jena.vocabulary.RDF ;
 
+/**
+ * Helper functions use to construct Fuseki servers.
+ */
 public class FusekiBuilder
 {
     /** Build a DataAccessPoint, including DataService, from the description at Resource svc */ 

http://git-wip-us.apache.org/repos/asf/jena/blob/689c4cf8/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/jetty/JettyLib.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/jetty/JettyLib.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/jetty/JettyLib.java
index 0d03ab4..d45d69f 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/jetty/JettyLib.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/jetty/JettyLib.java
@@ -102,7 +102,7 @@ public class JettyLib {
         return makeUserStore(user, password, "**");
     }
     
-    /** Make a {@link UserStore} for a sigle user,password,role*/
+    /** Make a {@link UserStore} for a single user,password,role*/
     public static UserStore makeUserStore(String user, String password, String role) {
         Objects.requireNonNull(user);
         Objects.requireNonNull(password);

http://git-wip-us.apache.org/repos/asf/jena/blob/689c4cf8/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads_R.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads_R.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads_R.java
index f50d045..668f210 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads_R.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads_R.java
@@ -80,7 +80,7 @@ public class REST_Quads_R extends REST_Quads {
 
         action.beginRead() ;
         try {
-            DatasetGraph dsg = action.getActiveDSG() ;
+            DatasetGraph dsg = actOn(action); 
             action.response.setHeader("Content-type", lang.getContentType().toHeaderString());
             // ActionLib.contentNegotationQuads above
             // RDF/XML is not a choice but this code is general.
@@ -101,6 +101,14 @@ public class REST_Quads_R extends REST_Quads {
         }
     }
 
+    /**
+     * Decide on the dataset to use for the operation. Can be overrided by specialist
+     * subclasses.
+     */
+    protected DatasetGraph actOn(HttpAction action) {
+        return action.getActiveDSG() ;
+    }
+
     @Override
     protected void doOptions(HttpAction action) {
         action.response.setHeader(HttpNames.hAllow, "GET, HEAD, OPTIONS") ;

http://git-wip-us.apache.org/repos/asf/jena/blob/689c4cf8/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP.java
index 2b44dd9..1321125 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP.java
@@ -34,8 +34,13 @@ import org.apache.jena.sparql.core.DatasetGraph ;
 public abstract class SPARQL_GSP extends ActionREST
 {
     protected final static Target determineTarget(HttpAction action) {
+        DatasetGraph dsg = action.getActiveDSG(); 
+        return determineTarget(dsg, action);
+    }
+    
+    protected final static Target determineTarget(DatasetGraph dsg, HttpAction action) {
         // Delayed until inside a transaction.
-        if ( action.getActiveDSG() == null )
+        if ( dsg == null )
             ServletOps.errorOccurred("Internal error : No action graph (not in a transaction?)") ;
         
         boolean dftGraph = getOneOnly(action.request, HttpNames.paramGraphDefault) != null ;
@@ -52,16 +57,16 @@ public abstract class SPARQL_GSP extends ActionREST
                 // No name (should have been a quads operations).
                 ServletOps.errorBadRequest("Neither default graph nor named graph specified and no direct name") ;
             Node gn = NodeFactory.createURI(directName) ;
-            return namedTarget(action, directName) ;
+            return namedTarget(dsg, directName) ;
         }
         
         if ( dftGraph )
-            return Target.createDefault(action.getActiveDSG()) ;
+            return Target.createDefault(dsg) ;
         
         // Named graph
         if ( uri.equals(HttpNames.valueDefault ) )
             // But "named" default
-            return Target.createDefault(action.getActiveDSG()) ;
+            return Target.createDefault(dsg) ;
         
         // Strictly, a bit naughty on the URI resolution.  But more sensible. 
         // Base is dataset.
@@ -78,12 +83,12 @@ public abstract class SPARQL_GSP extends ActionREST
             // Bad IRI
             ServletOps.errorBadRequest("Bad IRI: "+ex.getMessage()) ;
         }
-        return namedTarget(action, absUri) ;
+        return namedTarget(dsg, absUri) ;
     }
     
-    private static Target namedTarget(HttpAction action, String graphName) {
+    private static Target namedTarget(DatasetGraph dsg, String graphName) {
         Node gn = NodeFactory.createURI(graphName) ;
-        return Target.createNamed(action.getActiveDSG(), graphName, gn) ;
+        return Target.createNamed(dsg, graphName, gn) ;
     }
 
     // struct for target

http://git-wip-us.apache.org/repos/asf/jena/blob/689c4cf8/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP_R.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP_R.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP_R.java
index 090ea70..8204136 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP_R.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP_R.java
@@ -30,6 +30,7 @@ import org.apache.jena.graph.Graph ;
 import org.apache.jena.riot.* ;
 import org.apache.jena.riot.web.HttpNames ;
 import org.apache.jena.shared.JenaException ;
+import org.apache.jena.sparql.core.DatasetGraph;
 
 /** Only the READ operations */
 public class SPARQL_GSP_R extends SPARQL_GSP
@@ -62,17 +63,20 @@ public class SPARQL_GSP_R extends SPARQL_GSP
         action.beginRead() ;
         setCommonHeaders(action.response) ;
         try {
-            Target target = determineTarget(action) ;
+            DatasetGraph dsg = actOn(action);
+            Target target = determineTarget(dsg, action) ;
             if ( action.log.isDebugEnabled() )
                 action.log.debug("GET->"+target) ;
             boolean exists = target.exists() ;
             if ( ! exists )
                 ServletOps.errorNotFound("No such graph: <"+target.name+">") ;
+            Graph g = target.graph() ;
+            if ( ! target.isDefault && g.isEmpty() )
+                ServletOps.errorNotFound("No such graph: <"+target.name+">") ;
             // If we want to set the Content-Length, we need to buffer.
             //response.setContentLength(??) ;
             String ct = lang.getContentType().toHeaderString() ;
             action.response.setContentType(ct) ;
-            Graph g = target.graph() ;
             //Special case RDF/XML to be the plain (faster, less readable) form
             RDFFormat fmt = 
                 ( lang == Lang.RDFXML ) ? RDFFormat.RDFXML_PLAIN : RDFWriterRegistry.defaultSerialization(lang) ;
@@ -91,6 +95,14 @@ public class SPARQL_GSP_R extends SPARQL_GSP
         } finally { action.endRead() ; }
     }
     
+    /**
+     * Decide on the dataset to use for the operation. Can be overrided by specialist
+     * subclasses.
+     */
+    protected DatasetGraph actOn(HttpAction action) {
+        return action.getActiveDSG() ;
+    }
+    
     @Override
     protected void doOptions(HttpAction action) {
         setCommonHeadersForOptions(action.response) ;

http://git-wip-us.apache.org/repos/asf/jena/blob/689c4cf8/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Query.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Query.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Query.java
index 6c9e5b4..a14e75a 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Query.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Query.java
@@ -388,9 +388,13 @@ public abstract class SPARQL_Query extends SPARQL_Protocol
     }
 
     private void setAnyProtocolTimeouts(QueryExecution qexec, HttpAction action) {
-//        if ( !(action.getDataService().allowTimeoutOverride) )
-//            return ;
-
+        //        if ( !(action.getDataService().allowTimeoutOverride) )
+        //            return ;
+        // See also QueryExecutionBase.setTimeouts to parse and process N,M form.
+        // ?? Set only if lower than any settings from the dataset or global contexts already in
+        // the QueryExecution.
+        // Add context setting for "allow increases".
+        // Documentation.
         long desiredTimeout = Long.MAX_VALUE ;
         String timeoutHeader = action.request.getHeader("Timeout") ;
         String timeoutParameter = action.request.getParameter("timeout") ;


[02/27] jena git commit: JENA-1594: Initial machinery with SPARQL Query filtering

Posted by an...@apache.org.
JENA-1594: Initial machinery with SPARQL Query filtering


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

Branch: refs/heads/master
Commit: 344ae5d841c8d22ce8c87e4a2fa466fe2a263582
Parents: 06f5912
Author: Andy Seaborne <an...@apache.org>
Authored: Thu Aug 23 17:13:00 2018 +0100
Committer: Andy Seaborne <an...@apache.org>
Committed: Thu Aug 23 17:15:05 2018 +0100

----------------------------------------------------------------------
 jena-fuseki2/jena-fuseki-access/pom.xml         | 104 ++++++
 .../fuseki/access/AssemblerAccessDataset.java   |  63 ++++
 .../access/AssemblerSecurityRegistry.java       | 130 +++++++
 .../jena/fuseki/access/DataAccessCtl.java       | 179 +++++++++
 .../jena/fuseki/access/DataAccessLib.java       |  58 +++
 .../access/DatasetGraphAccessControl.java       |  49 +++
 .../fuseki/access/Filtered_REST_Quads_R.java    |  51 +++
 .../fuseki/access/Filtered_SPARQL_GSP_R.java    |  65 ++++
 .../access/Filtered_SPARQL_QueryDataset.java    |  64 ++++
 .../apache/jena/fuseki/access/GraphFilter.java  |  76 ++++
 .../jena/fuseki/access/GraphFilterTDB1.java     |  70 ++++
 .../jena/fuseki/access/GraphFilterTDB2.java     |  70 ++++
 .../apache/jena/fuseki/access/InitSecurity.java |  38 ++
 .../jena/fuseki/access/SecurityPolicy.java      | 116 ++++++
 .../jena/fuseki/access/SecurityRegistry.java    |  72 ++++
 .../jena/fuseki/access/VocabSecurity.java       |  55 +++
 .../org.apache.jena.sys.JenaSubsystemLifecycle  |   1 +
 .../apache/jena/fuseki/access/GraphData.java    |  59 +++
 .../fuseki/access/TS_SecurityFiltering.java     |  42 +++
 .../fuseki/access/TestSecurityAssembler.java    | 193 ++++++++++
 .../fuseki/access/TestSecurityFilterFuseki.java | 191 ++++++++++
 .../fuseki/access/TestSecurityFilterLocal.java  | 320 ++++++++++++++++
 .../src/test/resources/log4j.properties         |  40 ++
 .../testing/Access/assem-security-shared.ttl    |  58 +++
 .../testing/Access/assem-security.ttl           |  63 ++++
 jena-fuseki2/jena-fuseki-core/pom.xml           |   2 +
 .../org/apache/jena/fuseki/jetty/JettyLib.java  | 169 +++++++++
 .../apache/jena/fuseki/server/Operation.java    |   6 +-
 .../jena/fuseki/servlets/ActionService.java     |   2 +-
 .../servlets/ServiceDispatchRegistry.java       |  10 +-
 .../jena/fuseki/embedded/FusekiServer.java      | 126 ++++---
 .../jena/fuseki/embedded/JettyServer.java       | 369 +++++++++++++++++++
 jena-fuseki2/pom.xml                            |   1 +
 .../rdfconnection/RDFConnectionFactory.java     |  25 ++
 34 files changed, 2874 insertions(+), 63 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/jena-fuseki2/jena-fuseki-access/pom.xml
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/pom.xml b/jena-fuseki2/jena-fuseki-access/pom.xml
new file mode 100644
index 0000000..867a956
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/pom.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+  <name>Apache Jena - Fuseki Data Access Control</name>
+  <artifactId>jena-fuseki-access</artifactId>
+
+  <parent>
+    <groupId>org.apache.jena</groupId>
+    <artifactId>jena-fuseki</artifactId>
+    <version>3.9.0-SNAPSHOT</version>
+  </parent> 
+
+  <packaging>jar</packaging>
+
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.jena</groupId>
+      <artifactId>jena-fuseki-embedded</artifactId>
+      <version>3.9.0-SNAPSHOT</version>
+    </dependency>
+
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-compiler-plugin</artifactId>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <executions>
+          <execution>
+            <goals>
+              <goal>test-jar</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-source-plugin</artifactId>
+        <executions>
+          <execution>
+            <id>attach-sources</id>
+            <phase>package</phase>
+            <goals>
+              <goal>jar-no-fork</goal>
+            </goals>
+          </execution>
+          <execution>
+            <id>attach-sources-test</id>
+            <goals>
+              <goal>test-jar-no-fork</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <includes>
+            <include>**/TS_*.java</include>
+          </includes>
+        </configuration>
+      </plugin>
+
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-dependency-plugin</artifactId>
+        <configuration>
+          <overWriteReleases>false</overWriteReleases>
+          <overWriteIfNewer>true</overWriteIfNewer>
+        </configuration>
+      </plugin>
+
+    </plugins>
+
+  </build>
+
+</project>

http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AssemblerAccessDataset.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AssemblerAccessDataset.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AssemblerAccessDataset.java
new file mode 100644
index 0000000..75cb155
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AssemblerAccessDataset.java
@@ -0,0 +1,63 @@
+/*
+ * 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.fuseki.access;
+
+import org.apache.jena.assembler.Assembler;
+import org.apache.jena.assembler.Mode;
+import org.apache.jena.assembler.assemblers.AssemblerBase;
+import org.apache.jena.assembler.exceptions.AssemblerException;
+import org.apache.jena.query.Dataset;
+import org.apache.jena.query.DatasetFactory;
+import org.apache.jena.rdf.model.RDFNode;
+import org.apache.jena.rdf.model.Resource;
+import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.util.graph.GraphUtils;
+
+public class AssemblerAccessDataset extends AssemblerBase {
+    
+    /*
+     * <#access_dataset>  rdf:type access:AccessControlledDataset ;
+     *    access:registry   <#securityRegistry> ;
+     *    access:dataset    <#tdb_dataset_read> ;
+     *    .
+     */
+    @Override
+    public Dataset open(Assembler a, Resource root, Mode mode) {
+        if ( ! GraphUtils.exactlyOneProperty(root, VocabSecurity.pSecurityRegistry) )
+            throw new AssemblerException(root, "Expected exactly one access:registry property"); 
+        if ( ! GraphUtils.exactlyOneProperty(root, VocabSecurity.pDataset) )
+            throw new AssemblerException(root, "Expected exactly one access:dataset property"); 
+        
+        RDFNode rnRegistry = root.getProperty(VocabSecurity.pSecurityRegistry).getObject();
+        RDFNode rnDataset = root.getProperty(VocabSecurity.pDataset).getObject();
+        
+        SecurityRegistry sr = (SecurityRegistry)a.open(rnRegistry.asResource()) ;
+        Dataset ds = (Dataset)a.open(rnDataset.asResource()) ;
+        
+        DatasetGraph dsg = new DatasetGraphAccessControl(ds.asDatasetGraph(), sr);
+        ds = DatasetFactory.wrap(dsg);
+        
+//        // Add marker
+//        ds.getContext().set(DataAccessCtl.symControlledAccess, true);
+//        // Add security registry
+//        ds.getContext().set(DataAccessCtl.symSecurityRegistry, sr);
+        return ds;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AssemblerSecurityRegistry.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AssemblerSecurityRegistry.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AssemblerSecurityRegistry.java
new file mode 100644
index 0000000..1e8a3e4
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AssemblerSecurityRegistry.java
@@ -0,0 +1,130 @@
+/*
+ * 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.fuseki.access;
+
+import java.util.List;
+
+import org.apache.jena.assembler.Assembler;
+import org.apache.jena.assembler.Mode;
+import org.apache.jena.assembler.assemblers.AssemblerBase;
+import org.apache.jena.assembler.exceptions.AssemblerException;
+import org.apache.jena.ext.com.google.common.collect.ArrayListMultimap;
+import org.apache.jena.ext.com.google.common.collect.Multimap;
+import org.apache.jena.graph.Node;
+import org.apache.jena.rdf.model.RDFList;
+import org.apache.jena.rdf.model.RDFNode;
+import org.apache.jena.rdf.model.Resource;
+import org.apache.jena.rdf.model.StmtIterator;
+import org.apache.jena.rdf.model.impl.Util;
+import org.apache.jena.sparql.util.graph.GNode;
+import org.apache.jena.sparql.util.graph.GraphList;
+import org.apache.jena.sparql.util.graph.GraphUtils;
+
+public class AssemblerSecurityRegistry extends AssemblerBase {
+
+    /**
+     * SecurityRegistry.
+     * Builds a SecurityRegistry - a map fron user name to 
+     * 
+     * <#securityRegistry> rdf:type access:SecurityRegistry ;
+     *    access:entry ("user1" <http://host/graphname1>  <http://host/graphname2> ) ;) ;
+     *    access:entry ("user1" <http://host/graphname3> ) ;
+     *    access:entry ("user2" <http://host/graphname3> ) ;
+     *    .
+     * 
+     * access:entry [ :user "user2" ; :graphs (<http://host/graphname3> ) ] ;
+     */
+    
+    @Override
+    public SecurityRegistry open(Assembler a, Resource root, Mode mode) {
+        SecurityRegistry registry = new SecurityRegistry();
+        // Java walking gives better error messages.
+        StmtIterator sIter = root.listProperties(VocabSecurity.pEntry);
+        if ( ! sIter.hasNext() )
+            throw new AssemblerException(root, "No access entries");
+        Multimap<String, Node> map = ArrayListMultimap.create();
+        
+        sIter.forEachRemaining(s->{
+            RDFNode n = s.getObject(); 
+            if ( ! n.isResource()) 
+                throw new AssemblerException(root, "Found access:entry with non-resource"); 
+            Resource r = (Resource)n;
+            GNode entry = new GNode(root.getModel().getGraph(), n.asNode());
+            if ( GraphList.isListNode(entry) ) {
+                // Format:: access:entry ("user1" <http://host/graphname1>  <http://host/graphname2> ) ;
+                parseList(map, root, entry);
+            } else if ( r.hasProperty(VocabSecurity.pUser) || r.hasProperty(VocabSecurity.pGraphs) ) {
+                // Format:: access:entry [ :user "user2" ; :graphs (<http://host/graphname3> ) ]
+                parseStruct(map, root, r);
+            } else
+                throw new AssemblerException(root, "Found access:entry but failed to parse the object: "+s.getSubject());
+        });
+        
+        map.keySet().forEach(u->{
+            SecurityPolicy policy = new SecurityPolicy(map.get(u));
+            registry.put(u, policy);
+        });
+        
+        return registry;
+    }
+
+    /**Format:: access:entry ("user1" <http://host/graphname1>  <http://host/graphname2> ) ; */
+    private void parseList(Multimap<String, Node> map, Resource root, GNode entry) {
+        List<Node> members = GraphList.members(entry);
+        // string, then URIs.
+        if ( members.isEmpty() )
+            throw new AssemblerException(root, "Found access:entry with an empty list");
+        Node userNode = members.get(0);
+        if ( !  Util.isSimpleString(userNode) ) {}
+        String user =  userNode.getLiteralLexicalForm();
+        for ( int i = 1 ; i < members.size() ; i++ ) {
+            Node gn = members.get(i);
+            //if ( gn.isBlank() )
+            if ( ! gn.isURI() ) { }
+            //System.out.printf("L: user %s : access : %s\n", user, gn);
+            map.put(user, gn);
+        }
+    }
+
+    /** Format:: access:entry [ :user "user2" ; :graphs (<http://host/graphname3> ) ] */
+    private void parseStruct(Multimap<String, Node> map, Resource root, Resource r) {
+        if ( ! GraphUtils.exactlyOneProperty(r, VocabSecurity.pUser) )
+            throw new AssemblerException(root, "Expected exactly one access:user property for "+r); 
+        if ( ! GraphUtils.exactlyOneProperty(r, VocabSecurity.pGraphs) )
+            throw new AssemblerException(root, "Expected exactly one access:graphs property for "+r); 
+        
+        String user = GraphUtils.getStringValue(r, VocabSecurity.pUser);
+        r.listProperties(VocabSecurity.pGraphs).mapWith(s->s.getObject()).forEachRemaining(x->{
+            if ( x.isURIResource() ) {
+                //System.out.printf("S: user %s : access : %s\n", user, x.asNode());
+                map.put(user, x.asNode());
+            } else {
+                // List?
+                RDFList list = x.as(RDFList.class);
+                list.iterator().forEachRemaining(rn->{
+                    if ( ! rn.isURIResource() )
+                        throw new AssemblerException(root, "Not a graph name: "+rn);
+                    //System.out.printf("S: user %s : access : %s\n", user, rn.asNode());
+                    map.put(user, rn.asNode());
+                });
+            }
+        });
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessCtl.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessCtl.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessCtl.java
new file mode 100644
index 0000000..85b1f31
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessCtl.java
@@ -0,0 +1,179 @@
+/*
+ * 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.fuseki.access;
+
+import java.util.function.Function;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.jena.fuseki.embedded.FusekiServer;
+import org.apache.jena.fuseki.server.Operation;
+import org.apache.jena.fuseki.servlets.ActionService;
+import org.apache.jena.fuseki.servlets.HttpAction;
+import org.apache.jena.fuseki.servlets.ServiceDispatchRegistry;
+import org.apache.jena.query.Dataset;
+import org.apache.jena.query.DatasetFactory;
+import org.apache.jena.riot.WebContent;
+import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.util.Context;
+import org.apache.jena.sparql.util.Symbol;
+import org.eclipse.jetty.security.SecurityHandler;
+
+/** A library of operations related to data acess sexurity for Fuseki */  
+public class DataAccessCtl {
+
+    /**
+     * Flag for whether this is data access controlled or not - boolean false or undef for "not
+     * controlled".
+     */
+    public static final Symbol   symControlledAccess      = Symbol.create(VocabSecurity.getURI() + "controlled");
+    
+    /**
+     * Symbol for the {@link SecurityRegistry}. Must be present if
+     * {@link #symControlledAccess} indicates data access control.
+     */
+    public static final Symbol   symSecurityRegistry      = Symbol.create(VocabSecurity.getURI() + "registry");
+
+    /** Get the user from the servlet context via {@link HttpServletRequest#getRemoteUser} */ 
+    public static final Function<HttpAction, String> requestUserServlet = (action)->action.request.getRemoteUser();
+
+//    /** Build a fuseki server with controlled access enabled. */
+//    public static FusekiServer controlledFuseki(int port, Function<HttpAction, String> determineUser, Consumer<FusekiServer.Builder> configure) {
+//        FusekiServer.Builder b = controlledFuseki(port, determineUser);
+//        configure.accept(b);
+//        return b.build();
+//    }
+//    
+//    /** Builder for a read-only Fuseki server with controlled access actions enabled. */ 
+//    public static FusekiServer.Builder controlledFuseki(int port, Function<HttpAction, String> determineUser) {
+//        ServiceDispatchRegistry sdr = new ServiceDispatchRegistry(false);
+//        ActionService actionQuery   = new Filtered_SPARQL_QueryDataset(determineUser);
+//        ActionService actionGspR    = new Filtered_SPARQL_GSP_R(determineUser);
+//        ActionService actionQuadsR  = new Filtered_REST_Quads_R(determineUser);
+//        
+//        // Create empty and only add "read" operations.
+//        FusekiServer.Builder builder = FusekiServer.create(sdr)
+//            .port(port)
+//            .registerOperation(Operation.Query,     WebContent.contentTypeSPARQLQuery, actionQuery)
+//            .registerOperation(Operation.GSP_R,     actionGspR)
+//            .registerOperation(Operation.Quads_R,   actionQuadsR);
+//        return builder;
+//    }
+
+    /**
+     * Enable data access control on a {@link DatasetGraph}. This modifies the
+     * {@link DatasetGraph}'s {@link Context}.
+     */
+    public static void controlledDataset(DatasetGraph dsg, SecurityRegistry reg) {
+        // Or wrapper.
+        dsg.getContext().set(symControlledAccess, true);
+        dsg.getContext().set(symSecurityRegistry, reg);
+    }
+
+    /**
+     * Enable data access control on a {@link Dataset}. This modifies the
+     * {@link Dataset}'s {@link Context}.
+     */
+    public static void controlledDataset(Dataset ds, SecurityRegistry reg) {
+        ds.getContext().set(symControlledAccess, true);
+        ds.getContext().set(symSecurityRegistry, reg);
+    }
+
+    /**
+     * Return a {@link DatasetGraph} with added data access control. Use of the original
+     * {@code DatasetGraph} is not controlled.
+     */
+    public static Dataset wrapControlledDataset(Dataset dsBase, SecurityRegistry reg) {
+        DatasetGraph dsg = wrapControlledDataset(dsBase.asDatasetGraph(), reg);
+        return DatasetFactory.wrap(dsg);
+    }
+    
+    /**
+     * Return a {@link DatasetGraph} with added data access control. Use of the original
+     * {@code DatasetGraph} is not controlled.
+     */
+    public static DatasetGraph wrapControlledDataset(DatasetGraph dsgBase, SecurityRegistry reg) {
+        if ( dsgBase instanceof DatasetGraphAccessControl ) {
+            DatasetGraphAccessControl dsgx = (DatasetGraphAccessControl)dsgBase;
+            if ( reg == dsgx.getRegistry() )
+                return dsgx;
+            throw new IllegalArgumentException("DatasetGraph is alerady wrapped on a DatasetGraphAccessControl with a different SecurityRegistry");
+        }
+        
+        DatasetGraphAccessControl dsg1 = new DatasetGraphAccessControl(dsgBase, reg);
+        return dsg1;
+    } 
+
+    /**
+     * Return a {@code FusekiServer.Builder} setup for data access control
+     * but with no Jetty security handler.
+     */
+    public static FusekiServer.Builder fusekiBuilder(Function<HttpAction, String> determineUser) {
+        return fusekiBuilder(null, determineUser);
+    }
+
+    /**
+     * Return a {@code FusekiServer.Builder} setup for data access control and with a
+     * Jetty security handler.
+     */
+    public static FusekiServer.Builder fusekiBuilder(SecurityHandler securityHandler, Function<HttpAction, String> determineUser) {
+        FusekiServer.Builder builder = FusekiServer.create();
+        if ( securityHandler != null )
+            builder.securityHandler(securityHandler);
+        // Replace the standard operations with the SecurityRegistry processing ones. 
+        builder.registerOperation(Operation.Query, WebContent.contentTypeSPARQLQuery, new Filtered_SPARQL_QueryDataset(determineUser));
+        builder.registerOperation(Operation.GSP_R, new Filtered_SPARQL_GSP_R(determineUser));
+        builder.registerOperation(Operation.Quads_R, new Filtered_REST_Quads_R(determineUser));
+        return builder;
+    }
+
+    /**
+     * Modify in-place an existing {@link FusekiServer} so that the read-operations for
+     * query/GSP/Quads go to the data-filtering versions of the {@link ActionService ActionServices}.
+     * (It is better to create the server via {@link #DataAccessCtl.builder} first rather than modify afterwards.) 
+     */
+    public static void enable(FusekiServer server, Function<HttpAction, String> determineUser) {
+        /* 
+         * Reconfigure standard Jena Fuseki, replacing the default implementation of "query"
+         * with a filtering one.  This for this server only. 
+         */
+        // The mapping operation to handler is in the ServiceDispatchRegistry and is per
+        // server (per servlet context). "registerOrReplace" would be a better name,
+        ActionService queryServletAccessFilter = new Filtered_SPARQL_QueryDataset(determineUser);
+        ServletContext cxt = server.getServletContext();
+        ServiceDispatchRegistry.get(cxt).register(Operation.Query, WebContent.contentTypeSPARQLQuery, queryServletAccessFilter);
+        ServiceDispatchRegistry.get(cxt).register(Operation.GSP_R, null, new Filtered_SPARQL_GSP_R(determineUser));
+        ServiceDispatchRegistry.get(cxt).register(Operation.Quads_R, null, new Filtered_REST_Quads_R(determineUser));
+    }
+    
+    /**
+     * Return whether a {@code DatasetGraph} has access control, either because it is wrapped in
+     * {@link DatasetGraphAccessControl} or because it has the context settings.
+     */
+    public static boolean isAccessControlled(DatasetGraph dsg) {
+        if ( dsg instanceof DatasetGraphAccessControl )
+            return true;
+        if ( dsg.getContext().isDefined(DataAccessCtl.symControlledAccess) )
+            return true;
+        if ( dsg.getContext().isDefined(DataAccessCtl.symSecurityRegistry) )
+            return true;
+        return false;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessLib.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessLib.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessLib.java
new file mode 100644
index 0000000..4329cc6
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessLib.java
@@ -0,0 +1,58 @@
+/*
+ * 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.fuseki.access;
+
+import java.util.function.Function;
+
+import org.apache.jena.fuseki.servlets.HttpAction;
+import org.apache.jena.fuseki.servlets.ServletOps;
+import org.apache.jena.sparql.core.DatasetGraph;
+
+/** Package-only operations */
+class DataAccessLib {
+    
+    /** Determine the {@link SecurityPolicy} for this request */  
+    static SecurityPolicy getSecurityPolicy(HttpAction action, DatasetGraph dataset, Function<HttpAction, String> requestUser) {
+        SecurityRegistry registry = getSecurityRegistry(action, dataset);
+        if ( registry == null )
+            ServletOps.errorOccurred("Internal Server Error");
+
+        SecurityPolicy sCxt = null;
+        String user = requestUser.apply(action);
+        sCxt = registry.get(user);
+        if ( sCxt == null )
+            sCxt = noSecurityPolicy();
+        return sCxt;
+    }
+
+    
+    /** Get the {@link SecurityRegistry} for an action/query/dataset */
+    static SecurityRegistry getSecurityRegistry(HttpAction action, DatasetGraph dsg) {
+        if ( dsg instanceof DatasetGraphAccessControl )
+            return ((DatasetGraphAccessControl)dsg).getRegistry();
+        return dsg.getContext().get(DataAccessCtl.symSecurityRegistry);
+    }
+
+    static SecurityPolicy noSecurityPolicy() {
+        ServletOps.errorForbidden();
+        // Should not get here.
+        throw new InternalError();
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DatasetGraphAccessControl.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DatasetGraphAccessControl.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DatasetGraphAccessControl.java
new file mode 100644
index 0000000..d770ade
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DatasetGraphAccessControl.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.access;
+
+import java.util.Objects;
+
+import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.core.DatasetGraphWrapper;
+
+/** DatasetGraph layer that carries a SecurityRegistry. */ 
+class DatasetGraphAccessControl extends DatasetGraphWrapper {
+    
+    private SecurityRegistry registry = null; 
+
+    public DatasetGraphAccessControl(DatasetGraph dsg, SecurityRegistry registry) {
+        super(Objects.requireNonNull(dsg));
+        this.registry = Objects.requireNonNull(registry); 
+    }
+    
+    public SecurityRegistry getRegistry() {
+        return registry;
+    }
+
+    /**
+     * Return the underlying {@code DatasetGraph}. If the argument is not a
+     * {@code DatasetGraphAccessControl}, return the argument.
+     */
+    public static DatasetGraph unwrap(DatasetGraph dsg) {
+        if ( ! ( dsg instanceof DatasetGraphAccessControl ) )
+            return dsg;
+        return ((DatasetGraphAccessControl)dsg).getWrapped();
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_REST_Quads_R.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_REST_Quads_R.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_REST_Quads_R.java
new file mode 100644
index 0000000..d698ecb
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_REST_Quads_R.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.access;
+
+import java.util.function.Function;
+
+import org.apache.jena.fuseki.servlets.HttpAction;
+import org.apache.jena.fuseki.servlets.REST_Quads_R;
+import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.core.DatasetGraphWrapper;
+import org.apache.jena.sparql.util.Context;
+
+public class Filtered_REST_Quads_R extends REST_Quads_R {
+    public Filtered_REST_Quads_R(Function<HttpAction, String> determineUser) {}
+
+    @Override
+    protected void validate(HttpAction action) {
+        super.validate(action);
+    }
+
+    @Override
+    protected void doGet(HttpAction action) {
+        
+        DatasetGraph dsg0 = action.getActiveDSG();
+        DatasetGraph dsg = new DatasetGraphWrapper(dsg0) {
+            @Override public Context getContext() { return super.getContext(); }
+        };
+        // Replace datasetGraph
+        // XXX Implement access control for REST_Quads_R
+
+        HttpAction action2 = action;
+        
+        super.doGet(action2);
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_GSP_R.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_GSP_R.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_GSP_R.java
new file mode 100644
index 0000000..1eae1d1
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_GSP_R.java
@@ -0,0 +1,65 @@
+/*
+ * 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.fuseki.access;
+
+import java.util.function.Function;
+
+import org.apache.jena.fuseki.servlets.HttpAction;
+import org.apache.jena.fuseki.servlets.SPARQL_GSP_R;
+import org.apache.jena.sparql.util.Context;
+
+public class Filtered_SPARQL_GSP_R extends SPARQL_GSP_R {
+    
+    private final Function<HttpAction, String> requestUser;
+
+    public Filtered_SPARQL_GSP_R(Function<HttpAction, String> determineUser) {
+        this.requestUser = determineUser; 
+    }
+
+    @Override
+    protected void doGet(HttpAction action) {
+        // For this, mask on target.
+        
+        // Avoid doing twice?
+        Target target = determineTarget(action);
+        
+//        Node gn = target.graphName;
+//        boolean dftGraph = target.isDefault;
+        // Yes/no based on graph.
+        
+        // 1:: DatsetGraphWrapper and modify  action.activeDGS;
+        // 2:: action.getContext().set(...
+
+        
+        DataAccessLib.getSecurityPolicy(action, action.getActiveDSG(), requestUser);
+        
+        Context context = action.getActiveDSG().getContext();
+        //action.getContext(); // Is this the DGS? No. copied.
+        action.getContext().set(DataAccessCtl.symControlledAccess, true);
+        // XXX Implement access control for GSP_R
+        SecurityRegistry securityRegistry = null;
+        String user = requestUser.apply(action);
+        
+        
+        
+        action.getContext().set(DataAccessCtl.symSecurityRegistry, securityRegistry); 
+        super.doGet(action);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_QueryDataset.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_QueryDataset.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_QueryDataset.java
new file mode 100644
index 0000000..cc260f2
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_QueryDataset.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.fuseki.access;
+
+import java.util.function.Function;
+
+import org.apache.jena.fuseki.servlets.ActionService;
+import org.apache.jena.fuseki.servlets.HttpAction;
+import org.apache.jena.fuseki.servlets.SPARQL_QueryDataset;
+import org.apache.jena.query.Dataset;
+import org.apache.jena.query.DatasetFactory;
+import org.apache.jena.query.Query;
+import org.apache.jena.query.QueryExecution;
+import org.apache.jena.sparql.core.DatasetGraph;
+
+/** A Query {@link ActionService} that inserts a security filter on each query. */
+final
+public class Filtered_SPARQL_QueryDataset extends SPARQL_QueryDataset {
+    private final Function<HttpAction, String> requestUser;
+
+    public Filtered_SPARQL_QueryDataset(Function<HttpAction, String> requestUser) {
+        this.requestUser = requestUser; 
+    }
+
+    @Override
+    protected QueryExecution createQueryExecution(HttpAction action, Query query, Dataset dataset) {
+        // Server database, not the possibly dynamically built "dataset"
+        DatasetGraph dsg = action.getDataset();
+        if ( dsg == null )
+            return super.createQueryExecution(action, query, dataset);
+        if ( ! DataAccessCtl.isAccessControlled(dsg) )
+            return super.createQueryExecution(action, query, dataset);
+        
+        SecurityPolicy sCxt = DataAccessLib.getSecurityPolicy(action, dataset.asDatasetGraph(), requestUser);
+        if ( dsg instanceof DatasetGraphAccessControl ) {
+            // Take off one layer.
+            dsg = DatasetGraphAccessControl.unwrap(dsg);
+            // Add back the Dataset for the createQueryExecution call.
+            dataset = DatasetFactory.wrap(dsg);
+        }
+        
+        QueryExecution qExec = super.createQueryExecution(action, query, dataset);
+        if ( sCxt != null )
+            sCxt.filterTDB(dsg, qExec);
+        return qExec;
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/GraphFilter.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/GraphFilter.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/GraphFilter.java
new file mode 100644
index 0000000..33283d6
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/GraphFilter.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.access;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.function.Predicate;
+
+import org.apache.jena.atlas.lib.tuple.Tuple;
+import org.apache.jena.sparql.util.Symbol;
+
+/**
+ * A graph filter for TDB1 and TDB2 (by generic type).
+ * <p>
+ * This filter takes a collection of graph names and returns true from
+ * {@link #test(Tuple)} if the tuple graph slot is in the collection of graph names or
+ * matchDefaultGraph is true. It can be used as an "allow" filter; it can be negated to
+ * become a "deny" filter.
+ * 
+ * @see GraphFilterTDB1#graphFilter
+ * @see GraphFilterTDB2#graphFilter
+ */
+public abstract class GraphFilter<X> implements Predicate<Tuple<X>> {
+    private final Set<X> graphs;
+    private final boolean matchDefaultGraph;
+//    // This makes the GraphFilter stateful.
+//    private X slot = null;
+    
+    protected GraphFilter(Collection<X> matches, boolean matchDefaultGraph) {
+        this.graphs = new HashSet<X>(matches);
+        this.matchDefaultGraph = matchDefaultGraph;
+    }
+    
+    public abstract Symbol getContextKey();
+    
+    @Override
+    public boolean test(Tuple<X> t) {
+        if ( t.len() == 3 ) {
+            // Default graph.
+            return matchDefaultGraph; 
+        }
+        X g = t.get(0);
+        boolean b = perGraphTest(g);
+        return b;
+    }
+
+    // The per graph test.
+    private boolean perGraphTest(X g) {
+        return graphs.contains(g);
+//        if ( g == slot ) {
+//            System.err.println("Slot hit");
+//            return true;
+//        }
+//        boolean b = matches.contains(g);
+//        if ( b )
+//            slot = g ;
+//        return b;
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/GraphFilterTDB1.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/GraphFilterTDB1.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/GraphFilterTDB1.java
new file mode 100644
index 0000000..d15c57b
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/GraphFilterTDB1.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.fuseki.access;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.jena.atlas.lib.ListUtils;
+import org.apache.jena.graph.Node;
+import org.apache.jena.query.QueryExecution;
+import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.util.Symbol;
+import org.apache.jena.system.Txn;
+import org.apache.jena.tdb.store.NodeId;
+import org.apache.jena.tdb.store.nodetable.NodeTable;
+import org.apache.jena.tdb.sys.SystemTDB;
+import org.apache.jena.tdb.sys.TDBInternal;
+
+/** {@link GraphFilter} for TDB1 */ 
+class GraphFilterTDB1 extends GraphFilter<NodeId> {
+
+    private GraphFilterTDB1(Collection<NodeId> matches, boolean matchDefaultGraph) {
+        super(matches, matchDefaultGraph);
+    }
+
+    @Override
+    public Symbol getContextKey() {
+        return SystemTDB.symTupleFilter;
+    }
+    
+    /**
+     * Create a graph filter for a TDB1 {@link DatasetGraph}. The filter matches (returns
+     * true) for Tuples where the graph slot in quad is in the collection or for triples in the default
+     * graph according the boolean.
+     * 
+     * @see SecurityPolicy#filterTDB(DatasetGraph, QueryExecution)
+     */
+    public static GraphFilterTDB1 graphFilter(DatasetGraph dsg, Collection<Node> namedGraphs, boolean matchDefaultGraph) {
+        if ( ! TDBInternal.isTDB1(dsg) )
+            throw new IllegalArgumentException("DatasetGraph is not TDB1-backed");
+        List<NodeId> x =  
+            Txn.calculateRead(dsg, ()->{
+                NodeTable nt = TDBInternal.getDatasetGraphTDB(dsg).getQuadTable().getNodeTupleTable().getNodeTable();
+                return 
+                    ListUtils.toList(
+                        namedGraphs.stream()
+                        .map(n->nt.getNodeIdForNode(n))
+                        .filter(Objects::nonNull)
+                        );
+            });
+        return new GraphFilterTDB1(x, matchDefaultGraph);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/GraphFilterTDB2.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/GraphFilterTDB2.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/GraphFilterTDB2.java
new file mode 100644
index 0000000..cf95244
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/GraphFilterTDB2.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.fuseki.access;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.jena.atlas.lib.ListUtils;
+import org.apache.jena.graph.Node;
+import org.apache.jena.query.QueryExecution;
+import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.util.Symbol;
+import org.apache.jena.system.Txn;
+import org.apache.jena.tdb2.store.NodeId;
+import org.apache.jena.tdb2.store.nodetable.NodeTable;
+import org.apache.jena.tdb2.sys.SystemTDB;
+import org.apache.jena.tdb2.sys.TDBInternal;
+
+/** {@link GraphFilter} for TDB2 */
+class GraphFilterTDB2 extends GraphFilter<NodeId> {
+
+    private GraphFilterTDB2(Collection<NodeId> matches, boolean matchDefaultGraph) {
+        super(matches, matchDefaultGraph);
+    }
+
+    @Override
+    public Symbol getContextKey() {
+        return SystemTDB.symTupleFilter;
+    }
+    
+    /**
+     * Create a graph filter for a TDB2 {@link DatasetGraph}. The filter matches (returns
+     * true) for Tuples where the graph slot in quad is in the collection or for triples in the default
+     * graph according the boolean.
+     * 
+     * @see SecurityPolicy#filterTDB(DatasetGraph, QueryExecution)
+     */
+    public static GraphFilterTDB2 graphFilter(DatasetGraph dsg, Collection<Node> namedGraphs, boolean matchDefaultGraph) {
+        if ( ! TDBInternal.isTDB2(dsg) )
+            throw new IllegalArgumentException("DatasetGraph is not TDB2-backed");
+        List<NodeId> x =  
+            Txn.calculateRead(dsg, ()->{
+                NodeTable nt = TDBInternal.getDatasetGraphTDB(dsg).getQuadTable().getNodeTupleTable().getNodeTable();
+                return 
+                    ListUtils.toList(
+                        namedGraphs.stream()
+                        .map(n->nt.getNodeIdForNode(n))
+                        .filter(Objects::nonNull)
+                        );
+            });
+        return new GraphFilterTDB2(x, matchDefaultGraph);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/InitSecurity.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/InitSecurity.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/InitSecurity.java
new file mode 100644
index 0000000..55e760e
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/InitSecurity.java
@@ -0,0 +1,38 @@
+/*
+ * 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.fuseki.access;
+
+import org.apache.jena.sys.JenaSubsystemLifecycle;
+import org.apache.jena.sys.JenaSystem;
+
+public class InitSecurity implements JenaSubsystemLifecycle {
+
+    @Override
+    public void start() {
+        JenaSystem.logLifecycle("InitSecurity - start") ;
+        VocabSecurity.init();
+        JenaSystem.logLifecycle("InitSecurity - finish") ;
+    }
+
+    @Override
+    public void stop() {}
+    
+    @Override
+    public int level() { return 100; }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityPolicy.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityPolicy.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityPolicy.java
new file mode 100644
index 0000000..808e980
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityPolicy.java
@@ -0,0 +1,116 @@
+/*
+ * 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.fuseki.access;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Predicate;
+
+import org.apache.jena.graph.Node;
+import org.apache.jena.query.QueryExecution;
+import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.core.Quad;
+import org.apache.jena.sparql.util.Context;
+import org.apache.jena.sparql.util.NodeUtils;
+import org.apache.jena.tdb.TDBFactory;
+import org.apache.jena.tdb2.DatabaseMgr;
+
+/** A {@link SecurityPolicy} is the things actor (user, role) is allowed to do. 
+ * Currently version: the set of graphs, by graph name, they can access.
+ * It can be inverted into a "deny" policy with {@link Predicate#negate()}.
+ */ 
+public class SecurityPolicy {
+    
+    public static SecurityPolicy NONE = new SecurityPolicy();
+    public static SecurityPolicy DFT_GRAPH = new SecurityPolicy(true);
+
+    private final Collection<Node> graphNames = ConcurrentHashMap.newKeySet();
+    private final boolean matchDefaultGraph;
+    
+    public SecurityPolicy() {
+        this(false);
+    }
+
+    public SecurityPolicy(boolean matchDefaultGraph) {
+        this.matchDefaultGraph = matchDefaultGraph;
+    }
+
+    public SecurityPolicy(String...graphNames) {
+        this(NodeUtils.convertToSetNodes(graphNames));
+    }
+
+    public SecurityPolicy(Node...graphNames) {
+        this(Arrays.asList(graphNames));
+    }
+
+    public SecurityPolicy(Collection<Node> graphNames) {
+        this.graphNames.addAll(graphNames);
+        this.matchDefaultGraph = graphNames.stream().anyMatch(Quad::isDefaultGraph);
+    }
+    
+    /**
+     * Apply a filter suitable for the TDB-backed {@link DatasetGraph}, to the {@link Context} of the
+     * {@link QueryExecution}. This does not modify the {@link DatasetGraph}
+     */
+    public void filterTDB(DatasetGraph dsg, QueryExecution qExec) {
+        GraphFilter<?> predicate = predicate(dsg);
+        qExec.getContext().set(predicate.getContextKey(), predicate);
+    }
+    
+    /** Modify the {@link Context} of the TDB-backed {@link DatasetGraph}. */
+    public void filterTDB(DatasetGraph dsg) {
+        GraphFilter<?> predicate = predicate(dsg);
+        dsg.getContext().set(predicate.getContextKey(), predicate);
+    }
+
+    @Override
+    public String toString() {
+        return "dft:"+matchDefaultGraph+" / "+graphNames.toString();
+    }
+
+    /**
+     * Create a GraphFilter for a TDB backed dataset.
+     * 
+     * @return GraphFilter
+     * @throws IllegalArgumentException
+     *             if not a TDB database, or a {@link DatasetGraphAccessControl} wrapped
+     *             TDB database.
+     */
+    public GraphFilter<?> predicate(DatasetGraph dsg) {
+        dsg = DatasetGraphAccessControl.unwrap(dsg);
+        // dsg has to be the database dataset, not wrapped.
+        //  DatasetGraphSwitchable is wrapped but should not be unwrapped. 
+        if ( TDBFactory.isTDB1(dsg) )
+            return filterTDB1(dsg);
+        if ( DatabaseMgr.isTDB2(dsg) )
+            return filterTDB2(dsg);
+        throw new IllegalArgumentException("Not a TDB1 or TDB2 database: "+dsg.getClass().getSimpleName());
+    }
+
+    public GraphFilterTDB2 filterTDB2(DatasetGraph dsg) {
+        GraphFilterTDB2 f = GraphFilterTDB2.graphFilter(dsg, graphNames, matchDefaultGraph);
+        return f;
+    }
+    
+    public GraphFilterTDB1 filterTDB1(DatasetGraph dsg) {
+        GraphFilterTDB1 f = GraphFilterTDB1.graphFilter(dsg, graphNames, matchDefaultGraph);
+        return f; 
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityRegistry.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityRegistry.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityRegistry.java
new file mode 100644
index 0000000..1ad4098
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityRegistry.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.access;
+
+import java.util.StringJoiner;
+
+import javax.servlet.ServletContext;
+
+import org.apache.jena.atlas.lib.Registry;
+import org.apache.jena.fuseki.Fuseki;
+
+/**
+ * A {@link SecurityRegistry} is mapping from a string (typically a user name or role
+ * name) to a {@link SecurityPolicy}, where the {@link SecurityPolicy}
+ * is the access control operations for the user/role.
+ */ 
+public class SecurityRegistry extends Registry<String, SecurityPolicy>{
+    
+    public static SecurityRegistry get(ServletContext cxt) {
+        return (SecurityRegistry)cxt.getAttribute(Fuseki.attrSecurityRegistry);
+    }
+    
+    public static void set(ServletContext cxt, SecurityRegistry securityRegistry) {
+        cxt.setAttribute(Fuseki.attrSecurityRegistry, securityRegistry);
+    }
+
+    public SecurityRegistry() {}
+    
+    @Override
+    public SecurityPolicy get(String actor) {
+        if ( actor == null )
+            return SecurityPolicy.NONE;
+        SecurityPolicy policy = super.get(actor);
+        if ( policy == null )
+            policy = SecurityPolicy.NONE;
+        return policy;
+    }
+    
+    @Override 
+    public String toString() {
+        if ( true ) 
+            return "SecurityRegistry"+keys();
+        // Long form.
+        StringJoiner sj1 = new StringJoiner("\n", "{ SecurityRegistry\n", "\n}");
+        super.keys().forEach(u->{
+            SecurityPolicy x = super.get(u);
+            StringJoiner sj2 = new StringJoiner("");
+            sj2.add("  ")
+                .add(u)
+                .add(" -> ")
+                .add(x.toString());
+            sj1.add(sj2.toString());
+        });
+        return sj1.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/VocabSecurity.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/VocabSecurity.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/VocabSecurity.java
new file mode 100644
index 0000000..14ed959
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/VocabSecurity.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.access;
+
+import org.apache.jena.rdf.model.Property;
+import org.apache.jena.rdf.model.Resource;
+import org.apache.jena.sparql.core.assembler.AssemblerUtils;
+import org.apache.jena.tdb.assembler.Vocab;
+
+public class VocabSecurity {
+    private static final String NS = "http://jena.apache.org/access#";
+    
+    public static String getURI() { return NS ; } 
+
+    // Types
+    public static final Resource tAccessControlledDataset = Vocab.type(NS, "AccessControlledDataset");
+    public static final Resource tSecurityRegistry        = Vocab.type(NS, "SecurityRegistry");
+
+    // Make subproperty of.
+    public static final Property pDataset                 = Vocab.property(NS, "dataset");
+    public static final Property pSecurityRegistry        = Vocab.property(NS, "registry");
+
+    public static final Property pEntry                   = Vocab.property(NS, "entry");
+    public static final Property pUser                    = Vocab.property(NS, "user");
+    public static final Property pGraphs                  = Vocab.property(NS, "graphs");
+
+    private static boolean initialized = false ; 
+    
+    static { init() ; }
+    
+    static synchronized public void init() {
+        if ( initialized )
+            return;
+        initialized = true;
+        // AssemblerUtils.subProperty
+        AssemblerUtils.registerDataset(tAccessControlledDataset, new AssemblerAccessDataset());
+        AssemblerUtils.registerModel(tSecurityRegistry, new AssemblerSecurityRegistry());
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/jena-fuseki2/jena-fuseki-access/src/main/resources/META-INF/services/org.apache.jena.sys.JenaSubsystemLifecycle
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/resources/META-INF/services/org.apache.jena.sys.JenaSubsystemLifecycle b/jena-fuseki2/jena-fuseki-access/src/main/resources/META-INF/services/org.apache.jena.sys.JenaSubsystemLifecycle
new file mode 100644
index 0000000..e1699d2
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/main/resources/META-INF/services/org.apache.jena.sys.JenaSubsystemLifecycle
@@ -0,0 +1 @@
+org.apache.jena.fuseki.access.InitSecurity

http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/GraphData.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/GraphData.java b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/GraphData.java
new file mode 100644
index 0000000..9712b71
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/GraphData.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.access;
+
+import org.apache.jena.atlas.lib.StrUtils;
+import org.apache.jena.graph.Node;
+import org.apache.jena.riot.Lang;
+import org.apache.jena.riot.RDFParser;
+import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.sse.SSE;
+import org.apache.jena.system.Txn;
+
+/** Build graph data for the filter/security tests. */
+
+class GraphData {
+    private static String dataStr = StrUtils.strjoinNL 
+        ("PREFIX : <http://test/>"
+        ,""
+        ,":s0 :p 0 ."
+        ,":g1 { :s1 :p 1 }"
+        ,":g2 { :s2 :p 2 }"
+        ,":g3 { :s3 :p 3 }"
+        ,":g4 { :s4 :p 4 }"
+        );
+    
+    
+    static Node s0 = SSE.parseNode("<http://test/s0>"); 
+    static Node s1 = SSE.parseNode("<http://test/s1>"); 
+    static Node s2 = SSE.parseNode("<http://test/s2>"); 
+    static Node s3 = SSE.parseNode("<http://test/s3>"); 
+    static Node s4 = SSE.parseNode("<http://test/s4>"); 
+ 
+    static Node g1 = SSE.parseNode("<http://test/g1>"); 
+    static Node g2 = SSE.parseNode("<http://test/g2>"); 
+    static Node g3 = SSE.parseNode("<http://test/g3>"); 
+    static Node g4 = SSE.parseNode("<http://test/g4>"); 
+
+    public static void fill(DatasetGraph dsg) {
+         Txn.executeWrite(dsg, ()->{
+             RDFParser.create().fromString(dataStr).lang(Lang.TRIG).parse(dsg);
+         });
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TS_SecurityFiltering.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TS_SecurityFiltering.java b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TS_SecurityFiltering.java
new file mode 100644
index 0000000..35cb7ed
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TS_SecurityFiltering.java
@@ -0,0 +1,42 @@
+/*
+ * 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.fuseki.access;
+
+import org.apache.jena.atlas.logging.LogCtl;
+import org.apache.jena.fuseki.Fuseki;
+import org.junit.BeforeClass;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+@RunWith(Suite.class)
+@Suite.SuiteClasses( {
+    TestSecurityFilterLocal.class
+    , TestSecurityFilterFuseki.class
+    , TestSecurityAssembler.class
+})
+
+public class TS_SecurityFiltering {
+    @BeforeClass public static void setupForFusekiServer() {
+        LogCtl.setLevel(Fuseki.serverLogName,        "WARN");
+        LogCtl.setLevel(Fuseki.actionLogName,        "WARN");
+        LogCtl.setLevel(Fuseki.requestLogName,       "WARN");
+        LogCtl.setLevel(Fuseki.adminLogName,         "WARN");
+        LogCtl.setLevel("org.eclipse.jetty",         "WARN");
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityAssembler.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityAssembler.java b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityAssembler.java
new file mode 100644
index 0000000..eefdcd7
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityAssembler.java
@@ -0,0 +1,193 @@
+/*
+ * 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.fuseki.access;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.jena.atlas.iterator.Iter;
+import org.apache.jena.atlas.lib.StrUtils;
+import org.apache.jena.fuseki.FusekiLib;
+import org.apache.jena.fuseki.embedded.FusekiServer;
+import org.apache.jena.graph.Node;
+import org.apache.jena.query.Dataset;
+import org.apache.jena.query.QuerySolution;
+import org.apache.jena.query.ResultSetFormatter;
+import org.apache.jena.rdf.model.RDFNode;
+import org.apache.jena.rdfconnection.RDFConnection;
+import org.apache.jena.rdfconnection.RDFConnectionFactory;
+import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.core.assembler.AssemblerUtils;
+import org.apache.jena.sparql.sse.SSE;
+import org.apache.jena.system.Txn;
+import org.junit.Test;
+
+/**
+ * Test on the assembler for data access control.
+ * <ul>
+ * <li>assem-security.ttl - two services "/database" and "/plain" each with their own dataset. 
+ * <li>assem-security-shared.ttl - two services "/database" and "/plain" with a shared dataset.
+ * </ul>
+ */
+public class TestSecurityAssembler {
+    static final String DIR = "testing/Access/";
+    
+    // Check the main test inputs. 
+    @Test public void assembler1() { 
+        Dataset ds = (Dataset)AssemblerUtils.build(DIR+"assem-security.ttl", VocabSecurity.tAccessControlledDataset);
+    }
+    
+    @Test public void assembler2() { 
+        Dataset ds = (Dataset)AssemblerUtils.build(DIR+"assem-security-shared.ttl", VocabSecurity.tAccessControlledDataset);
+        SecurityRegistry securityRegistry = ds.getContext().get(DataAccessCtl.symSecurityRegistry);
+    }
+    
+    private static FusekiServer setup(String assembler, AtomicReference<String> user) {
+        int port = FusekiLib.choosePort();
+        FusekiServer server = DataAccessCtl.fusekiBuilder((a)->user.get())
+            .port(port)
+            .parseConfigFile(assembler)
+            .build();
+                
+        return server;
+    }
+    
+    // Two separate datasets  
+    @Test public void assembler3() {
+        AtomicReference<String> user = new AtomicReference<>();
+        FusekiServer server = setup(DIR+"assem-security.ttl", user);
+        // Add data directly to the datasets.
+        DatasetGraph dsg = server.getDataAccessPointRegistry().get("/database").getDataService().getDataset();
+        //System.out.println(dsg.getContext());
+        Txn.executeWrite(dsg,  ()->{
+            dsg.add(SSE.parseQuad("(<http://host/graphname1> :s1 :p :o)"));
+            dsg.add(SSE.parseQuad("(<http://host/graphname3> :s3 :p :o)"));
+            dsg.add(SSE.parseQuad("(<http://host/graphname9> :s9 :p :o)"));
+        });
+        server.start();
+        try {
+            testAssembler(server.getPort(), user);
+
+            // Access the uncontrolled dataset.
+            user.set(null);
+            String plainUrl = "http://localhost:"+server.getPort()+"/plain";
+            try(RDFConnection conn = RDFConnectionFactory.connect(plainUrl)) {
+                conn.update("INSERT DATA { <x:s> <x:p> 123 , 456 }");
+                conn.queryResultSet("SELECT * { ?s ?p ?o }",
+                    rs->{
+                        int x = ResultSetFormatter.consume(rs);
+                        assertEquals(2, x);
+                    });
+            }
+        } finally { server.stop(); }
+    }
+    
+    // Shared dataset
+    @Test public void assembler4() {
+        AtomicReference<String> user = new AtomicReference<>();
+        FusekiServer server = setup(DIR+"assem-security-shared.ttl", user);
+    
+        String x = StrUtils.strjoinNL
+            ("PREFIX : <http://example/>"
+            ,"INSERT DATA {"
+            ,"   GRAPH <http://host/graphname1> {:s1 :p :o}"
+            ,"   GRAPH <http://host/graphname3> {:s3 :p :o}"
+            ,"   GRAPH <http://host/graphname9> {:s9 :p :o}"
+            ,"}"
+            );
+        
+        server.start();
+        try {
+            user.set(null);
+            String plainUrl = "http://localhost:"+server.getPort()+"/plain";
+            try(RDFConnection conn = RDFConnectionFactory.connect(plainUrl)) {
+                conn.update(x);
+                conn.queryResultSet("SELECT * { GRAPH ?g { ?s ?p ?o } }",
+                    rs->{
+                        int c = ResultSetFormatter.consume(rs);
+                        assertEquals(3, c);
+                    });
+            }
+            testAssembler(server.getPort(), user);
+        } finally { server.stop(); }
+    }
+
+    private void testAssembler(int port, AtomicReference<String> user) {
+        // The access controlled dataset.
+        String url = "http://localhost:"+port+"/database";
+
+        Node s1 = SSE.parseNode(":s1"); 
+        Node s3 = SSE.parseNode(":s3");
+        Node s9 = SSE.parseNode(":s9"); 
+
+        user.set("user1");
+        try(RDFConnection conn = RDFConnectionFactory.connect(url)) {
+            Set<Node> visible = query(conn, "SELECT * { GRAPH ?g { ?s ?p ?o }}");
+            assertSeen(visible, s1, s3);
+        }
+
+        user.set("userX"); // No such user in the registry
+        try(RDFConnection conn = RDFConnectionFactory.connect(url)) {
+            Set<Node> visible = query(conn, "SELECT * { GRAPH ?g { ?s ?p ?o }}");
+            assertSeen(visible);
+        }
+        user.set(null); // No user.
+        try(RDFConnection conn = RDFConnectionFactory.connect(url)) {
+            Set<Node> visible = query(conn, "SELECT * { GRAPH ?g { ?s ?p ?o }}");
+            assertSeen(visible);
+        }
+
+        user.set("user2");
+        try(RDFConnection conn = RDFConnectionFactory.connect(url)) {
+            Set<Node> visible = query(conn, "SELECT * { GRAPH ?g { ?s ?p ?o }}");
+            assertSeen(visible, s9);
+        }
+
+        user.set("userZ"); // No graphs with data.
+        try(RDFConnection conn = RDFConnectionFactory.connect(url)) {
+            Set<Node> visible = query(conn, "SELECT * { GRAPH ?g { ?s ?p ?o }}");
+            assertSeen(visible);
+        }
+
+    }
+
+    private static void assertSeen(Set<Node> visible, Node ... expected) {
+        Set<Node> expectedNodes = new HashSet<>(Arrays.asList(expected));
+        assertEquals(expectedNodes, visible);
+    }
+    
+    private Set<Node> query(RDFConnection conn, String queryString) {
+        Set<Node> results = new HashSet<>();
+        conn.queryResultSet(queryString, rs->{
+            List<QuerySolution> list = Iter.toList(rs);
+            list.stream()
+            .map(qs->qs.get("s"))
+            .filter(Objects::nonNull)
+            .map(RDFNode::asNode)
+            .forEach(n->results.add(n));
+        });
+        return results;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/344ae5d8/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterFuseki.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterFuseki.java b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterFuseki.java
new file mode 100644
index 0000000..4eb8dd4
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterFuseki.java
@@ -0,0 +1,191 @@
+/*
+ * 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.fuseki.access;
+
+import static org.apache.jena.fuseki.access.GraphData.s0;
+import static org.apache.jena.fuseki.access.GraphData.s1;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+import org.apache.jena.atlas.iterator.Iter;
+import org.apache.jena.fuseki.FusekiLib;
+import org.apache.jena.fuseki.embedded.FusekiServer;
+import org.apache.jena.fuseki.jetty.JettyLib;
+import org.apache.jena.graph.Node;
+import org.apache.jena.query.QuerySolution;
+import org.apache.jena.rdf.model.RDFNode;
+import org.apache.jena.rdfconnection.RDFConnection;
+import org.apache.jena.rdfconnection.RDFConnectionFactory;
+import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.core.Quad;
+import org.apache.jena.sparql.engine.http.QueryExceptionHTTP;
+import org.apache.jena.tdb.TDBFactory;
+import org.apache.jena.tdb2.DatabaseMgr;
+import org.eclipse.jetty.security.PropertyUserStore;
+import org.eclipse.jetty.security.SecurityHandler;
+import org.eclipse.jetty.security.UserStore;
+import org.eclipse.jetty.util.security.Credential;
+import org.eclipse.jetty.util.security.Password;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class TestSecurityFilterFuseki {
+
+    @Parameters(name = "{index}: {0}")
+    public static Iterable<Object[]> data() {
+        Object[] obj1 = { "TDB", "data1" };
+        Object[] obj2 = { "TDB2", "data2" };
+        return Arrays.asList(obj1, obj2);
+    }
+
+    private final String baseUrl;
+    private static final DatasetGraph testdsg1 =  TDBFactory.createDatasetGraph();
+    private static final DatasetGraph testdsg2 =  DatabaseMgr.createDatasetGraph();
+    private static FusekiServer fusekiServer;
+
+    // Set up Fuseki with two datasets, "data1" backed by TDB and "data2" backed by TDB2.
+    @BeforeClass public static void beforeClass() {
+        int port = FusekiLib.choosePort();
+        GraphData.fill(testdsg1);
+        GraphData.fill(testdsg2);
+        
+        SecurityRegistry reg = new SecurityRegistry();
+        reg.put("userNone", SecurityPolicy.NONE);
+        reg.put("userDft", SecurityPolicy.DFT_GRAPH);
+        reg.put("user0", new SecurityPolicy(Quad.defaultGraphIRI.getURI()));
+        reg.put("user1", new SecurityPolicy("http://test/g1", Quad.defaultGraphIRI.getURI()));
+        reg.put("user2", new SecurityPolicy("http://test/g1", "http://test/g2", "http://test/g3"));
+        
+        // XXXX Also need wrapped tests
+        DataAccessCtl.controlledDataset(testdsg1, reg);
+        DataAccessCtl.controlledDataset(testdsg2, reg);
+
+        UserStore userStore = userStore();
+        SecurityHandler sh = JettyLib.makeSecurityHandler("/*", "DatasetRealm", userStore);
+        
+        fusekiServer = DataAccessCtl.fusekiBuilder(sh,  DataAccessCtl.requestUserServlet)
+            .port(port)
+            .add("data1", testdsg1)
+            .add("data2", testdsg2)
+            .build();
+        fusekiServer.start();
+    }
+
+    @AfterClass public static void afterClass() {
+        fusekiServer.stop();
+    }
+
+    private static UserStore userStore() {
+        PropertyUserStore propertyUserStore = new PropertyUserStore();
+        String[] roles = new String[]{"**"};
+        addUserPassword(propertyUserStore, "user0", "pw0", roles);
+        addUserPassword(propertyUserStore, "user1", "pw1", roles);
+        addUserPassword(propertyUserStore, "user2", "pw2", roles);
+        return propertyUserStore;
+    }
+
+    private static void addUserPassword(PropertyUserStore propertyUserStore, String user, String password, String[] roles) {
+        Credential cred  = new Password(password);
+        propertyUserStore.addUser(user, cred, roles);
+    }
+
+    public TestSecurityFilterFuseki(String label, String dsName) {
+        int port = fusekiServer.getPort();
+        baseUrl = "http://localhost:"+port+"/"+dsName;
+    }
+
+    private static void assertSeen(Set<Node> visible, Node ... expected) {
+        Set<Node> expectedNodes = new HashSet<>(Arrays.asList(expected));
+        assertEquals(expectedNodes, visible);
+    }
+
+    private static String queryAll        = "SELECT * { { ?s ?p ?o } UNION { GRAPH ?g { ?s ?p ?o } } }";
+    private static String queryDft        = "SELECT * { ?s ?p ?o }";
+    private static String queryNamed      = "SELECT * { GRAPH ?g { ?s ?p ?o } }";
+
+    private static String queryG2         = "SELECT * { GRAPH <http://test/graph2> { ?s ?p ?o } }";
+    private static String queryGraphNames = "SELECT * { GRAPH ?g { } }";
+
+    private Set<Node> query(String user, String password, String queryString) {
+        Set<Node> results = new HashSet<>();
+        try (RDFConnection conn = RDFConnectionFactory.connectPW(baseUrl, user, password)) {
+            conn.queryResultSet(queryString, rs->{
+                List<QuerySolution> list = Iter.toList(rs);
+                list.stream()
+                    .map(qs->qs.get("s"))
+                    .filter(Objects::nonNull)
+                    .map(RDFNode::asNode)
+                    .forEach(n->results.add(n));
+            });
+        }
+        return results;
+    }
+
+    private void query401(String user, String password, String queryString) {
+        queryHttp(401, user, password, queryString); 
+    }
+
+    private void query403(String user, String password, String queryString) {
+        queryHttp(403, user, password, queryString); 
+    }
+
+    private void queryHttp(int statusCode, String user, String password, String queryString) {
+        try {
+            query(user, password, queryString);
+            if ( statusCode < 200 && statusCode > 299 ) 
+                fail("Should have responded with "+statusCode);
+        } catch (QueryExceptionHTTP ex) {
+            assertEquals(statusCode, ex.getResponseCode());
+        }
+    }
+    
+    @Test public void query_user0() {
+        Set<Node> results = query("user0", "pw0", queryAll);
+        assertSeen(results, s0);
+    }
+    
+    @Test public void query_user1() {
+        Set<Node> results = query("user1", "pw1", queryAll);
+        assertSeen(results, s0, s1);
+    }
+    
+    @Test public void query_userX() {
+        query401("userX", "pwX", queryAll);
+    }
+    
+    @Test public void query_bad_user() {
+        query401("userX", "pwX", queryAll);
+    }
+
+    @Test public void query_bad_password() {
+        query401("user0", "not-the-password", queryAll);
+    }
+
+}


[23/27] jena git commit: Rename SecuriyPolicy to SecurityContext

Posted by an...@apache.org.
Rename SecuriyPolicy to SecurityContext


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

Branch: refs/heads/master
Commit: 5e0003389eddfd86f92ab22277c7cd2f7c4aad5f
Parents: 80a2f7b
Author: Andy Seaborne <an...@apache.org>
Authored: Mon Aug 27 12:14:59 2018 +0100
Committer: Andy Seaborne <an...@apache.org>
Committed: Mon Aug 27 12:14:59 2018 +0100

----------------------------------------------------------------------
 .../access/AssemblerSecurityRegistry.java       |   4 +-
 .../jena/fuseki/access/DataAccessCtl.java       |   4 +-
 .../jena/fuseki/access/DataAccessLib.java       |  10 +-
 .../access/Filtered_SPARQL_QueryDataset.java    |   2 +-
 .../jena/fuseki/access/GraphFilterTDB1.java     |   2 +-
 .../jena/fuseki/access/GraphFilterTDB2.java     |   2 +-
 .../jena/fuseki/access/SecurityContext.java     | 162 +++++++++++++++++++
 .../jena/fuseki/access/SecurityPolicy.java      | 162 -------------------
 .../jena/fuseki/access/SecurityRegistry.java    |  18 +--
 .../fuseki/access/TestSecurityFilterFuseki.java |  10 +-
 .../fuseki/access/TestSecurityFilterLocal.java  |  38 ++---
 11 files changed, 207 insertions(+), 207 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/5e000338/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AssemblerSecurityRegistry.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AssemblerSecurityRegistry.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AssemblerSecurityRegistry.java
index 1e8a3e4..dc66768 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AssemblerSecurityRegistry.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AssemblerSecurityRegistry.java
@@ -77,8 +77,8 @@ public class AssemblerSecurityRegistry extends AssemblerBase {
         });
         
         map.keySet().forEach(u->{
-            SecurityPolicy policy = new SecurityPolicy(map.get(u));
-            registry.put(u, policy);
+            SecurityContext sCxt = new SecurityContext(map.get(u));
+            registry.put(u, sCxt);
         });
         
         return registry;

http://git-wip-us.apache.org/repos/asf/jena/blob/5e000338/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessCtl.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessCtl.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessCtl.java
index 3bc0812..f1bf7d3 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessCtl.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessCtl.java
@@ -155,9 +155,9 @@ public class DataAccessCtl {
     }
 
     /**
-     * Return a read-only {@link DatasetGraphFilteredView} that fulfils the {@link SecurityPolicy}.
+     * Return a read-only {@link DatasetGraphFilteredView} that fulfils the {@link SecurityContext}.
      */
-    public static DatasetGraphFilteredView filteredDataset(DatasetGraph dsg, SecurityPolicy sCxt) {
+    public static DatasetGraphFilteredView filteredDataset(DatasetGraph dsg, SecurityContext sCxt) {
         return new DatasetGraphFilteredView(dsg, sCxt.predicateQuad(), sCxt.visibleGraphs());
     }
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/5e000338/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessLib.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessLib.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessLib.java
index 127aa92..c9b98bf 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessLib.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessLib.java
@@ -27,13 +27,13 @@ import org.apache.jena.sparql.core.DatasetGraph;
 /** Package-only operations */
 class DataAccessLib {
     
-    /** Determine the {@link SecurityPolicy} for this request */  
-    static SecurityPolicy getSecurityPolicy(HttpAction action, DatasetGraph dataset, Function<HttpAction, String> requestUser) {
+    /** Determine the {@link SecurityContext} for this request */  
+    static SecurityContext getSecurityContext(HttpAction action, DatasetGraph dataset, Function<HttpAction, String> requestUser) {
         SecurityRegistry registry = getSecurityRegistry(action, dataset);
         if ( registry == null )
             ServletOps.errorOccurred("Internal Server Error");
 
-        SecurityPolicy sCxt = null;
+        SecurityContext sCxt = null;
         String user = requestUser.apply(action);
         sCxt = registry.get(user);
         if ( sCxt == null )
@@ -48,7 +48,7 @@ class DataAccessLib {
         return dsg.getContext().get(DataAccessCtl.symSecurityRegistry);
     }
 
-    static SecurityPolicy noSecurityPolicy() {
+    static SecurityContext noSecurityPolicy() {
         ServletOps.errorForbidden();
         // Should not get here.
         throw new InternalError();
@@ -61,7 +61,7 @@ class DataAccessLib {
         if ( ! DataAccessCtl.isAccessControlled(dsg) )
             // Not access controlled.
             return dsg;//super.actOn(action);
-        SecurityPolicy sCxt = DataAccessLib.getSecurityPolicy(action, dsg, requestUser);
+        SecurityContext sCxt = DataAccessLib.getSecurityContext(action, dsg, requestUser);
         dsg = DatasetGraphAccessControl.removeWrapper(dsg);
         dsg = DataAccessCtl.filteredDataset(dsg, sCxt);
         return dsg;

http://git-wip-us.apache.org/repos/asf/jena/blob/5e000338/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_QueryDataset.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_QueryDataset.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_QueryDataset.java
index 8162f91..06a5829 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_QueryDataset.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_QueryDataset.java
@@ -54,7 +54,7 @@ public class Filtered_SPARQL_QueryDataset extends SPARQL_QueryDataset {
         if ( ! DataAccessCtl.isAccessControlled(dsg) )
             return super.createQueryExecution(action, query, dataset);
 
-        SecurityPolicy sCxt = DataAccessLib.getSecurityPolicy(action, dataset.asDatasetGraph(), requestUser);
+        SecurityContext sCxt = DataAccessLib.getSecurityContext(action, dataset.asDatasetGraph(), requestUser);
         // A QueryExecution for controlled access
         QueryExecution qExec = sCxt.createQueryExecution(query, dsg);
         return qExec;

http://git-wip-us.apache.org/repos/asf/jena/blob/5e000338/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/GraphFilterTDB1.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/GraphFilterTDB1.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/GraphFilterTDB1.java
index d15c57b..1d799aa 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/GraphFilterTDB1.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/GraphFilterTDB1.java
@@ -50,7 +50,7 @@ class GraphFilterTDB1 extends GraphFilter<NodeId> {
      * true) for Tuples where the graph slot in quad is in the collection or for triples in the default
      * graph according the boolean.
      * 
-     * @see SecurityPolicy#filterTDB(DatasetGraph, QueryExecution)
+     * @see SecurityContext#filterTDB(DatasetGraph, QueryExecution)
      */
     public static GraphFilterTDB1 graphFilter(DatasetGraph dsg, Collection<Node> namedGraphs, boolean matchDefaultGraph) {
         if ( ! TDBInternal.isTDB1(dsg) )

http://git-wip-us.apache.org/repos/asf/jena/blob/5e000338/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/GraphFilterTDB2.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/GraphFilterTDB2.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/GraphFilterTDB2.java
index cf95244..7c6b126 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/GraphFilterTDB2.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/GraphFilterTDB2.java
@@ -50,7 +50,7 @@ class GraphFilterTDB2 extends GraphFilter<NodeId> {
      * true) for Tuples where the graph slot in quad is in the collection or for triples in the default
      * graph according the boolean.
      * 
-     * @see SecurityPolicy#filterTDB(DatasetGraph, QueryExecution)
+     * @see SecurityContext#filterTDB(DatasetGraph, QueryExecution)
      */
     public static GraphFilterTDB2 graphFilter(DatasetGraph dsg, Collection<Node> namedGraphs, boolean matchDefaultGraph) {
         if ( ! TDBInternal.isTDB2(dsg) )

http://git-wip-us.apache.org/repos/asf/jena/blob/5e000338/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityContext.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityContext.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityContext.java
new file mode 100644
index 0000000..2ffaea0
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityContext.java
@@ -0,0 +1,162 @@
+/*
+ * 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.fuseki.access;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Predicate;
+
+import org.apache.jena.graph.Node;
+import org.apache.jena.query.Query;
+import org.apache.jena.query.QueryExecution;
+import org.apache.jena.query.QueryExecutionFactory;
+import org.apache.jena.query.QueryFactory;
+import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.core.Quad;
+import org.apache.jena.sparql.util.Context;
+import org.apache.jena.sparql.util.NodeUtils;
+import org.apache.jena.tdb.TDBFactory;
+import org.apache.jena.tdb2.DatabaseMgr;
+
+/** A {@link SecurityContext} is the things actor (user, role) is allowed to do. 
+ * Currently version: the set of graphs, by graph name, they can access.
+ * It can be inverted into a "deny" policy with {@link Predicate#negate()}.
+ */ 
+public class SecurityContext {
+    
+    public static SecurityContext NONE = new SecurityContext();
+    public static SecurityContext DFT_GRAPH = new SecurityContext(true);
+
+    private final Collection<Node> graphNames = ConcurrentHashMap.newKeySet();
+    private final boolean matchDefaultGraph;
+    
+    public SecurityContext() {
+        this(false);
+    }
+
+    public SecurityContext(boolean matchDefaultGraph) {
+        this.matchDefaultGraph = matchDefaultGraph;
+    }
+
+    public SecurityContext(String...graphNames) {
+        this(NodeUtils.convertToSetNodes(graphNames));
+    }
+
+    public SecurityContext(Node...graphNames) {
+        this(Arrays.asList(graphNames));
+    }
+
+    public SecurityContext(Collection<Node> visibleGraphs) {
+        this.graphNames.addAll(visibleGraphs);
+        this.matchDefaultGraph = visibleGraphs.stream().anyMatch(Quad::isDefaultGraph);
+        if ( matchDefaultGraph ) {
+            this.graphNames.remove(Quad.defaultGraphIRI);
+            this.graphNames.remove(Quad.defaultGraphNodeGenerated);
+        }
+    }
+    
+    public Collection<Node> visibleGraphs() {
+        return Collections.unmodifiableCollection(graphNames);
+    }
+    
+    /**
+     * Apply a filter suitable for the TDB-backed {@link DatasetGraph}, to the {@link Context} of the
+     * {@link QueryExecution}. This does not modify the {@link DatasetGraph}
+     */
+    /*package*/ void filterTDB(DatasetGraph dsg, QueryExecution qExec) {
+        GraphFilter<?> predicate = predicate(dsg);
+        qExec.getContext().set(predicate.getContextKey(), predicate);
+    }
+
+    public QueryExecution createQueryExecution(String queryString, DatasetGraph dsg) {
+        return createQueryExecution(QueryFactory.create(queryString), dsg);
+    }
+    
+    public QueryExecution createQueryExecution(Query query, DatasetGraph dsg) {
+        if ( ! ( dsg instanceof DatasetGraphAccessControl ) ) {
+            return QueryExecutionFactory.create(query, dsg);
+        }
+        if ( isAccessControlledTDB(dsg) ) {
+            QueryExecution qExec = QueryExecutionFactory.create(query, dsg);
+            filterTDB(dsg, qExec);
+            return qExec;
+        }
+        
+        DatasetGraph dsgA = DataAccessCtl.filteredDataset(dsg, this);
+        return QueryExecutionFactory.create(query, dsgA); 
+    }
+    
+    @Override
+    public String toString() {
+        return "dft:"+matchDefaultGraph+" / "+graphNames.toString();
+    }
+
+    public Predicate<Quad> predicateQuad() {
+        return quad -> {
+            if ( quad.isDefaultGraph() )
+                return matchDefaultGraph;
+            if ( quad.isUnionGraph() ) 
+                // Union graph is automatically there but its visible contents are different.
+                return true;
+            return graphNames.contains(quad.getGraph());
+        };
+    }
+
+    /**
+     * Create a GraphFilter for a TDB backed dataset.
+     * 
+     * @return GraphFilter
+     * @throws IllegalArgumentException
+     *             if not a TDB database, or a {@link DatasetGraphAccessControl} wrapped
+     *             TDB database.
+     */
+    public GraphFilter<?> predicate(DatasetGraph dsg) {
+        dsg = DatasetGraphAccessControl.removeWrapper(dsg);
+        // dsg has to be the database dataset, not wrapped.
+        //  DatasetGraphSwitchable is wrapped but should not be unwrapped. 
+        if ( TDBFactory.isTDB1(dsg) )
+            return filterTDB1(dsg);
+        if ( DatabaseMgr.isTDB2(dsg) )
+            return filterTDB2(dsg);
+        throw new IllegalArgumentException("Not a TDB1 or TDB2 database: "+dsg.getClass().getSimpleName());
+    }
+
+    public boolean isAccessControlledTDB(DatasetGraph dsg) {
+        DatasetGraph dsgBase = DatasetGraphAccessControl.unwrapOrNull(dsg);
+        if ( dsgBase == null )
+            return false;
+        if ( TDBFactory.isTDB1(dsgBase) )
+            return true;
+        if ( DatabaseMgr.isTDB2(dsgBase) )
+            return true;
+        return false;
+    }
+    
+    public GraphFilterTDB2 filterTDB2(DatasetGraph dsg) {
+        GraphFilterTDB2 f = GraphFilterTDB2.graphFilter(dsg, graphNames, matchDefaultGraph);
+        return f;
+    }
+    
+    public GraphFilterTDB1 filterTDB1(DatasetGraph dsg) {
+        GraphFilterTDB1 f = GraphFilterTDB1.graphFilter(dsg, graphNames, matchDefaultGraph);
+        return f; 
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/5e000338/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityPolicy.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityPolicy.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityPolicy.java
deleted file mode 100644
index ca19794..0000000
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityPolicy.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.jena.fuseki.access;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Predicate;
-
-import org.apache.jena.graph.Node;
-import org.apache.jena.query.Query;
-import org.apache.jena.query.QueryExecution;
-import org.apache.jena.query.QueryExecutionFactory;
-import org.apache.jena.query.QueryFactory;
-import org.apache.jena.sparql.core.DatasetGraph;
-import org.apache.jena.sparql.core.Quad;
-import org.apache.jena.sparql.util.Context;
-import org.apache.jena.sparql.util.NodeUtils;
-import org.apache.jena.tdb.TDBFactory;
-import org.apache.jena.tdb2.DatabaseMgr;
-
-/** A {@link SecurityPolicy} is the things actor (user, role) is allowed to do. 
- * Currently version: the set of graphs, by graph name, they can access.
- * It can be inverted into a "deny" policy with {@link Predicate#negate()}.
- */ 
-public class SecurityPolicy {
-    
-    public static SecurityPolicy NONE = new SecurityPolicy();
-    public static SecurityPolicy DFT_GRAPH = new SecurityPolicy(true);
-
-    private final Collection<Node> graphNames = ConcurrentHashMap.newKeySet();
-    private final boolean matchDefaultGraph;
-    
-    public SecurityPolicy() {
-        this(false);
-    }
-
-    public SecurityPolicy(boolean matchDefaultGraph) {
-        this.matchDefaultGraph = matchDefaultGraph;
-    }
-
-    public SecurityPolicy(String...graphNames) {
-        this(NodeUtils.convertToSetNodes(graphNames));
-    }
-
-    public SecurityPolicy(Node...graphNames) {
-        this(Arrays.asList(graphNames));
-    }
-
-    public SecurityPolicy(Collection<Node> visibleGraphs) {
-        this.graphNames.addAll(visibleGraphs);
-        this.matchDefaultGraph = visibleGraphs.stream().anyMatch(Quad::isDefaultGraph);
-        if ( matchDefaultGraph ) {
-            this.graphNames.remove(Quad.defaultGraphIRI);
-            this.graphNames.remove(Quad.defaultGraphNodeGenerated);
-        }
-    }
-    
-    public Collection<Node> visibleGraphs() {
-        return Collections.unmodifiableCollection(graphNames);
-    }
-    
-    /**
-     * Apply a filter suitable for the TDB-backed {@link DatasetGraph}, to the {@link Context} of the
-     * {@link QueryExecution}. This does not modify the {@link DatasetGraph}
-     */
-    /*package*/ void filterTDB(DatasetGraph dsg, QueryExecution qExec) {
-        GraphFilter<?> predicate = predicate(dsg);
-        qExec.getContext().set(predicate.getContextKey(), predicate);
-    }
-
-    public QueryExecution createQueryExecution(String queryString, DatasetGraph dsg) {
-        return createQueryExecution(QueryFactory.create(queryString), dsg);
-    }
-    
-    public QueryExecution createQueryExecution(Query query, DatasetGraph dsg) {
-        if ( ! ( dsg instanceof DatasetGraphAccessControl ) ) {
-            return QueryExecutionFactory.create(query, dsg);
-        }
-        if ( isAccessControlledTDB(dsg) ) {
-            QueryExecution qExec = QueryExecutionFactory.create(query, dsg);
-            filterTDB(dsg, qExec);
-            return qExec;
-        }
-        
-        DatasetGraph dsgA = DataAccessCtl.filteredDataset(dsg, this);
-        return QueryExecutionFactory.create(query, dsgA); 
-    }
-    
-    @Override
-    public String toString() {
-        return "dft:"+matchDefaultGraph+" / "+graphNames.toString();
-    }
-
-    public Predicate<Quad> predicateQuad() {
-        return quad -> {
-            if ( quad.isDefaultGraph() )
-                return matchDefaultGraph;
-            if ( quad.isUnionGraph() ) 
-                // Union graph is automatically there but its visible contents are different.
-                return true;
-            return graphNames.contains(quad.getGraph());
-        };
-    }
-
-    /**
-     * Create a GraphFilter for a TDB backed dataset.
-     * 
-     * @return GraphFilter
-     * @throws IllegalArgumentException
-     *             if not a TDB database, or a {@link DatasetGraphAccessControl} wrapped
-     *             TDB database.
-     */
-    public GraphFilter<?> predicate(DatasetGraph dsg) {
-        dsg = DatasetGraphAccessControl.removeWrapper(dsg);
-        // dsg has to be the database dataset, not wrapped.
-        //  DatasetGraphSwitchable is wrapped but should not be unwrapped. 
-        if ( TDBFactory.isTDB1(dsg) )
-            return filterTDB1(dsg);
-        if ( DatabaseMgr.isTDB2(dsg) )
-            return filterTDB2(dsg);
-        throw new IllegalArgumentException("Not a TDB1 or TDB2 database: "+dsg.getClass().getSimpleName());
-    }
-
-    public boolean isAccessControlledTDB(DatasetGraph dsg) {
-        DatasetGraph dsgBase = DatasetGraphAccessControl.unwrapOrNull(dsg);
-        if ( dsgBase == null )
-            return false;
-        if ( TDBFactory.isTDB1(dsgBase) )
-            return true;
-        if ( DatabaseMgr.isTDB2(dsgBase) )
-            return true;
-        return false;
-    }
-    
-    public GraphFilterTDB2 filterTDB2(DatasetGraph dsg) {
-        GraphFilterTDB2 f = GraphFilterTDB2.graphFilter(dsg, graphNames, matchDefaultGraph);
-        return f;
-    }
-    
-    public GraphFilterTDB1 filterTDB1(DatasetGraph dsg) {
-        GraphFilterTDB1 f = GraphFilterTDB1.graphFilter(dsg, graphNames, matchDefaultGraph);
-        return f; 
-    }
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/5e000338/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityRegistry.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityRegistry.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityRegistry.java
index 490a785..10b9a08 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityRegistry.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityRegistry.java
@@ -27,10 +27,10 @@ import org.apache.jena.fuseki.Fuseki;
 
 /**
  * A {@link SecurityRegistry} is mapping from a string (typically a user name or role
- * name) to a {@link SecurityPolicy}, where the {@link SecurityPolicy}
+ * name) to a {@link SecurityContext}, where the {@link SecurityContext}
  * is the access control operations for the user/role.
  */ 
-public class SecurityRegistry extends Registry<String, SecurityPolicy>{
+public class SecurityRegistry extends Registry<String, SecurityContext>{
     
     public static SecurityRegistry get(ServletContext cxt) {
         return (SecurityRegistry)cxt.getAttribute(Fuseki.attrSecurityRegistry);
@@ -43,13 +43,13 @@ public class SecurityRegistry extends Registry<String, SecurityPolicy>{
     public SecurityRegistry() {}
     
     @Override
-    public SecurityPolicy get(String actor) {
+    public SecurityContext get(String actor) {
         if ( actor == null )
-            return SecurityPolicy.NONE;
-        SecurityPolicy policy = super.get(actor);
-        if ( policy == null )
-            policy = SecurityPolicy.NONE;
-        return policy;
+            return SecurityContext.NONE;
+        SecurityContext sCxt = super.get(actor);
+        if ( sCxt == null )
+            sCxt = SecurityContext.NONE;
+        return sCxt;
     }
     
     @Override 
@@ -61,7 +61,7 @@ public class SecurityRegistry extends Registry<String, SecurityPolicy>{
         // Long form.
         StringJoiner sj1 = new StringJoiner("\n", "{ SecurityRegistry\n", "\n}");
         super.keys().forEach(u->{
-            SecurityPolicy x = super.get(u);
+            SecurityContext x = super.get(u);
             StringJoiner sj2 = new StringJoiner("");
             sj2.add("  ")
                 .add(u)

http://git-wip-us.apache.org/repos/asf/jena/blob/5e000338/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterFuseki.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterFuseki.java b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterFuseki.java
index c9d188a..bbbd87c 100644
--- a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterFuseki.java
+++ b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterFuseki.java
@@ -84,11 +84,11 @@ public class TestSecurityFilterFuseki {
         addTestData(testdsg3);
         
         SecurityRegistry reg = new SecurityRegistry();
-        reg.put("userNone", SecurityPolicy.NONE);
-        reg.put("userDft", SecurityPolicy.DFT_GRAPH);
-        reg.put("user0", new SecurityPolicy(Quad.defaultGraphIRI.getURI()));
-        reg.put("user1", new SecurityPolicy("http://test/g1", Quad.defaultGraphIRI.getURI()));
-        reg.put("user2", new SecurityPolicy("http://test/g1", "http://test/g2", "http://test/g3"));
+        reg.put("userNone", SecurityContext.NONE);
+        reg.put("userDft", SecurityContext.DFT_GRAPH);
+        reg.put("user0", new SecurityContext(Quad.defaultGraphIRI.getURI()));
+        reg.put("user1", new SecurityContext("http://test/g1", Quad.defaultGraphIRI.getURI()));
+        reg.put("user2", new SecurityContext("http://test/g1", "http://test/g2", "http://test/g3"));
         
         testdsg1 = DataAccessCtl.controlledDataset(testdsg1, reg);
         testdsg2 = DataAccessCtl.controlledDataset(testdsg2, reg);

http://git-wip-us.apache.org/repos/asf/jena/blob/5e000338/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterLocal.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterLocal.java b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterLocal.java
index 1e03aba..ac0bd0f 100644
--- a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterLocal.java
+++ b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterLocal.java
@@ -93,11 +93,11 @@ public class TestSecurityFilterLocal {
     public TestSecurityFilterLocal(String name, Creator<DatasetGraph> source, boolean applyFilterTDB) {
         DatasetGraph dsgBase = source.create();
         AccessTestLib.addTestData(dsgBase);
-        reg.put("userNone", SecurityPolicy.NONE);
-        reg.put("userDft", SecurityPolicy.DFT_GRAPH);
-        reg.put("user0", new SecurityPolicy(Quad.defaultGraphIRI.getURI()));
-        reg.put("user1", new SecurityPolicy("http://test/g1", Quad.defaultGraphIRI.getURI()));
-        reg.put("user2", new SecurityPolicy("http://test/g1", "http://test/g2", "http://test/g3"));
+        reg.put("userNone", SecurityContext.NONE);
+        reg.put("userDft", SecurityContext.DFT_GRAPH);
+        reg.put("user0", new SecurityContext(Quad.defaultGraphIRI.getURI()));
+        reg.put("user1", new SecurityContext("http://test/g1", Quad.defaultGraphIRI.getURI()));
+        reg.put("user2", new SecurityContext("http://test/g1", "http://test/g2", "http://test/g3"));
         testdsg = DataAccessCtl.controlledDataset(dsgBase, reg);
         this.applyFilterTDB = applyFilterTDB;
         this.applyFilterDSG = ! applyFilterTDB;
@@ -115,7 +115,7 @@ public class TestSecurityFilterLocal {
     private static String queryG2         = "SELECT * { GRAPH <http://test/graph2> { ?s ?p ?o } }";
     private static String queryGraphNames = "SELECT * { GRAPH ?g { } }";
 
-    private Set<Node> subjects(DatasetGraph dsg, String queryString, SecurityPolicy sCxt) {
+    private Set<Node> subjects(DatasetGraph dsg, String queryString, SecurityContext sCxt) {
         final DatasetGraph dsg1 = applyFilterDSG
             ? DataAccessCtl.filteredDataset(dsg, sCxt)
             : dsg;
@@ -135,7 +135,7 @@ public class TestSecurityFilterLocal {
             });
     }
     
-    private Set<Node> subjects(DatasetGraph dsg,  Function<DatasetGraph, Graph> graphChoice, String queryString, SecurityPolicy sCxt) {
+    private Set<Node> subjects(DatasetGraph dsg,  Function<DatasetGraph, Graph> graphChoice, String queryString, SecurityContext sCxt) {
         final DatasetGraph dsg1 = applyFilterDSG
             ? DataAccessCtl.filteredDataset(dsg, sCxt)
             : dsg;
@@ -153,7 +153,7 @@ public class TestSecurityFilterLocal {
             });
     }
 
-    private Set<Node> graphs(DatasetGraph dsg, SecurityPolicy sCxt) {
+    private Set<Node> graphs(DatasetGraph dsg, SecurityContext sCxt) {
         final DatasetGraph dsg1 = applyFilterDSG
             ? DataAccessCtl.filteredDataset(dsg, sCxt)
             : dsg;
@@ -171,14 +171,14 @@ public class TestSecurityFilterLocal {
     }
 
     @Test public void filter_setup() {
-        Set<Node> visible = subjects(testdsg, queryAll, SecurityPolicy.NONE);
+        Set<Node> visible = subjects(testdsg, queryAll, SecurityContext.NONE);
         assertEquals(0, visible.size());
         assertSeen(visible);
     }
 
     // QueryExecution
     private void filter_user(String user, Node ... expected) {
-        SecurityPolicy sCxt = reg.get(user);
+        SecurityContext sCxt = reg.get(user);
         Set<Node> visible = subjects(testdsg, queryAll, sCxt);
         assertSeen(visible, expected);
     }
@@ -209,50 +209,50 @@ public class TestSecurityFilterLocal {
 
     // "Access Denied"
     @Test public void no_access_user1() {
-        SecurityPolicy sCxt = reg.get("user1");
+        SecurityContext sCxt = reg.get("user1");
         Set<Node> visible = subjects(testdsg, queryG2, sCxt);
         assertTrue(visible.isEmpty());
     }
 
     @Test public void graph_names_userNone() {
-        SecurityPolicy sCxt = reg.get("userNone");
+        SecurityContext sCxt = reg.get("userNone");
         Set<Node> visible = graphs(testdsg, sCxt); 
         assertSeen(visible);
     }
     
     @Test public void graph_names_userDft() {
-        SecurityPolicy sCxt = reg.get("userDft");
+        SecurityContext sCxt = reg.get("userDft");
         Set<Node> visible = graphs(testdsg, sCxt); 
         assertSeen(visible);
     }
     
     @Test public void graph_names_user0() {
-        SecurityPolicy sCxt = reg.get("user0");
+        SecurityContext sCxt = reg.get("user0");
         Set<Node> visible = graphs(testdsg, sCxt); 
         assertSeen(visible);
     }
     
     @Test public void graph_names_user1() {
-        SecurityPolicy sCxt = reg.get("user1");
+        SecurityContext sCxt = reg.get("user1");
         Set<Node> visible = graphs(testdsg, sCxt); 
         assertSeen(visible, g1);
     }
 
     @Test public void graph_names_user2() {
-        SecurityPolicy sCxt = reg.get("user2");
+        SecurityContext sCxt = reg.get("user2");
         Set<Node> visible = graphs(testdsg, sCxt); 
         assertSeen(visible, g1, g2, g3);
     }
 
     @Test public void graph_names_userX() {
-        SecurityPolicy sCxt = reg.get("userX");
+        SecurityContext sCxt = reg.get("userX");
         Set<Node> visible = graphs(testdsg, sCxt); 
         assertSeen(visible);
     }
 
     // QueryExecution w/ Union default graph
     private void filter_union_user(String user, Node ... expected) {
-        SecurityPolicy sCxt = reg.get(user);
+        SecurityContext sCxt = reg.get(user);
         Set<Node> visible;
         if ( applyFilterTDB ) {
             // TDB special version. Set the TDB flags for union default graph
@@ -348,7 +348,7 @@ public class TestSecurityFilterLocal {
     }
 
     private void query_model_user(DatasetGraph dsg, Function<DatasetGraph, Graph> graphChoice, String user, Node ... expected) {
-        SecurityPolicy sCxt = reg.get(user);
+        SecurityContext sCxt = reg.get(user);
         Set<Node> visible = subjects(dsg, graphChoice, queryDft, sCxt);
         assertSeen(visible, expected);
     }


[08/27] jena git commit: Minor formatting tiding

Posted by an...@apache.org.
Minor formatting tiding


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

Branch: refs/heads/master
Commit: ea7f95efb493f3a1a8c8638e2496fceed178a0aa
Parents: ba3b4b6
Author: Andy Seaborne <an...@apache.org>
Authored: Wed Aug 22 16:40:06 2018 +0100
Committer: Andy Seaborne <an...@apache.org>
Committed: Thu Aug 23 17:15:05 2018 +0100

----------------------------------------------------------------------
 .../src/main/java/org/apache/jena/query/DatasetFactory.java | 9 +--------
 .../org/apache/jena/sparql/core/DatasetGraphFactory.java    | 5 ++---
 2 files changed, 3 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/ea7f95ef/jena-arq/src/main/java/org/apache/jena/query/DatasetFactory.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/query/DatasetFactory.java b/jena-arq/src/main/java/org/apache/jena/query/DatasetFactory.java
index 45dac4a..de99b71 100644
--- a/jena-arq/src/main/java/org/apache/jena/query/DatasetFactory.java
+++ b/jena-arq/src/main/java/org/apache/jena/query/DatasetFactory.java
@@ -64,7 +64,6 @@ public class DatasetFactory {
      * so overheads can accumulate).
      * 
      * @return a transactional, in-memory, modifiable Dataset
-     * 
      */
 	public static Dataset createTxnMem() {
 		return wrap(DatasetGraphFactory.createTxnMem());
@@ -72,7 +71,7 @@ public class DatasetFactory {
 
 	/**
 	 * Create a general-purpose  {@link Dataset}.<br/>
-	 * Any graphs needed are in-memory unless explciitly added with {@link Dataset#addNamedModel}.
+	 * Any graphs needed are in-memory unless explicitly added with {@link Dataset#addNamedModel}.
 	 * </p>
 	 * This dataset type can contain graphs from any source when added via {@link Dataset#addNamedModel}.
 	 * These are held as links to the supplied graph and not copied.
@@ -194,7 +193,6 @@ public class DatasetFactory {
 	 * @param namedSourceList
 	 * @return a named graph container of graphs based on a list of URIs.
 	 */
-
 	public static Dataset createNamed(List<String> namedSourceList) {
 		return create((List<String>) null, namedSourceList, null);
 	}
@@ -209,7 +207,6 @@ public class DatasetFactory {
 	 * @param namedSourceList graphs to be attached as named graphs
 	 * @return Dataset
 	 */
-
 	public static Dataset create(List<String> uriList, List<String> namedSourceList) {
 		return create(uriList, namedSourceList, null);
 	}
@@ -224,7 +221,6 @@ public class DatasetFactory {
 	 * @param namedSourceList graphs to be attached as named graphs
 	 * @return Dataset
 	 */
-
 	public static Dataset create(String uri, List<String> namedSourceList) {
 		return create(uri, namedSourceList, null);
 	}
@@ -240,7 +236,6 @@ public class DatasetFactory {
 	 * @param baseURI baseURI for relative URI expansion
 	 * @return Dataset
 	 */
-
 	public static Dataset create(String uri, List<String> namedSourceList, String baseURI) {
 		return DatasetUtils.createDataset(uri, namedSourceList, baseURI);
 	}
@@ -256,7 +251,6 @@ public class DatasetFactory {
 	 * @param baseURI baseURI for relative URI expansion
 	 * @return Dataset
 	 */
-
 	public static Dataset create(List<String> uriList, List<String> namedSourceList, String baseURI) {
 		return DatasetUtils.createDataset(uriList, namedSourceList, baseURI);
 	}
@@ -316,7 +310,6 @@ public class DatasetFactory {
 	 * @param resource The resource for the dataset
 	 * @return Dataset
 	 */
-
 	public static Dataset assemble(Resource resource) {
         Objects.requireNonNull(resource, "resource can not be null") ;
 		Dataset ds = (Dataset) Assembler.general.open(resource);

http://git-wip-us.apache.org/repos/asf/jena/blob/ea7f95ef/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphFactory.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphFactory.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphFactory.java
index 29af401..140e62f 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphFactory.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphFactory.java
@@ -37,7 +37,7 @@ public class DatasetGraphFactory
      * This implementation copies models when {@link Dataset#addNamedModel(String, Model)} is called.
      * <p>
      * This implementation provides "best effort" transactions; it only provides MRSW locking.
-     * Use {@link #createTxnMem} for a proper in-memeory transactional {@code DatasetGraph}.
+     * Use {@link #createTxnMem} for a proper in-memory transactional {@code DatasetGraph}.
      * 
      * @see #createTxnMem
      */
@@ -54,8 +54,7 @@ public class DatasetGraphFactory
      * (the implementation adds a begin/commit around each add or delete
      * so overheads can accumulate).
      * 
-     * @return a transactional, in-memory, modifiable Dataset
-     * 
+     * @return a transactional, in-memory, modifiable DatasetGraph
      */
     public static DatasetGraph createTxnMem() { return new DatasetGraphInMemory(); }
 


[15/27] jena git commit: Decouple FusekiLogging from the webapp.

Posted by an...@apache.org.
Decouple FusekiLogging from the webapp.

Pass in an addition allocation to look for a log4j1 properties file.


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

Branch: refs/heads/master
Commit: 2490112032af274391c4b6bca558ff358ce34c3c
Parents: 689c4cf
Author: Andy Seaborne <an...@apache.org>
Authored: Sat Aug 25 17:12:12 2018 +0100
Committer: Andy Seaborne <an...@apache.org>
Committed: Sat Aug 25 17:12:12 2018 +0100

----------------------------------------------------------------------
 .../org/apache/jena/fuseki/cmd/FusekiCmd.java     |  5 +++--
 .../apache/jena/fuseki/system/FusekiLogging.java  | 18 +++++++++++++-----
 .../webapp/FusekiServerEnvironmentInit.java       |  5 +++--
 .../java/org/apache/jena/fuseki/TS_Fuseki.java    |  2 +-
 4 files changed, 20 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/24901120/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/cmd/FusekiCmd.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/cmd/FusekiCmd.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/cmd/FusekiCmd.java
index e62b118..e2957b8 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/cmd/FusekiCmd.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/cmd/FusekiCmd.java
@@ -58,8 +58,9 @@ public class FusekiCmd {
     // statics of a class are run.
 
     static {
-        FusekiEnv.mode = FusekiEnv.INIT.STANDALONE ;
-        FusekiLogging.setLogging() ;
+        FusekiEnv.mode = FusekiEnv.INIT.STANDALONE;
+        FusekiEnv.setEnvironment();
+        FusekiLogging.setLogging(FusekiEnv.FUSEKI_BASE);
     }
 
     static public void main(String... argv) {

http://git-wip-us.apache.org/repos/asf/jena/blob/24901120/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/system/FusekiLogging.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/system/FusekiLogging.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/system/FusekiLogging.java
index 404c581..147fbd0 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/system/FusekiLogging.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/system/FusekiLogging.java
@@ -20,11 +20,11 @@ package org.apache.jena.fuseki.system;
 
 import java.io.File ;
 import java.net.URL ;
+import java.nio.file.Path;
 
 import org.apache.jena.atlas.lib.StrUtils ;
 import org.apache.jena.atlas.logging.LogCtl ;
 import org.apache.jena.fuseki.Fuseki;
-import org.apache.jena.fuseki.webapp.FusekiEnv;
 import org.apache.jena.riot.SysRIOT ;
 import org.apache.log4j.PropertyConfigurator ;
 import org.apache.log4j.helpers.Loader ;
@@ -65,12 +65,20 @@ public class FusekiLogging
     
     /** Set up logging - standalone and war packaging */
     public static synchronized void setLogging() {
+        setLogging(null);
+        
+    }
+    
+    /** Set up logging. Allow an extra location (string directory name without trailing "/"). This may be null 
+     * 
+     * @param extraDir
+     */
+    public static synchronized void setLogging(Path extraDir) {
         if ( ! allowLoggingReset )
             return ;
         if ( loggingInitialized )
             return ;
         loggingInitialized = true ;
-        FusekiEnv.setEnvironment() ;
         
         logLogging("Fuseki logging") ;
         // No loggers have been created but configuration may have been set up.
@@ -86,12 +94,12 @@ public class FusekiLogging
         }
         logLogging("Fuseki logging - setup") ;
         // Look for a log4j.properties in the current working directory
-        // and an existing FUSEKI_BASE for easy customization.
+        // and a plane (e.g. FUSEKI_BASE in the webapp/full server) for easy customization.
         String fn1 = "log4j.properties" ;
         String fn2 = null ;
         
-        if ( FusekiEnv.FUSEKI_BASE != null ) 
-            fn2 = FusekiEnv.FUSEKI_BASE.toString()+"/log4j.properties" ;
+        if ( extraDir != null ) 
+            fn2 = extraDir.resolve("log4j.properties").toString() ;
         if ( attempt(fn1) ) return ; 
         if ( attempt(fn2) ) return ;
         

http://git-wip-us.apache.org/repos/asf/jena/blob/24901120/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/webapp/FusekiServerEnvironmentInit.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/webapp/FusekiServerEnvironmentInit.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/webapp/FusekiServerEnvironmentInit.java
index b950550..6d3d889 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/webapp/FusekiServerEnvironmentInit.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/webapp/FusekiServerEnvironmentInit.java
@@ -34,9 +34,10 @@ public class FusekiServerEnvironmentInit implements ServletContextListener {
     
     @Override
     public void contextInitialized(ServletContextEvent sce) {
-        FusekiLogging.setLogging();
-        JenaSystem.init() ;
+        // These two do not touch Jena.
         FusekiEnv.setEnvironment() ;
+        FusekiLogging.setLogging(FusekiEnv.FUSEKI_BASE);
+        JenaSystem.init() ;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/jena/blob/24901120/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/TS_Fuseki.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/TS_Fuseki.java b/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/TS_Fuseki.java
index d3e51c4..42e33df 100644
--- a/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/TS_Fuseki.java
+++ b/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/TS_Fuseki.java
@@ -55,8 +55,8 @@ public class TS_Fuseki extends ServerTest
         FileOps.ensureDir(FusekiTestHome);
         FileOps.clearDirectory(FusekiTestHome);
         System.setProperty("FUSEKI_HOME", FusekiTestHome) ;
-        FusekiLogging.setLogging();
         FusekiEnv.setEnvironment() ;
+        FusekiLogging.setLogging();
         // To avoid confusion with log4j.properties in the main part of the server,
         // we modify in place the logging, not try to set it with another
         // Log4j properties file from the classpath.  


[25/27] jena git commit: JENA-1594: Structure to allow the adding/modifying of server setup.

Posted by an...@apache.org.
JENA-1594: Structure to allow the adding/modifying of server setup.

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

Branch: refs/heads/master
Commit: 20eb07ca560d85e4bf6d74ae29342422dec665db
Parents: 228f4b9
Author: Andy Seaborne <an...@apache.org>
Authored: Mon Aug 27 18:23:04 2018 +0100
Committer: Andy Seaborne <an...@apache.org>
Committed: Mon Aug 27 18:23:04 2018 +0100

----------------------------------------------------------------------
 .../apache/jena/fuseki/cmds/FusekiBasicCmd.java | 537 +------------------
 .../jena/fuseki/cmds/FusekiBasicMain.java       | 513 ++++++++++++++++++
 .../apache/jena/fuseki/cmds/ServerConfig.java   |  51 ++
 3 files changed, 576 insertions(+), 525 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/20eb07ca/jena-fuseki2/jena-fuseki-basic/src/main/java/org/apache/jena/fuseki/cmds/FusekiBasicCmd.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-basic/src/main/java/org/apache/jena/fuseki/cmds/FusekiBasicCmd.java b/jena-fuseki2/jena-fuseki-basic/src/main/java/org/apache/jena/fuseki/cmds/FusekiBasicCmd.java
index f1ba1f2..72f0879 100644
--- a/jena-fuseki2/jena-fuseki-basic/src/main/java/org/apache/jena/fuseki/cmds/FusekiBasicCmd.java
+++ b/jena-fuseki2/jena-fuseki-basic/src/main/java/org/apache/jena/fuseki/cmds/FusekiBasicCmd.java
@@ -18,52 +18,9 @@
 
 package org.apache.jena.fuseki.cmds;
 
-import java.net.BindException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-import arq.cmdline.CmdARQ;
-import arq.cmdline.ModAssembler;
-import arq.cmdline.ModDatasetAssembler;
-import jena.cmd.ArgDecl;
-import jena.cmd.CmdException;
-import org.apache.jena.assembler.exceptions.AssemblerException;
-import org.apache.jena.atlas.lib.DateTimeUtils ;
-import org.apache.jena.atlas.lib.FileOps;
-import org.apache.jena.atlas.logging.FmtLog;
-import org.apache.jena.fuseki.Fuseki;
-import org.apache.jena.fuseki.FusekiException;
-import org.apache.jena.fuseki.embedded.FusekiServer;
-import org.apache.jena.fuseki.server.DataAccessPoint;
-import org.apache.jena.fuseki.server.DataAccessPointRegistry;
-import org.apache.jena.fuseki.server.DataService;
-import org.apache.jena.fuseki.servlets.SPARQL_QueryGeneral ;
 import org.apache.jena.fuseki.system.FusekiLogging;
-import org.apache.jena.fuseki.validation.DataValidator ;
-import org.apache.jena.fuseki.validation.IRIValidator ;
-import org.apache.jena.fuseki.validation.QueryValidator ;
-import org.apache.jena.fuseki.validation.UpdateValidator ;
-import org.apache.jena.query.ARQ;
-import org.apache.jena.query.Dataset;
-import org.apache.jena.riot.Lang;
-import org.apache.jena.riot.RDFDataMgr;
-import org.apache.jena.riot.RDFLanguages;
-import org.apache.jena.sparql.core.DatasetGraph;
-import org.apache.jena.sparql.core.DatasetGraphFactory;
-import org.apache.jena.sys.JenaSystem;
-import org.apache.jena.system.Txn;
-import org.apache.jena.tdb.TDB;
-import org.apache.jena.tdb.TDBFactory;
-import org.apache.jena.tdb.transaction.TransactionManager;
-import org.apache.jena.tdb2.DatabaseMgr;
-import org.slf4j.Logger;
 
-/** Fuseki command that runs a Fuseki server with no UI, just SPARQL services.
+/** Fuseki command that runs a Fuseki server without the admin UI, just SPARQL services.
  * <p>
  * Use {@code --conf=} for multiple datasets and specific service names. 
  * <p>
@@ -71,489 +28,19 @@ import org.slf4j.Logger;
  */
 
 public class FusekiBasicCmd {
-    // Own module (or optional dependency on jena-cmds)
-    // Needs jena-cmds.
+    // This class wraps FusekiBasicMain so that it can take control of logging setup.
+    // It does not depend via inheritance on any Jena code - FusekiBasicMain does.
+    // Inheritance causes initialization in the super class first, before class
+    // initialization code in this class.
     
-    static {
-        FusekiLogging.setLogging();
-    }
+    static { FusekiLogging.setLogging(); }
 
-    /** Build and run, a server based on command line syntax. This operation does not return. */  
+    /**
+     * Build and run, a server based on command line syntax. This operation does not
+     * return. See {@link FusekiBasicMain#build} to build a server using command line
+     * syntax but not start it.
+     */
     static public void main(String... argv) {
-        FusekiCmdInner.innerMain(argv);
-    }
-    
-    /** Build, but do not start, a server based on command line syntax. */  
-    static public FusekiServer build(String... argv) {
-        return FusekiCmdInner.build(argv);
-    }
-
-    /** Dataset setup (command line, config file) for a dataset (or several if config file) */
-    static class ServerConfig {
-        public int port;
-        // Dataset name on the command line.
-        public String    datasetPath      = null; 
-        // Command line --update.
-        public boolean   allowUpdate      = false;
-        
-        // This is set ...
-        public DatasetGraph dsg           = null;
-        // ... or this.
-        public String serverConfig        = null;
-        
-
-        // Allow there to be no registered datasets without it being an error.
-        // which is "return  dsg==null && serverConfig==null;"
-        public boolean empty              = false ;
-        // Setup for SPARQLer - validators and general query engine, some pages. 
-        public boolean sparqler           = false ;
-        public boolean validators         = false ;
-        public boolean loopback           = false;
-        public String datasetDescription;
-        public String contentDirectory    = null;
-    }
-    
-    static class FusekiCmdInner extends CmdARQ {
-        private static int defaultPort = 3030;
-        
-        private static ArgDecl  argMem          = new ArgDecl(ArgDecl.NoValue,  "mem");
-        private static ArgDecl  argUpdate       = new ArgDecl(ArgDecl.NoValue,  "update", "allowUpdate");
-        private static ArgDecl  argFile         = new ArgDecl(ArgDecl.HasValue, "file");
-
-        private static ArgDecl  argTDB2mode     = new ArgDecl(ArgDecl.NoValue,  "tdb2");
-        private static ArgDecl  argMemTDB       = new ArgDecl(ArgDecl.NoValue,  "memtdb", "memTDB", "tdbmem");
-        private static ArgDecl  argTDB          = new ArgDecl(ArgDecl.HasValue, "loc", "location", "tdb");
-        
-        // No SPARQL dataset or services
-        private static ArgDecl  argEmpty        = new ArgDecl(ArgDecl.NoValue,  "empty", "no-dataset");
-        private static ArgDecl  argPort         = new ArgDecl(ArgDecl.HasValue, "port");
-        private static ArgDecl  argLocalhost    = new ArgDecl(ArgDecl.NoValue,  "localhost", "local");
-        private static ArgDecl  argTimeout      = new ArgDecl(ArgDecl.HasValue, "timeout");
-        private static ArgDecl  argConfig       = new ArgDecl(ArgDecl.HasValue, "config", "conf");
-        private static ArgDecl  argGZip         = new ArgDecl(ArgDecl.HasValue, "gzip");
-        private static ArgDecl  argBase         = new ArgDecl(ArgDecl.HasValue, "base", "files");
-        private static ArgDecl  argSparqler     = new ArgDecl(ArgDecl.HasValue, "sparqler");
-        private static ArgDecl  argValidators   = new ArgDecl(ArgDecl.NoValue,  "validators");
-        // private static ModLocation modLocation = new ModLocation();
-        private static ModDatasetAssembler modDataset      = new ModDatasetAssembler();
-
-        static void innerMain(String... argv) {
-            JenaSystem.init();
-            new FusekiCmdInner(argv).mainRun();
-        }
-
-        /** Build, but do not start, a server based on command line syntax. */  
-        static FusekiServer build(String... argv) {
-            FusekiCmdInner inner = new FusekiCmdInner(argv);
-            inner.process();
-            return inner.buildServer();
-        }
-
-        private final ServerConfig serverConfig  = new ServerConfig();
-        private boolean useTDB2;
-        
-        public FusekiCmdInner(String... argv) {
-            super(argv);
-
-            if ( false )
-                // Consider ...
-                TransactionManager.QueueBatchSize = TransactionManager.QueueBatchSize / 2;
-
-            getUsage().startCategory("Fuseki");
-            addModule(modDataset);
-            add(argMem, "--mem",
-                "Create an in-memory, non-persistent dataset for the server");
-            add(argFile, "--file=FILE",
-                "Create an in-memory, non-persistent dataset for the server, initialised with the contents of the file");
-            add(argTDB2mode, "--tdb2",
-                "Create command line persistent datasets with TDB2");
-            add(argTDB, "--loc=DIR",
-                "Use an existing TDB database (or create if does not exist)");
-            add(argMemTDB, "--memTDB",
-                "Create an in-memory, non-persistent dataset using TDB (testing only)");
-//            add(argEmpty, "--empty",
-//                "Run with no datasets and services (validators only)");
-            add(argEmpty); // Hidden for now.
-            add(argPort, "--port",
-                "Listen on this port number");
-            add(argLocalhost, "--localhost",
-                "Listen only on the localhost interface");
-            add(argTimeout, "--timeout=",
-                "Global timeout applied to queries (value in ms) -- format is X[,Y] ");
-            add(argUpdate, "--update",
-                "Allow updates (via SPARQL Update and SPARQL HTTP Update)");
-            add(argConfig, "--config=",
-                "Use a configuration file to determine the services");
-            add(argGZip, "--gzip=on|off",
-                "Enable GZip compression (HTTP Accept-Encoding) if request header set");
-            add(argBase, "--base=DIR",
-                "Directory for static content");
-            add(argSparqler, "--sparqler=DIR",
-                "Run with SPARQLer services Directory for static content");
-            add(argValidators, "--validators", "Install validators");
-
-            super.modVersion.addClass(TDB.class);
-            super.modVersion.addClass(Fuseki.class);
-        }
-
-        static String argUsage = "[--config=FILE] [--mem|--desc=AssemblerFile|--file=FILE] [--port PORT] /DatasetPathName";
-
-        @Override
-        protected String getSummary() {
-            return getCommandName() + " " + argUsage;
-        }
-
-        @Override
-        protected void processModulesAndArgs() {
-            int x = 0;
-
-            Logger log = Fuseki.serverLog;
-
-            // ---- Checking
-
-            if ( contains(argMem) )             
-                x++;
-            if ( contains(argFile) )
-                x++;
-            if ( contains(ModAssembler.assemblerDescDecl) )
-                x++;
-            if ( contains(argTDB) )
-                x++;
-            if ( contains(argMemTDB) )
-                x++;
-            if ( contains(argConfig) )
-                x++;
-
-            boolean allowEmpty = contains(argEmpty) || contains(argSparqler);
-            
-            
-            if ( x == 0 && ! allowEmpty )
-                throw new CmdException("No dataset specified on the command line.");
-
-            if ( x > 1 )
-                throw new CmdException("Multiple ways providing a dataset. Only one of --mem, --file, --loc or --desc");
-            
-            if ( x > 0 && allowEmpty )
-                throw new CmdException("Dataset provided but 'no dataset' flag given");
-            
-            //---- check: Invalid: --conf + service name.
-            if ( contains(argConfig) ) {
-                if ( getPositional().size() != 0 )
-                    throw new CmdException("Can't have both a configutation file and a service name");
-            } else if ( ! allowEmpty ) {
-                if ( getPositional().size() == 0 )
-                    throw new CmdException("Missing service name");
-                if ( getPositional().size() > 1 )
-                    throw new CmdException("Multiple dataset path names given");
-                serverConfig.datasetPath = DataAccessPoint.canonical(getPositionalArg(0));
-            }
-            
-            serverConfig.datasetDescription = "<unset>";
-            
-            // ---- check: Invalid: --update + --conf
-            if ( contains(argUpdate) && contains(argConfig) )
-                throw new CmdException("--update and a configuration file does not make sense (control using the configuration file only)");
-            boolean allowUpdate = contains(argUpdate);
-            serverConfig.allowUpdate = allowUpdate;
-
-            // ---- Port
-            serverConfig.port = defaultPort;
-            
-            if ( contains(argPort) ) {
-                String portStr = getValue(argPort);
-                try {
-                    int port = Integer.parseInt(portStr);
-                    serverConfig.port = port;
-                } catch (NumberFormatException ex) {
-                    throw new CmdException(argPort.getKeyName() + " : bad port number: " + portStr);
-                }
-            }
-            if ( contains(argLocalhost) )
-                serverConfig.loopback = true;
-
-            // ---- Dataset
-            // Only one of these is choose from the checking above.
-            
-            // Which TDB to use to create a command line TDB database. 
-            useTDB2 = contains(argTDB2mode);
-            String tag = useTDB2 ? "TDB2" : "TDB";
-            
-            if ( allowEmpty ) {
-                serverConfig.empty = true;
-                serverConfig.datasetDescription = "No dataset";
-            }                
-
-            // Fuseki config file 
-            if ( contains(argConfig) ) {
-                String file = getValue(argConfig);
-                if ( file.startsWith("file:") )
-                    file = file.substring("file:".length());
-                
-                Path path = Paths.get(file);
-                if ( ! Files.exists(path) )
-                    throw new CmdException("File not found: "+file);
-                if ( Files.isDirectory(path) )
-                    throw new CmdException("Is a directory: "+file);
-                serverConfig.datasetDescription = "Configuration: "+path.toAbsolutePath();
-                serverConfig.serverConfig = getValue(argConfig);
-            }
-            
-            // Ways to setup a dataset.
-            if ( contains(argMem) ) {
-                serverConfig.datasetDescription = "in-memory";
-                // Only one setup should be called by the test above but to be safe
-                // and in case of future changes, clear the configuration.  
-                serverConfig.dsg = DatasetGraphFactory.createTxnMem();
-                // Always allow, else you can't do very much! 
-                serverConfig.allowUpdate = true;
-            }
-
-            if ( contains(argFile) ) {
-                String filename = getValue(argFile);
-                String pathname = filename;
-                if ( filename.startsWith("file:") )
-                    pathname = filename.substring("file:".length());
-
-                serverConfig.datasetDescription = "file:"+filename;
-                if ( !FileOps.exists(pathname) )
-                    throw new CmdException("File not found: " + filename);
-                serverConfig.dsg = DatasetGraphFactory.createTxnMem();
-                
-                // INITIAL DATA.
-                Lang language = RDFLanguages.filenameToLang(filename);
-                if ( language == null )
-                    throw new CmdException("Can't guess language for file: " + filename);
-                Txn.executeWrite(serverConfig.dsg,  ()->RDFDataMgr.read(serverConfig.dsg, filename));
-            }
-
-//            if ( contains(argMemTDB) ) {
-//                //log.info("TDB dataset: in-memory") ;
-//                cmdLineConfig.reset();
-//                cmdLineConfig.argTemplateFile = useTDB2 ? Template.templateTDB2_MemFN : Template.templateTDB1_MemFN ;
-//                cmdLineConfig.params.put(Template.DIR, Names.memName) ;
-//                // Always allow.
-//                cmdLineConfig.allowUpdate = true ;
-//                cmdLineConfig.datasetDescription = useTDB2 ? "TDB2 dataset (in-memory)" : "TDB dataset (in-memory)";
-//            }
-//
-//            if ( contains(argTDB) ) {
-//                cmdLineConfig.reset();
-//                cmdLineConfig.argTemplateFile = 
-//                    useTDB2 ? Template.templateTDB2_DirFN : Template.templateTDB1_DirFN;
-//                String dir = getValue(argTDB) ;
-//                cmdLineConfig.params.put(Template.DIR, dir) ;
-//                cmdLineConfig.datasetDescription = useTDB2 ? "TDB2 dataset: "+dir : "TDB dataset: "+dir;
-//            }
-            
-            if ( contains(argMemTDB) ) {
-                serverConfig.datasetDescription = tag+" dataset in-memory";
-                serverConfig.dsg =
-                    useTDB2
-                    ? DatabaseMgr.createDatasetGraph()
-                    : TDBFactory.createDatasetGraph();
-                serverConfig.allowUpdate = true;
-            }
-
-            if ( contains(argTDB) ) {
-                String dir = getValue(argTDB);
-                serverConfig.datasetDescription = tag+" dataset: "+dir;
-                serverConfig.dsg = 
-                    useTDB2
-                    ? DatabaseMgr.connectDatasetGraph(dir)
-                    : TDBFactory.createDatasetGraph(dir);
-            }
-
-            if ( contains(ModAssembler.assemblerDescDecl) ) {
-                serverConfig.datasetDescription = "Assembler: "+ getValue(ModAssembler.assemblerDescDecl);
-                // Need to add service details.
-                Dataset ds = modDataset.createDataset();
-                serverConfig.dsg = ds.asDatasetGraph();
-            }
-
-            // ---- Misc features.
-            if ( contains(argTimeout) ) {
-                String str = getValue(argTimeout);
-                ARQ.getContext().set(ARQ.queryTimeout, str);
-            }
-            
-            if ( contains(argValidators) ) {
-                serverConfig.validators = true;
-            }
-                
-            if ( contains(argSparqler) ) {
-                String filebase = getValue(argSparqler);
-                if ( ! FileOps.exists(filebase) )
-                    throw new CmdException("File area not found: "+filebase); 
-                serverConfig.contentDirectory = filebase;
-                serverConfig.sparqler = true;
-                serverConfig.validators = true;
-            }
-            
-            if ( contains(argBase) ) {
-                // Static files.
-                String filebase = getValue(argBase);
-                if ( ! FileOps.exists(filebase) ) {
-                    throw new CmdException("File area not found: "+filebase); 
-                    //FmtLog.warn(Fuseki.configLog, "File area not found: "+filebase);  
-                }
-                serverConfig.contentDirectory = filebase;
-            }
-
-//            if ( contains(argGZip) ) {
-//                if ( !hasValueOfTrue(argGZip) && !hasValueOfFalse(argGZip) )
-//                    throw new CmdException(argGZip.getNames().get(0) + ": Not understood: " + getValue(argGZip));
-//                jettyServerConfig.enableCompression = super.hasValueOfTrue(argGZip);
-//            }
-        }
-
-//        private static String sort_out_dir(String path) {
-//            path.replace('\\', '/');
-//            if ( !path.endsWith("/") )
-//                path = path + "/";
-//            return path;
-//        }
-
-        @Override
-        protected void exec() {
-            try {
-                FusekiServer server = buildServer(serverConfig);
-                info(server, serverConfig);
-                try {
-                    server.start();
-                } catch (FusekiException ex) {
-                    if ( ex.getCause() instanceof BindException ) {
-                        Fuseki.serverLog.error("Failed to start server: "+ex.getCause().getMessage()+ ": port="+serverConfig.port) ;
-                        System.exit(1);
-                    }
-                    throw ex;
-                } catch (Exception ex) {
-                    throw new FusekiException("Failed to start server: " + ex.getMessage(), ex) ;
-                }
-                server.join();
-                System.exit(0);
-            }
-            catch (AssemblerException ex) {
-                if ( ex.getCause() != null )
-                    System.err.println(ex.getCause().getMessage());
-                else
-                    System.err.println(ex.getMessage());
-            }
-        }
-
-        private FusekiServer buildServer() {
-            return buildServer(serverConfig);
-        }
-
-        // ServerConfig -> Setup the builder.
-        private static FusekiServer buildServer(ServerConfig serverConfig) {
-            FusekiServer.Builder builder = FusekiServer.create();
-            // Loopback.
-            builder.port(serverConfig.port);
-            builder.loopback(serverConfig.loopback);
-            
-            if ( serverConfig.validators ) {
-                if ( serverConfig.sparqler )
-                    builder.addServlet("/sparql",  new SPARQL_QueryGeneral());
-                // Validators.
-                builder.addServlet("/validate/query",  new QueryValidator());
-                builder.addServlet("/validate/update", new UpdateValidator());
-                builder.addServlet("/validate/iri",    new IRIValidator());
-                builder.addServlet("/validate/data",   new DataValidator());
-            } 
-            if ( ! serverConfig.empty ) {
-                if ( serverConfig.serverConfig != null )
-                    // Config file.
-                    builder.parseConfigFile(serverConfig.serverConfig);
-                else
-                    // One dataset.
-                    builder.add(serverConfig.datasetPath, serverConfig.dsg, serverConfig.allowUpdate);
-            }
-            
-            if ( serverConfig.contentDirectory != null )
-                builder.staticFileBase(serverConfig.contentDirectory) ;
-
-            return builder.build();
-        }
-
-        private void info(FusekiServer server, ServerConfig serverConfig) {
-            if ( super.isQuiet() )
-                return;
-
-            Logger log = Fuseki.serverLog;
-
-            String version = Fuseki.VERSION;
-            String buildDate = Fuseki.BUILD_DATE ;
-
-            if ( version != null && version.equals("${project.version}") )
-                version = null ;
-            if ( buildDate != null && buildDate.equals("${build.time.xsd}") )
-                buildDate = DateTimeUtils.nowAsXSDDateTimeString() ;
-            
-            String name = Fuseki.NAME;
-            name = name +" (basic server)";
-            
-            if ( version != null ) {
-                if ( Fuseki.developmentMode && buildDate != null )
-                    FmtLog.info(log, "%s %s %s", name, version, buildDate) ;
-                else
-                    FmtLog.info(log, "%s %s", name, version);
-            }
-            
-            // Dataset -> Endpoints
-            Map<String, List<String>> mapDatasetEndpoints = description(DataAccessPointRegistry.get(server.getServletContext()));
-            
-            if ( serverConfig.empty ) {
-                FmtLog.info(log, "No SPARQL datasets services"); 
-            } else {
-                if ( serverConfig.datasetPath == null && serverConfig.serverConfig == null )
-                    log.error("No dataset path nor server configuration file");
-            }
-            
-            if ( serverConfig.datasetPath != null ) {
-                if ( mapDatasetEndpoints.size() != 1 )
-                    log.error("Expected only one dataset");
-                List<String> endpoints = mapDatasetEndpoints.get(serverConfig.datasetPath); 
-                FmtLog.info(log,  "Dataset Type = %s", serverConfig.datasetDescription);
-                FmtLog.info(log,  "Path = %s; Services = %s", serverConfig.datasetPath, endpoints);
-            }
-            if ( serverConfig.serverConfig != null ) {
-                // May be many datasets and services.
-                FmtLog.info(log,  "Configuration file %s", serverConfig.serverConfig);
-                mapDatasetEndpoints.forEach((path, endpoints)->{
-                    FmtLog.info(log,  "Path = %s; Services = %s", path, endpoints);
-                });
-            }
-            
-            if ( serverConfig.contentDirectory != null )
-                FmtLog.info(log,  "Static files = %s", serverConfig.contentDirectory);
-                
-            if ( super.isVerbose() )
-                PlatformInfo.logDetailsVerbose(log);
-            else if ( !super.isQuiet() )
-                PlatformInfo.logDetails(log);
-        }
-
-        private static Map<String, List<String>> description(DataAccessPointRegistry reg) {
-            Map<String, List<String>> desc = new LinkedHashMap<>();
-            reg.forEach((ds,dap)->{
-                List<String> endpoints = new ArrayList<>();
-                desc.put(ds, endpoints);
-                DataService dSrv = dap.getDataService();
-                dSrv.getOperations().forEach((op)->{
-                    dSrv.getEndpoints(op).forEach(ep-> {
-                        String x = ep.getEndpoint();
-                        if ( x.isEmpty() )
-                            x = "quads";
-                        endpoints.add(x);   
-                    });
-                });
-            });
-            return desc;
-        }
-        
-        @Override
-        protected String getCommandName() {
-            return "fuseki";
-        }
+        FusekiBasicMain.innerMain(argv);
     }
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/20eb07ca/jena-fuseki2/jena-fuseki-basic/src/main/java/org/apache/jena/fuseki/cmds/FusekiBasicMain.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-basic/src/main/java/org/apache/jena/fuseki/cmds/FusekiBasicMain.java b/jena-fuseki2/jena-fuseki-basic/src/main/java/org/apache/jena/fuseki/cmds/FusekiBasicMain.java
new file mode 100644
index 0000000..6febd26
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-basic/src/main/java/org/apache/jena/fuseki/cmds/FusekiBasicMain.java
@@ -0,0 +1,513 @@
+/*
+ * 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.fuseki.cmds;
+
+import java.net.BindException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import arq.cmdline.CmdARQ;
+import arq.cmdline.ModAssembler;
+import arq.cmdline.ModDatasetAssembler;
+import jena.cmd.ArgDecl;
+import jena.cmd.CmdException;
+import org.apache.jena.assembler.exceptions.AssemblerException;
+import org.apache.jena.atlas.lib.DateTimeUtils;
+import org.apache.jena.atlas.lib.FileOps;
+import org.apache.jena.atlas.logging.FmtLog;
+import org.apache.jena.fuseki.Fuseki;
+import org.apache.jena.fuseki.FusekiException;
+import org.apache.jena.fuseki.embedded.FusekiServer;
+import org.apache.jena.fuseki.server.DataAccessPoint;
+import org.apache.jena.fuseki.server.DataAccessPointRegistry;
+import org.apache.jena.fuseki.server.DataService;
+import org.apache.jena.fuseki.servlets.SPARQL_QueryGeneral;
+import org.apache.jena.fuseki.validation.DataValidator;
+import org.apache.jena.fuseki.validation.IRIValidator;
+import org.apache.jena.fuseki.validation.QueryValidator;
+import org.apache.jena.fuseki.validation.UpdateValidator;
+import org.apache.jena.query.ARQ;
+import org.apache.jena.query.Dataset;
+import org.apache.jena.riot.Lang;
+import org.apache.jena.riot.RDFDataMgr;
+import org.apache.jena.riot.RDFLanguages;
+import org.apache.jena.sparql.core.DatasetGraphFactory;
+import org.apache.jena.sys.JenaSystem;
+import org.apache.jena.system.Txn;
+import org.apache.jena.tdb.TDB;
+import org.apache.jena.tdb.TDBFactory;
+import org.apache.jena.tdb.transaction.TransactionManager;
+import org.apache.jena.tdb2.DatabaseMgr;
+import org.slf4j.Logger;
+
+public class FusekiBasicMain extends CmdARQ {
+        private static int defaultPort = 3030;
+        
+        private static ArgDecl  argMem          = new ArgDecl(ArgDecl.NoValue,  "mem");
+        private static ArgDecl  argUpdate       = new ArgDecl(ArgDecl.NoValue,  "update", "allowUpdate");
+        private static ArgDecl  argFile         = new ArgDecl(ArgDecl.HasValue, "file");
+
+        private static ArgDecl  argTDB2mode     = new ArgDecl(ArgDecl.NoValue,  "tdb2");
+        private static ArgDecl  argMemTDB       = new ArgDecl(ArgDecl.NoValue,  "memtdb", "memTDB", "tdbmem");
+        private static ArgDecl  argTDB          = new ArgDecl(ArgDecl.HasValue, "loc", "location", "tdb");
+        
+        // No SPARQL dataset or services
+        private static ArgDecl  argEmpty        = new ArgDecl(ArgDecl.NoValue,  "empty", "no-dataset");
+        private static ArgDecl  argPort         = new ArgDecl(ArgDecl.HasValue, "port");
+        private static ArgDecl  argLocalhost    = new ArgDecl(ArgDecl.NoValue,  "localhost", "local");
+        private static ArgDecl  argTimeout      = new ArgDecl(ArgDecl.HasValue, "timeout");
+        private static ArgDecl  argConfig       = new ArgDecl(ArgDecl.HasValue, "config", "conf");
+        private static ArgDecl  argGZip         = new ArgDecl(ArgDecl.HasValue, "gzip");
+        private static ArgDecl  argBase         = new ArgDecl(ArgDecl.HasValue, "base", "files");
+        private static ArgDecl  argSparqler     = new ArgDecl(ArgDecl.HasValue, "sparqler");
+        private static ArgDecl  argValidators   = new ArgDecl(ArgDecl.NoValue,  "validators");
+        // private static ModLocation modLocation = new ModLocation();
+        private static ModDatasetAssembler modDataset      = new ModDatasetAssembler();
+
+        private final ServerConfig serverConfig  = new ServerConfig();
+        private boolean useTDB2;
+        
+        /** Build, but do not start, a server based on command line syntax. */  
+        public static FusekiServer build(String... argv) {
+            FusekiBasicMain inner = new FusekiBasicMain(argv);
+            inner.process();
+            return inner.buildServer();
+        }
+
+        static void innerMain(String... argv) {
+            JenaSystem.init();
+            new FusekiBasicMain(argv).mainRun();
+        }
+
+        protected FusekiBasicMain(String... argv) {
+            super(argv);
+
+            if ( false )
+                // Consider ...
+                TransactionManager.QueueBatchSize = TransactionManager.QueueBatchSize / 2;
+
+            getUsage().startCategory("Fuseki");
+            addModule(modDataset);
+            add(argMem, "--mem",
+                "Create an in-memory, non-persistent dataset for the server");
+            add(argFile, "--file=FILE",
+                "Create an in-memory, non-persistent dataset for the server, initialised with the contents of the file");
+            add(argTDB2mode, "--tdb2",
+                "Create command line persistent datasets with TDB2");
+            add(argTDB, "--loc=DIR",
+                "Use an existing TDB database (or create if does not exist)");
+            add(argMemTDB, "--memTDB",
+                "Create an in-memory, non-persistent dataset using TDB (testing only)");
+//            add(argEmpty, "--empty",
+//                "Run with no datasets and services (validators only)");
+            add(argEmpty); // Hidden for now.
+            add(argPort, "--port",
+                "Listen on this port number");
+            add(argLocalhost, "--localhost",
+                "Listen only on the localhost interface");
+            add(argTimeout, "--timeout=",
+                "Global timeout applied to queries (value in ms) -- format is X[,Y] ");
+            add(argUpdate, "--update",
+                "Allow updates (via SPARQL Update and SPARQL HTTP Update)");
+            add(argConfig, "--config=",
+                "Use a configuration file to determine the services");
+            add(argGZip, "--gzip=on|off",
+                "Enable GZip compression (HTTP Accept-Encoding) if request header set");
+            add(argBase, "--base=DIR",
+                "Directory for static content");
+            add(argSparqler, "--sparqler=DIR",
+                "Run with SPARQLer services Directory for static content");
+            add(argValidators, "--validators", "Install validators");
+
+            super.modVersion.addClass(TDB.class);
+            super.modVersion.addClass(Fuseki.class);
+        }
+
+        static String argUsage = "[--config=FILE] [--mem|--desc=AssemblerFile|--file=FILE] [--port PORT] /DatasetPathName";
+
+        @Override
+        protected String getSummary() {
+            return getCommandName() + " " + argUsage;
+        }
+
+        @Override
+        protected void processModulesAndArgs() {
+            int x = 0;
+
+            Logger log = Fuseki.serverLog;
+
+            // ---- Checking
+
+            if ( contains(argMem) )             
+                x++;
+            if ( contains(argFile) )
+                x++;
+            if ( contains(ModAssembler.assemblerDescDecl) )
+                x++;
+            if ( contains(argTDB) )
+                x++;
+            if ( contains(argMemTDB) )
+                x++;
+            if ( contains(argConfig) )
+                x++;
+
+            boolean allowEmpty = contains(argEmpty) || contains(argSparqler);
+            
+            
+            if ( x == 0 && ! allowEmpty )
+                throw new CmdException("No dataset specified on the command line.");
+
+            if ( x > 1 )
+                throw new CmdException("Multiple ways providing a dataset. Only one of --mem, --file, --loc or --desc");
+            
+            if ( x > 0 && allowEmpty )
+                throw new CmdException("Dataset provided but 'no dataset' flag given");
+            
+            //---- check: Invalid: --conf + service name.
+            if ( contains(argConfig) ) {
+                if ( getPositional().size() != 0 )
+                    throw new CmdException("Can't have both a configutation file and a service name");
+            } else if ( ! allowEmpty ) {
+                if ( getPositional().size() == 0 )
+                    throw new CmdException("Missing service name");
+                if ( getPositional().size() > 1 )
+                    throw new CmdException("Multiple dataset path names given");
+                serverConfig.datasetPath = DataAccessPoint.canonical(getPositionalArg(0));
+            }
+            
+            serverConfig.datasetDescription = "<unset>";
+            
+            // ---- check: Invalid: --update + --conf
+            if ( contains(argUpdate) && contains(argConfig) )
+                throw new CmdException("--update and a configuration file does not make sense (control using the configuration file only)");
+            boolean allowUpdate = contains(argUpdate);
+            serverConfig.allowUpdate = allowUpdate;
+
+            // ---- Port
+            serverConfig.port = defaultPort;
+            
+            if ( contains(argPort) ) {
+                String portStr = getValue(argPort);
+                try {
+                    int port = Integer.parseInt(portStr);
+                    serverConfig.port = port;
+                } catch (NumberFormatException ex) {
+                    throw new CmdException(argPort.getKeyName() + " : bad port number: " + portStr);
+                }
+            }
+            if ( contains(argLocalhost) )
+                serverConfig.loopback = true;
+
+            // ---- Dataset
+            // Only one of these is choose from the checking above.
+            
+            // Which TDB to use to create a command line TDB database. 
+            useTDB2 = contains(argTDB2mode);
+            String tag = useTDB2 ? "TDB2" : "TDB";
+            
+            if ( allowEmpty ) {
+                serverConfig.empty = true;
+                serverConfig.datasetDescription = "No dataset";
+            }                
+
+            // Fuseki config file 
+            if ( contains(argConfig) ) {
+                String file = getValue(argConfig);
+                if ( file.startsWith("file:") )
+                    file = file.substring("file:".length());
+                
+                Path path = Paths.get(file);
+                if ( ! Files.exists(path) )
+                    throw new CmdException("File not found: "+file);
+                if ( Files.isDirectory(path) )
+                    throw new CmdException("Is a directory: "+file);
+                serverConfig.datasetDescription = "Configuration: "+path.toAbsolutePath();
+                serverConfig.serverConfig = getValue(argConfig);
+            }
+            
+            // Ways to setup a dataset.
+            if ( contains(argMem) ) {
+                serverConfig.datasetDescription = "in-memory";
+                // Only one setup should be called by the test above but to be safe
+                // and in case of future changes, clear the configuration.  
+                serverConfig.dsg = DatasetGraphFactory.createTxnMem();
+                // Always allow, else you can't do very much! 
+                serverConfig.allowUpdate = true;
+            }
+
+            if ( contains(argFile) ) {
+                String filename = getValue(argFile);
+                String pathname = filename;
+                if ( filename.startsWith("file:") )
+                    pathname = filename.substring("file:".length());
+
+                serverConfig.datasetDescription = "file:"+filename;
+                if ( !FileOps.exists(pathname) )
+                    throw new CmdException("File not found: " + filename);
+                serverConfig.dsg = DatasetGraphFactory.createTxnMem();
+                
+                // INITIAL DATA.
+                Lang language = RDFLanguages.filenameToLang(filename);
+                if ( language == null )
+                    throw new CmdException("Can't guess language for file: " + filename);
+                Txn.executeWrite(serverConfig.dsg,  ()->RDFDataMgr.read(serverConfig.dsg, filename));
+            }
+
+//            if ( contains(argMemTDB) ) {
+//                //log.info("TDB dataset: in-memory") ;
+//                cmdLineConfig.reset();
+//                cmdLineConfig.argTemplateFile = useTDB2 ? Template.templateTDB2_MemFN : Template.templateTDB1_MemFN ;
+//                cmdLineConfig.params.put(Template.DIR, Names.memName) ;
+//                // Always allow.
+//                cmdLineConfig.allowUpdate = true ;
+//                cmdLineConfig.datasetDescription = useTDB2 ? "TDB2 dataset (in-memory)" : "TDB dataset (in-memory)";
+//            }
+//
+//            if ( contains(argTDB) ) {
+//                cmdLineConfig.reset();
+//                cmdLineConfig.argTemplateFile = 
+//                    useTDB2 ? Template.templateTDB2_DirFN : Template.templateTDB1_DirFN;
+//                String dir = getValue(argTDB) ;
+//                cmdLineConfig.params.put(Template.DIR, dir) ;
+//                cmdLineConfig.datasetDescription = useTDB2 ? "TDB2 dataset: "+dir : "TDB dataset: "+dir;
+//            }
+            
+            if ( contains(argMemTDB) ) {
+                serverConfig.datasetDescription = tag+" dataset in-memory";
+                serverConfig.dsg =
+                    useTDB2
+                    ? DatabaseMgr.createDatasetGraph()
+                    : TDBFactory.createDatasetGraph();
+                serverConfig.allowUpdate = true;
+            }
+
+            if ( contains(argTDB) ) {
+                String dir = getValue(argTDB);
+                serverConfig.datasetDescription = tag+" dataset: "+dir;
+                serverConfig.dsg = 
+                    useTDB2
+                    ? DatabaseMgr.connectDatasetGraph(dir)
+                    : TDBFactory.createDatasetGraph(dir);
+            }
+
+            if ( contains(ModAssembler.assemblerDescDecl) ) {
+                serverConfig.datasetDescription = "Assembler: "+ getValue(ModAssembler.assemblerDescDecl);
+                // Need to add service details.
+                Dataset ds = modDataset.createDataset();
+                serverConfig.dsg = ds.asDatasetGraph();
+            }
+
+            // ---- Misc features.
+            if ( contains(argTimeout) ) {
+                String str = getValue(argTimeout);
+                ARQ.getContext().set(ARQ.queryTimeout, str);
+            }
+            
+            if ( contains(argValidators) ) {
+                serverConfig.validators = true;
+            }
+                
+            if ( contains(argSparqler) ) {
+                String filebase = getValue(argSparqler);
+                if ( ! FileOps.exists(filebase) )
+                    throw new CmdException("File area not found: "+filebase); 
+                serverConfig.contentDirectory = filebase;
+                serverConfig.sparqler = true;
+                serverConfig.validators = true;
+            }
+            
+            if ( contains(argBase) ) {
+                // Static files.
+                String filebase = getValue(argBase);
+                if ( ! FileOps.exists(filebase) ) {
+                    throw new CmdException("File area not found: "+filebase); 
+                    //FmtLog.warn(Fuseki.configLog, "File area not found: "+filebase);  
+                }
+                serverConfig.contentDirectory = filebase;
+            }
+
+//            if ( contains(argGZip) ) {
+//                if ( !hasValueOfTrue(argGZip) && !hasValueOfFalse(argGZip) )
+//                    throw new CmdException(argGZip.getNames().get(0) + ": Not understood: " + getValue(argGZip));
+//                jettyServerConfig.enableCompression = super.hasValueOfTrue(argGZip);
+//            }
+        }
+
+//        private static String sort_out_dir(String path) {
+//            path.replace('\\', '/');
+//            if ( !path.endsWith("/") )
+//                path = path + "/";
+//            return path;
+//        }
+
+        @Override
+        protected void exec() {
+            try {
+                FusekiServer server = buildServer(serverConfig);
+                info(server, serverConfig);
+                try {
+                    server.start();
+                } catch (FusekiException ex) {
+                    if ( ex.getCause() instanceof BindException ) {
+                        Fuseki.serverLog.error("Failed to start server: "+ex.getCause().getMessage()+ ": port="+serverConfig.port) ;
+                        System.exit(1);
+                    }
+                    throw ex;
+                } catch (Exception ex) {
+                    throw new FusekiException("Failed to start server: " + ex.getMessage(), ex) ;
+                }
+                server.join();
+                System.exit(0);
+            }
+            catch (AssemblerException ex) {
+                if ( ex.getCause() != null )
+                    System.err.println(ex.getCause().getMessage());
+                else
+                    System.err.println(ex.getMessage());
+            }
+        }
+
+        private FusekiServer buildServer() {
+            return buildServer(serverConfig);
+        }
+
+        // ServerConfig -> Setup the builder.
+        private FusekiServer buildServer(ServerConfig serverConfig) {
+            FusekiServer.Builder builder = builder();
+            return buildServer(builder, serverConfig);
+        }
+        
+        protected FusekiServer.Builder builder() {
+            return FusekiServer.create();
+        }
+        
+        private static FusekiServer buildServer(FusekiServer.Builder builder, ServerConfig serverConfig) {
+            builder.port(serverConfig.port);
+            builder.loopback(serverConfig.loopback);
+            
+            if ( serverConfig.validators ) {
+                if ( serverConfig.sparqler )
+                    builder.addServlet("/sparql",  new SPARQL_QueryGeneral());
+                // Validators.
+                builder.addServlet("/validate/query",  new QueryValidator());
+                builder.addServlet("/validate/update", new UpdateValidator());
+                builder.addServlet("/validate/iri",    new IRIValidator());
+                builder.addServlet("/validate/data",   new DataValidator());
+            } 
+            if ( ! serverConfig.empty ) {
+                if ( serverConfig.serverConfig != null )
+                    // Config file.
+                    builder.parseConfigFile(serverConfig.serverConfig);
+                else
+                    // One dataset.
+                    builder.add(serverConfig.datasetPath, serverConfig.dsg, serverConfig.allowUpdate);
+            }
+            
+            if ( serverConfig.contentDirectory != null )
+                builder.staticFileBase(serverConfig.contentDirectory) ;
+
+            return builder.build();
+        }
+
+        private void info(FusekiServer server, ServerConfig serverConfig) {
+            if ( super.isQuiet() )
+                return;
+
+            Logger log = Fuseki.serverLog;
+
+            String version = Fuseki.VERSION;
+            String buildDate = Fuseki.BUILD_DATE ;
+
+            if ( version != null && version.equals("${project.version}") )
+                version = null ;
+            if ( buildDate != null && buildDate.equals("${build.time.xsd}") )
+                buildDate = DateTimeUtils.nowAsXSDDateTimeString() ;
+            
+            String name = Fuseki.NAME;
+            name = name +" (basic server)";
+            
+            if ( version != null ) {
+                if ( Fuseki.developmentMode && buildDate != null )
+                    FmtLog.info(log, "%s %s %s", name, version, buildDate) ;
+                else
+                    FmtLog.info(log, "%s %s", name, version);
+            }
+            
+            // Dataset -> Endpoints
+            Map<String, List<String>> mapDatasetEndpoints = description(DataAccessPointRegistry.get(server.getServletContext()));
+            
+            if ( serverConfig.empty ) {
+                FmtLog.info(log, "No SPARQL datasets services"); 
+            } else {
+                if ( serverConfig.datasetPath == null && serverConfig.serverConfig == null )
+                    log.error("No dataset path nor server configuration file");
+            }
+            
+            if ( serverConfig.datasetPath != null ) {
+                if ( mapDatasetEndpoints.size() != 1 )
+                    log.error("Expected only one dataset");
+                List<String> endpoints = mapDatasetEndpoints.get(serverConfig.datasetPath); 
+                FmtLog.info(log,  "Dataset Type = %s", serverConfig.datasetDescription);
+                FmtLog.info(log,  "Path = %s; Services = %s", serverConfig.datasetPath, endpoints);
+            }
+            if ( serverConfig.serverConfig != null ) {
+                // May be many datasets and services.
+                FmtLog.info(log,  "Configuration file %s", serverConfig.serverConfig);
+                mapDatasetEndpoints.forEach((path, endpoints)->{
+                    FmtLog.info(log,  "Path = %s; Services = %s", path, endpoints);
+                });
+            }
+            
+            if ( serverConfig.contentDirectory != null )
+                FmtLog.info(log,  "Static files = %s", serverConfig.contentDirectory);
+                
+            if ( super.isVerbose() )
+                PlatformInfo.logDetailsVerbose(log);
+            else if ( !super.isQuiet() )
+                PlatformInfo.logDetails(log);
+        }
+
+        private static Map<String, List<String>> description(DataAccessPointRegistry reg) {
+            Map<String, List<String>> desc = new LinkedHashMap<>();
+            reg.forEach((ds,dap)->{
+                List<String> endpoints = new ArrayList<>();
+                desc.put(ds, endpoints);
+                DataService dSrv = dap.getDataService();
+                dSrv.getOperations().forEach((op)->{
+                    dSrv.getEndpoints(op).forEach(ep-> {
+                        String x = ep.getEndpoint();
+                        if ( x.isEmpty() )
+                            x = "quads";
+                        endpoints.add(x);   
+                    });
+                });
+            });
+            return desc;
+        }
+        
+        @Override
+        protected String getCommandName() {
+            return "fuseki";
+        }
+    }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jena/blob/20eb07ca/jena-fuseki2/jena-fuseki-basic/src/main/java/org/apache/jena/fuseki/cmds/ServerConfig.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-basic/src/main/java/org/apache/jena/fuseki/cmds/ServerConfig.java b/jena-fuseki2/jena-fuseki-basic/src/main/java/org/apache/jena/fuseki/cmds/ServerConfig.java
new file mode 100644
index 0000000..e8301e6
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-basic/src/main/java/org/apache/jena/fuseki/cmds/ServerConfig.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.cmds;
+
+import org.apache.jena.sparql.core.DatasetGraph;
+
+/** Setup details (command line, config file) from command line processing.
+ *  Thisis built by {@link FusekiBasicMain#exec}.
+ *  This is processed by {@link FusekiBasicMain#buildServer}.
+ */
+class ServerConfig {
+    /** Server port */
+    public int port;
+    /** Loopback */
+    public boolean loopback           = false;
+    /** The dataset name */
+    public String    datasetPath      = null; 
+    /** Allow update */
+    public boolean   allowUpdate      = false;
+    
+    // This is set ...
+    public DatasetGraph dsg           = null;
+    // ... or this.
+    public String serverConfig        = null;
+    
+
+    /** Allow there to be no registered datasets without it being an error. */
+    public boolean empty              = false ;
+    /** Setup for SPARQLer - validators and general query engine, some pages. */ 
+    public boolean sparqler           = false ;
+    public boolean validators         = false ;
+    /** An informative label */
+    public String datasetDescription;
+    public String contentDirectory    = null;
+}
\ No newline at end of file


[07/27] jena git commit: Refactor to make subclassing easier

Posted by an...@apache.org.
Refactor to make subclassing easier


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

Branch: refs/heads/master
Commit: 62656645e698792116e82495320cca131ae45304
Parents: ea7f95e
Author: Andy Seaborne <an...@apache.org>
Authored: Wed Aug 22 16:40:47 2018 +0100
Committer: Andy Seaborne <an...@apache.org>
Committed: Thu Aug 23 17:15:05 2018 +0100

----------------------------------------------------------------------
 .../org/apache/jena/sparql/util/Context.java    | 89 +++++++++++++-------
 1 file changed, 59 insertions(+), 30 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/62656645/jena-arq/src/main/java/org/apache/jena/sparql/util/Context.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/util/Context.java b/jena-arq/src/main/java/org/apache/jena/sparql/util/Context.java
index 9318cf1..44c7812 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/util/Context.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/util/Context.java
@@ -20,6 +20,8 @@ package org.apache.jena.sparql.util ;
 
 import java.util.* ;
 import java.util.concurrent.ConcurrentHashMap ;
+import java.util.function.BiConsumer;
+
 import org.apache.jena.atlas.lib.Lib ;
 import org.apache.jena.query.ARQ ;
 import org.apache.jena.sparql.ARQConstants ;
@@ -54,6 +56,44 @@ public class Context {
         putAll(cxt) ;
     }
 
+    // All access to the underlying goes via map*.
+    
+    protected Object mapGet(Symbol property) {
+        return context.get(property);
+    }
+
+    protected void mapPut(Symbol property, Object value) {
+        if ( readonly )
+            throw new ARQException("Context is readonly") ;
+        if ( property == null )
+            throw new ARQException("Context key is null") ;
+        if ( value == null ) {
+            mapRemove(property) ;
+            return ;
+        }
+        context.put(property, value) ;
+    }
+
+    protected void mapRemove(Symbol property) {
+        context.remove(property);
+    }
+
+    protected boolean mapContains(Symbol property) {
+        return context.containsKey(property);
+    }
+
+    protected Set<Symbol> mapKeySet() {
+        return context.keySet();
+    }
+
+    protected int mapSize() {
+        return context.size();
+    }
+
+    protected void mapForEach(BiConsumer<Symbol, Object> action) {
+        context.forEach(action);
+    }
+
     /**
      * Return a copy of this context. Modifications of the copy do not affect
      * the original context.
@@ -67,7 +107,7 @@ public class Context {
     /** Get the object value of a property or null */
     @SuppressWarnings("unchecked")
     public <T> T get(Symbol property) {
-        return (T) context.get(property) ;
+        return (T) mapGet(property) ;
     }
 
     /**
@@ -75,7 +115,7 @@ public class Context {
      * present .
      */
     public Object get(Symbol property, Object defaultValue) {
-        Object x = context.get(property) ;
+        Object x = mapGet(property) ;
         if ( x == null )
             return defaultValue ;
         return x ;
@@ -83,26 +123,16 @@ public class Context {
 
     /** Store a named value - overwrites any previous set value */
     public void put(Symbol property, Object value) {
-        _put(property, value) ;
+        mapPut(property, value) ;
     }
 
     /** Store a named value - overwrites any previous set value */
     public void set(Symbol property, Object value) {
-        _put(property, value) ;
-    }
-
-    private void _put(Symbol property, Object value) {
-        if ( readonly )
-            throw new ARQException("Context is readonly") ;
-        if ( property == null )
-            throw new ARQException("Context key is null") ;
-        if ( value == null ) {
-            context.remove(property) ;
-            return ;
-        }
-        context.put(property, value) ;
+        mapPut(property, value) ;
     }
 
+    // All access to the underlying goes via map*.
+    
     /** Store a named value - overwrites any previous set value */
     public void set(Symbol property, boolean value) {
         if ( value )
@@ -113,19 +143,19 @@ public class Context {
 
     /** Store a named value only if it is not currently set */
     public void setIfUndef(Symbol property, Object value) {
-        Object x = context.get(property) ;
+        Object x = mapGet(property) ;
         if ( x == null )
             put(property, value) ;
     }
 
     /** Remove any value associated with a property */
     public void remove(Symbol property) {
-        context.remove(property) ;
+        mapRemove(property) ;
     }
 
     /** Remove any value associated with a property - alternative method name */
     public void unset(Symbol property) {
-        context.remove(property) ;
+        remove(property) ;
     }
 
     // ---- Helpers
@@ -134,7 +164,7 @@ public class Context {
 
     /** Is a property set? */
     public boolean isDefined(Symbol property) {
-        return context.containsKey(property) ;
+        return mapContains(property) ;
     }
 
     /** Is a property not set? */
@@ -157,7 +187,7 @@ public class Context {
 
     /** Get the value a string (uses .toString() if the value is not null) */
     public String getAsString(Symbol property) {
-        Object x = context.get(property) ;
+        Object x = mapGet(property) ;
         if ( x == null )
             return null ;
         return x.toString() ;
@@ -167,7 +197,7 @@ public class Context {
     public int getInt(Symbol symbol, int defaultValue) {
         if (  isUndef(symbol) )
             return defaultValue ; 
-        Object obj = context.get(symbol) ;
+        Object obj = mapGet(symbol) ;
         if ( obj instanceof String ) {
             return Integer.parseInt((String)obj) ;
         } else if ( obj instanceof Integer ) {
@@ -181,7 +211,7 @@ public class Context {
     public long getLong(Symbol symbol, long defaultValue) {
         if (  isUndef(symbol) )
             return defaultValue ; 
-        Object obj = context.get(symbol) ;
+        Object obj = mapGet(symbol) ;
         if ( obj instanceof String ) {
             return Long.parseLong((String)obj) ;
         } else if ( obj instanceof Integer ) {
@@ -189,7 +219,7 @@ public class Context {
         } else if ( obj instanceof Long ) {
             return ((Long)obj) ;
         } else {
-            throw new ARQException("Value for "+symbol+" is not a recoginized class: "+Lib.className(obj)) ;
+            throw new ARQException("Value for "+symbol+" is not a recognized class: "+Lib.className(obj)) ;
         }
     }
     
@@ -197,7 +227,7 @@ public class Context {
         if ( readonly )
             throw new ARQException("Context is readonly") ;
         if ( other != null )
-            other.context.forEach(this::put);
+            other.mapForEach(this::put);
     }
 
     // -- true/false
@@ -299,12 +329,12 @@ public class Context {
 
     /** Set of properties (as Symbols) currently defined */
     public Set<Symbol> keys() {
-        return context.keySet() ;
+        return mapKeySet() ;
     }
 
     /** Return the number of context items */
     public int size() {
-        return context.size() ;
+        return mapSize() ;
     }
 
     @Override
@@ -319,7 +349,7 @@ public class Context {
         return x ;
     }
 
-    /** Setup a context using anouter context and a dataset.
+    /** Setup a context using another context and a dataset.
      *  This adds the current time.
      */
     public static Context setupContextExec(Context globalContext, DatasetGraph dataset) {
@@ -343,7 +373,7 @@ public class Context {
         return context;
     }
 
-    /** Merge an outer (fglobal) and local context to produce a new context
+    /** Merge an outer (global) and local context to produce a new context
      * The new context is always a separate copy.  
      */
     public static Context mergeCopy(Context contextGlobal, Context contextLocal) {
@@ -383,5 +413,4 @@ public class Context {
             return false ;
         return true ;
     }
-
 }


[27/27] jena git commit: JENA-1594: Merge commit 'refs/pull/465/head' of https://github.com/apache/jena

Posted by an...@apache.org.
JENA-1594: Merge commit 'refs/pull/465/head' of https://github.com/apache/jena

This closes #465.


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

Branch: refs/heads/master
Commit: c8a31476d4072b44790965d5d79963c545e6c2ec
Parents: 0a0b831 3b01252
Author: Andy Seaborne <an...@apache.org>
Authored: Fri Aug 31 13:04:11 2018 +0100
Committer: Andy Seaborne <an...@apache.org>
Committed: Fri Aug 31 13:04:11 2018 +0100

----------------------------------------------------------------------
 .travis.yml                                     |   2 +-
 .../org/apache/jena/query/DatasetFactory.java   |   9 +-
 .../jena/query/QueryExecutionFactory.java       |  62 ++-
 .../apache/jena/sparql/core/DatasetGraph.java   |   4 +-
 .../jena/sparql/core/DatasetGraphCopyAdd.java   |  41 --
 .../jena/sparql/core/DatasetGraphFactory.java   |   5 +-
 .../sparql/core/DatasetGraphFilteredView.java   | 159 ++++++
 .../jena/sparql/core/DatasetGraphReadOnly.java  |  21 +-
 .../jena/sparql/core/DatasetGraphWrapper.java   |  17 +-
 .../sparql/core/DatasetGraphWrapperView.java    |  30 ++
 .../engine/QueryEngineFactoryWrapper.java       |  13 +-
 .../jena/sparql/engine/QueryExecutionBase.java  |  36 +-
 .../org/apache/jena/sparql/expr/ExprSystem.java |   9 +-
 .../jena/sparql/sse/builders/BuilderLib.java    | 245 ++++-----
 .../org/apache/jena/sparql/util/Context.java    |  89 +--
 .../org/apache/jena/sparql/util/FmtUtils.java   |   2 +-
 .../org/apache/jena/sparql/core/TS_Core.java    |   3 +-
 .../core/TestDatasetGraphFilteredView.java      | 162 ++++++
 jena-base/pom.xml                               |   7 +
 .../rulesys/impl/TestLPBRuleEngine.java         |   4 +-
 .../tdb2/solver/StageGeneratorDirectTDB.java    |   2 +-
 .../jena/tdb2/store/GraphViewSwitchable.java    |  11 +-
 jena-fuseki2/jena-fuseki-access/pom.xml         | 104 ++++
 .../fuseki/access/AssemblerAccessDataset.java   |  63 +++
 .../access/AssemblerSecurityRegistry.java       | 130 +++++
 .../fuseki/access/AuthorizationService.java     |  30 ++
 .../jena/fuseki/access/DataAccessCtl.java       | 163 ++++++
 .../jena/fuseki/access/DataAccessLib.java       |  81 +++
 .../access/DatasetGraphAccessControl.java       |  59 ++
 .../fuseki/access/Filtered_REST_Quads_R.java    |  43 ++
 .../fuseki/access/Filtered_SPARQL_GSP_R.java    |  39 ++
 .../access/Filtered_SPARQL_QueryDataset.java    |  63 +++
 .../apache/jena/fuseki/access/GraphFilter.java  |  76 +++
 .../jena/fuseki/access/GraphFilterTDB1.java     |  70 +++
 .../jena/fuseki/access/GraphFilterTDB2.java     |  70 +++
 .../apache/jena/fuseki/access/InitSecurity.java |  38 ++
 .../jena/fuseki/access/SecurityContext.java     | 162 ++++++
 .../jena/fuseki/access/SecurityRegistry.java    |  63 +++
 .../jena/fuseki/access/VocabSecurity.java       |  55 ++
 .../org.apache.jena.sys.JenaSubsystemLifecycle  |   1 +
 .../access/AbstractTestSecurityAssembler.java   | 326 +++++++++++
 .../jena/fuseki/access/AccessTestLib.java       |  70 +++
 .../fuseki/access/TS_SecurityFiltering.java     |  44 ++
 .../fuseki/access/TestAssemblerSeparate.java    |  26 +
 .../jena/fuseki/access/TestAssemblerShared.java |  26 +
 .../access/TestSecurityAssemblerBuild.java      |  46 ++
 .../fuseki/access/TestSecurityFilterFuseki.java | 315 +++++++++++
 .../fuseki/access/TestSecurityFilterLocal.java  | 355 ++++++++++++
 .../src/test/resources/log4j.properties         |  40 ++
 .../testing/Access/assem-security-shared.ttl    |  58 ++
 .../testing/Access/assem-security.ttl           |  63 +++
 .../apache/jena/fuseki/cmds/FusekiBasicCmd.java | 539 +------------------
 .../jena/fuseki/cmds/FusekiBasicMain.java       | 513 ++++++++++++++++++
 .../apache/jena/fuseki/cmds/ServerConfig.java   |  51 ++
 jena-fuseki2/jena-fuseki-core/pom.xml           |   2 +
 .../java/org/apache/jena/fuseki/Fuseki.java     |   2 +-
 .../jena/fuseki/build/FusekiBuildLib.java       |   5 +-
 .../apache/jena/fuseki/build/FusekiBuilder.java |   3 +
 .../apache/jena/fuseki/build/FusekiConfig.java  |   3 +-
 .../apache/jena/fuseki/build/FusekiConst.java   |  41 ++
 .../org/apache/jena/fuseki/cmd/FusekiCmd.java   |   6 +-
 .../org/apache/jena/fuseki/cmd/JettyFuseki.java | 325 +++++++++++
 .../apache/jena/fuseki/ctl/ActionAsyncTask.java |   1 -
 .../apache/jena/fuseki/ctl/ActionBackup.java    |  65 ---
 .../jena/fuseki/ctl/ActionBackupList.java       |  94 ----
 .../org/apache/jena/fuseki/ctl/ActionItem.java  |  46 ++
 .../org/apache/jena/fuseki/ctl/ActionStats.java |  11 +-
 .../apache/jena/fuseki/ctl/JsonDescription.java |  73 +++
 .../org/apache/jena/fuseki/ctl/TaskBase.java    |   4 +-
 .../apache/jena/fuseki/jetty/JettyFuseki.java   | 325 -----------
 .../org/apache/jena/fuseki/jetty/JettyLib.java  | 169 ++++++
 .../apache/jena/fuseki/mgt/ActionBackup.java    |  68 +++
 .../jena/fuseki/mgt/ActionBackupList.java       |  95 ++++
 .../apache/jena/fuseki/mgt/ActionDatasets.java  |   7 +-
 .../org/apache/jena/fuseki/mgt/ActionItem.java  |  46 --
 .../jena/fuseki/mgt/ActionServerStatus.java     |  12 +-
 .../apache/jena/fuseki/mgt/JsonDescription.java |  72 ---
 .../org/apache/jena/fuseki/mgt/MgtConst.java    |  52 --
 .../java/org/apache/jena/fuseki/mgt/MgtJMX.java |  61 ---
 .../apache/jena/fuseki/mgt/ServerMgtConst.java  |  39 ++
 .../apache/jena/fuseki/mgt/ServiceMXBean.java   |  32 --
 .../apache/jena/fuseki/server/Operation.java    |   6 +-
 .../apache/jena/fuseki/server/ServerConst.java  |  41 ++
 .../apache/jena/fuseki/servlets/ActionREST.java |   9 +
 .../jena/fuseki/servlets/ActionService.java     |   2 +-
 .../jena/fuseki/servlets/REST_Quads_R.java      |   2 +-
 .../jena/fuseki/servlets/REST_Quads_RW.java     |   4 +-
 .../apache/jena/fuseki/servlets/SPARQL_GSP.java |  16 +-
 .../jena/fuseki/servlets/SPARQL_GSP_R.java      |  13 +-
 .../jena/fuseki/servlets/SPARQL_GSP_RW.java     |  21 +-
 .../jena/fuseki/servlets/SPARQL_Query.java      |  16 +-
 .../servlets/ServiceDispatchRegistry.java       |  10 +-
 .../jena/fuseki/system/FusekiLogging.java       |  18 +-
 .../webapp/FusekiServerEnvironmentInit.java     |   5 +-
 .../apache/jena/fuseki/webapp/SystemState.java  |  16 -
 .../src/main/webapp/WEB-INF/web.xml             |   8 +-
 .../src/main/webapp/js/app/controllers/.svnkeep |   0
 .../src/main/webapp/js/app/layouts/.svnkeep     |   0
 .../src/main/webapp/js/app/routers/.svnkeep     |   0
 .../src/main/webapp/js/app/views/.svnkeep       |   0
 .../java/org/apache/jena/fuseki/ServerCtl.java  |   2 +-
 .../java/org/apache/jena/fuseki/TS_Fuseki.java  |   2 +-
 .../java/org/apache/jena/fuseki/TestAdmin.java  |  19 +-
 .../jena/fuseki/embedded/FusekiServer.java      | 126 +++--
 .../jena/fuseki/embedded/JettyServer.java       | 369 +++++++++++++
 jena-fuseki2/pom.xml                            |   1 +
 .../jena/permissions/SecurityEvaluator.java     |  67 +--
 .../rdfconnection/RDFConnectionFactory.java     |  25 +
 .../jena/rdfconnection/RDFConnectionRemote.java |   2 +-
 pom.xml                                         |   4 +-
 110 files changed, 5432 insertions(+), 1746 deletions(-)
----------------------------------------------------------------------



[12/27] jena git commit: Use the connection's httpClient and httpContext for fetchDataset().

Posted by an...@apache.org.
Use the connection's httpClient and httpContext for fetchDataset().

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

Branch: refs/heads/master
Commit: dfa40be260e8e604223693f25d93ea3dc675b7c5
Parents: 09f0e8b
Author: Andy Seaborne <an...@apache.org>
Authored: Fri Aug 24 12:03:32 2018 +0100
Committer: Andy Seaborne <an...@apache.org>
Committed: Fri Aug 24 12:03:32 2018 +0100

----------------------------------------------------------------------
 .../java/org/apache/jena/rdfconnection/RDFConnectionRemote.java    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/dfa40be2/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnectionRemote.java
----------------------------------------------------------------------
diff --git a/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnectionRemote.java b/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnectionRemote.java
index 7570daf..7f730c9 100644
--- a/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnectionRemote.java
+++ b/jena-rdfconnection/src/main/java/org/apache/jena/rdfconnection/RDFConnectionRemote.java
@@ -421,7 +421,7 @@ public class RDFConnectionRemote implements RDFConnection {
             throw new ARQException("Dataset operations not available - no dataset URL provided"); 
         Dataset ds = DatasetFactory.createTxnMem();
         Txn.executeWrite(ds, ()->{
-            TypedInputStream s = exec(()->HttpOp.execHttpGet(destination, acceptDataset));
+            TypedInputStream s = exec(()->HttpOp.execHttpGet(destination, acceptDataset, this.httpClient, this.httpContext));
             Lang lang = RDFLanguages.contentTypeToLang(s.getContentType());
             RDFDataMgr.read(ds, s, lang);
         });


[24/27] jena git commit: Correct indentation.

Posted by an...@apache.org.
Correct indentation.

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

Branch: refs/heads/master
Commit: 228f4b96e0eea3081d6b707613b63eaba6ae013f
Parents: 5e00033
Author: Andy Seaborne <an...@apache.org>
Authored: Mon Aug 27 18:21:00 2018 +0100
Committer: Andy Seaborne <an...@apache.org>
Committed: Mon Aug 27 18:21:00 2018 +0100

----------------------------------------------------------------------
 jena-fuseki2/jena-fuseki-core/src/main/webapp/WEB-INF/web.xml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/228f4b96/jena-fuseki2/jena-fuseki-core/src/main/webapp/WEB-INF/web.xml
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/webapp/WEB-INF/web.xml b/jena-fuseki2/jena-fuseki-core/src/main/webapp/WEB-INF/web.xml
index b9f1ff1..0737e76 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/webapp/WEB-INF/web.xml
+++ b/jena-fuseki2/jena-fuseki-core/src/main/webapp/WEB-INF/web.xml
@@ -232,8 +232,8 @@
   </servlet>
 
   <servlet-mapping>
-   <servlet-name>DumpServlet</servlet-name>
-   <url-pattern>/$/dump</url-pattern>
+    <servlet-name>DumpServlet</servlet-name>
+    <url-pattern>/$/dump</url-pattern>
   </servlet-mapping>
 
   <servlet-mapping>


[20/27] jena git commit: JENA-1594: General mechanism for data accesx control using DatasetGraphFilteredView

Posted by an...@apache.org.
JENA-1594: General mechanism for data accesx control using DatasetGraphFilteredView


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

Branch: refs/heads/master
Commit: 99b994637cd1d91ce1eba0d61e0576776dcf708e
Parents: 7b0661f
Author: Andy Seaborne <an...@apache.org>
Authored: Sun Aug 26 18:08:53 2018 +0100
Committer: Andy Seaborne <an...@apache.org>
Committed: Sun Aug 26 19:27:52 2018 +0100

----------------------------------------------------------------------
 .../sparql/core/DatasetGraphFilteredView.java   |   9 +-
 .../jena/sparql/core/DatasetGraphWrapper.java   |  17 +-
 .../sparql/core/DatasetGraphWrapperView.java    |  30 ++++
 .../engine/QueryEngineFactoryWrapper.java       |  13 +-
 .../org/apache/jena/sparql/core/TS_Core.java    |   3 +-
 .../core/TestDatasetGraphFilteredView.java      | 162 +++++++++++++++++
 .../jena/fuseki/access/DataAccessCtl.java       |  53 ++----
 .../jena/fuseki/access/DataAccessLib.java       |  14 +-
 .../access/DatasetGraphAccessControl.java       |  12 +-
 .../fuseki/access/DatasetGraphFiltered.java     | 144 ---------------
 .../fuseki/access/Filtered_REST_Quads_R.java    |  22 +--
 .../fuseki/access/Filtered_SPARQL_GSP_R.java    |  17 +-
 .../access/Filtered_SPARQL_QueryDataset.java    |  26 +--
 .../jena/fuseki/access/SecurityPolicy.java      |  50 ++++--
 .../access/AbstractTestSecurityAssembler.java   |   7 +-
 .../jena/fuseki/access/AccessTestLib.java       |  70 ++++++++
 .../apache/jena/fuseki/access/GraphData.java    |  59 -------
 .../fuseki/access/TestSecurityFilterFuseki.java |  22 +--
 .../fuseki/access/TestSecurityFilterLocal.java  | 173 +++++++++++--------
 .../apache/jena/fuseki/servlets/ActionREST.java |   9 +
 .../jena/fuseki/servlets/REST_Quads_R.java      |  10 +-
 .../jena/fuseki/servlets/REST_Quads_RW.java     |   4 +-
 .../apache/jena/fuseki/servlets/SPARQL_GSP.java |   5 -
 .../jena/fuseki/servlets/SPARQL_GSP_R.java      |  15 +-
 .../jena/fuseki/servlets/SPARQL_GSP_RW.java     |  21 ++-
 25 files changed, 530 insertions(+), 437 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/99b99463/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphFilteredView.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphFilteredView.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphFilteredView.java
index a9fa8f2..3084e33 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphFilteredView.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphFilteredView.java
@@ -34,7 +34,7 @@ import org.apache.jena.sparql.graph.GraphUnionRead;
  * A read-only {@link DatasetGraph} that applies a filter testing all triples and quads
  * returned by accessing the data. Only quads where the filter tests for "true" are exposed. 
  */
-public class DatasetGraphFilteredView extends DatasetGraphReadOnly {
+public class DatasetGraphFilteredView extends DatasetGraphReadOnly implements DatasetGraphWrapperView {
   /* 
   Write operations
     add(Quad)
@@ -104,12 +104,15 @@ public class DatasetGraphFilteredView extends DatasetGraphReadOnly {
 
     @Override
     public Iterator<Node> listGraphNodes() {
-//        Predicate<Node> notSpecial = x-> ! ( Quad.isDefaultGraph(x)|| Quad.isUnionGraph(x) );
-//        return Iter.filter(visibleGraphs.iterator(), notSpecial);
         return visibleGraphs.iterator();
     }
 
     @Override
+    public long size() {
+        return visibleGraphs.size();
+    }
+
+    @Override
     public Graph getUnionGraph() {
         return new GraphUnionRead(this, visibleGraphs);
     }

http://git-wip-us.apache.org/repos/asf/jena/blob/99b99463/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphWrapper.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphWrapper.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphWrapper.java
index 426faef..f1d9861 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphWrapper.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphWrapper.java
@@ -40,7 +40,7 @@ public class DatasetGraphWrapper implements DatasetGraph, Sync
         return get();
     }
     
-    /** Recursively unwrap a DatasetGraphWrapped.
+    /** Recursively unwrap a {@link DatasetGraphWrapper}.
      * 
      * @return the first found {@link DatasetGraph} that is not an instance of {@link DatasetGraphWrapper}
      */
@@ -51,6 +51,21 @@ public class DatasetGraphWrapper implements DatasetGraph, Sync
         }
         return dsgw;
     }
+    
+    /** Recursively unwrap a {@link DatasetGraphWrapper}, stopping at a {@link DatasetGraphWrapper}
+     * that indicate it is "view changing", ie shows quads to the base dataset graph.  
+     * 
+     * @return the first found {@link DatasetGraph} that is not an instance of {@link DatasetGraphWrapper}
+     */
+    public final DatasetGraph getBaseForQuery() {
+        DatasetGraph dsgw = dsg;
+        while (dsgw instanceof DatasetGraphWrapper) {
+            if ( dsgw instanceof DatasetGraphWrapperView )
+                break;
+            dsgw = ((DatasetGraphWrapper)dsg).getWrapped();
+        }
+        return dsgw;
+    }
 
     /** The dataset to use for redirection - can be overridden.
      *  It is also guarantee that this is called only once per

http://git-wip-us.apache.org/repos/asf/jena/blob/99b99463/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphWrapperView.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphWrapperView.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphWrapperView.java
new file mode 100644
index 0000000..181ace1
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphWrapperView.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.core;
+
+import org.apache.jena.sparql.engine.QueryEngineFactoryWrapper;
+
+/** 
+ *  Marker interface that indicates a DatasetGraphWrapper
+ *  is one that defined a different view on the base dataset data.
+ *  Hence, query should not access the wrapped dataset.
+ *  
+ *  @see QueryEngineFactoryWrapper
+ */
+public interface DatasetGraphWrapperView extends DatasetGraph {}

http://git-wip-us.apache.org/repos/asf/jena/blob/99b99463/jena-arq/src/main/java/org/apache/jena/sparql/engine/QueryEngineFactoryWrapper.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/engine/QueryEngineFactoryWrapper.java b/jena-arq/src/main/java/org/apache/jena/sparql/engine/QueryEngineFactoryWrapper.java
index 2f00eee..9d5f775 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/engine/QueryEngineFactoryWrapper.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/engine/QueryEngineFactoryWrapper.java
@@ -22,9 +22,7 @@ import org.apache.jena.query.Query ;
 import org.apache.jena.sparql.algebra.Op ;
 import org.apache.jena.sparql.core.DatasetGraph ;
 import org.apache.jena.sparql.core.DatasetGraphWrapper ;
-import org.apache.jena.sparql.engine.Plan ;
-import org.apache.jena.sparql.engine.QueryEngineFactory ;
-import org.apache.jena.sparql.engine.QueryEngineRegistry ;
+import org.apache.jena.sparql.core.DatasetGraphWrapperView;
 import org.apache.jena.sparql.engine.binding.Binding ;
 import org.apache.jena.sparql.util.Context ;
 
@@ -36,7 +34,8 @@ public class QueryEngineFactoryWrapper implements QueryEngineFactory
     
     @Override
     public boolean accept(Query query, DatasetGraph dsg, Context context) {
-        if ( !( dsg instanceof DatasetGraphWrapper ) )
+        // DatasetGraphFilteredView changes the seen contents so we can't unwrap it for query. 
+        if ( !( dsg instanceof DatasetGraphWrapper ) || dsg instanceof DatasetGraphWrapperView )
             return false ;    
         DatasetGraph dsg2 = ((DatasetGraphWrapper)dsg).getWrapped() ;
         return QueryEngineRegistry.findFactory(query, dsg2, context).accept(query, dsg2, context) ;
@@ -44,7 +43,7 @@ public class QueryEngineFactoryWrapper implements QueryEngineFactory
 
     @Override
     public Plan create(Query query, DatasetGraph dsg, Binding inputBinding, Context context) {
-        if ( ! ( dsg instanceof DatasetGraphWrapper ) )
+        if ( !( dsg instanceof DatasetGraphWrapper ) || dsg instanceof DatasetGraphWrapperView )
             return null ;    
         DatasetGraph dsg2 = ((DatasetGraphWrapper)dsg).getWrapped() ;
         return QueryEngineRegistry.findFactory(query, dsg2, context).create(query, dsg2, inputBinding, context) ;
@@ -52,7 +51,7 @@ public class QueryEngineFactoryWrapper implements QueryEngineFactory
 
     @Override
     public boolean accept(Op op, DatasetGraph dsg, Context context) {
-        if ( ! ( dsg instanceof DatasetGraphWrapper ) )
+        if ( !( dsg instanceof DatasetGraphWrapper ) || dsg instanceof DatasetGraphWrapperView )
             return false ;    
         DatasetGraph dsg2 = ((DatasetGraphWrapper)dsg).getWrapped() ;
         return QueryEngineRegistry.findFactory(op, dsg2, context).accept(op, dsg2, context) ;
@@ -60,7 +59,7 @@ public class QueryEngineFactoryWrapper implements QueryEngineFactory
 
     @Override
     public Plan create(Op op, DatasetGraph dsg, Binding inputBinding, Context context) {
-        if ( ! ( dsg instanceof DatasetGraphWrapper ) )
+        if ( !( dsg instanceof DatasetGraphWrapper ) || dsg instanceof DatasetGraphWrapperView )
             return null ;    
         DatasetGraph dsg2 = ((DatasetGraphWrapper)dsg).getWrapped() ;
         return QueryEngineRegistry.findFactory(op, dsg2, context).create(op, dsg2, inputBinding, context) ;

http://git-wip-us.apache.org/repos/asf/jena/blob/99b99463/jena-arq/src/test/java/org/apache/jena/sparql/core/TS_Core.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/core/TS_Core.java b/jena-arq/src/test/java/org/apache/jena/sparql/core/TS_Core.java
index 800e428..fcdab02 100644
--- a/jena-arq/src/test/java/org/apache/jena/sparql/core/TS_Core.java
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/core/TS_Core.java
@@ -40,8 +40,9 @@ import org.junit.runners.Suite ;
     , TestDatasetGraphBaseFind_Mem.class
     , TestDatasetGraphBaseFindPattern_General.class
     , TestDatasetGraphBaseFindPattern_Mem.class
-    
     , TestSpecialGraphNames.class
+    
+    , TestDatasetGraphFilteredView.class
     , TestSpecials.class
 })
 

http://git-wip-us.apache.org/repos/asf/jena/blob/99b99463/jena-arq/src/test/java/org/apache/jena/sparql/core/TestDatasetGraphFilteredView.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/test/java/org/apache/jena/sparql/core/TestDatasetGraphFilteredView.java b/jena-arq/src/test/java/org/apache/jena/sparql/core/TestDatasetGraphFilteredView.java
new file mode 100644
index 0000000..4ae6e78
--- /dev/null
+++ b/jena-arq/src/test/java/org/apache/jena/sparql/core/TestDatasetGraphFilteredView.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.core;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Set;
+import java.util.function.Predicate;
+
+import org.apache.jena.atlas.iterator.Iter;
+import org.apache.jena.atlas.lib.Creator;
+import org.apache.jena.atlas.lib.StrUtils;
+import org.apache.jena.graph.Node;
+import org.apache.jena.riot.Lang;
+import org.apache.jena.riot.RDFParser;
+import org.apache.jena.sparql.sse.SSE;
+import org.apache.jena.system.Txn;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
+/**
+ * Tests for API access via DatasetGraphFiltered
+ */
+@RunWith(Parameterized.class)
+public class TestDatasetGraphFilteredView {
+    @Parameters(name = "{index}: {0}")
+    public static Iterable<Object[]> data() {
+        Creator<DatasetGraph> c1 = DatasetGraphFactory::create;
+        Object[] obj1 = { "General", c1 };
+        Creator<DatasetGraph> c2 = DatasetGraphFactory::createTxnMem;
+        Object[] obj2 = { "TIM", c2 };
+        return Arrays.asList(obj1, obj2);
+    }
+
+    final DatasetGraph basedsg;
+    
+    private static String dataStr = StrUtils.strjoinNL
+        ("PREFIX : <http://test/>"
+            ,""
+            ,":s0 :p 0 ."
+            ,":g1 { :s1 :p 1 }"
+            ,":g2 { :s2 :p 2 , '02' }"
+            ,":g3 { :s3 :p 3 , '03' , '003' }"
+            ,":g4 { :s4 :p 4 , '04' , '004', '0004' }"
+            );
+
+    public static Node s0 = SSE.parseNode("<http://test/s0>"); 
+    public static Node s1 = SSE.parseNode("<http://test/s1>"); 
+    public static Node s2 = SSE.parseNode("<http://test/s2>"); 
+    public static Node s3 = SSE.parseNode("<http://test/s3>"); 
+    public static Node s4 = SSE.parseNode("<http://test/s4>"); 
+
+    public static Node g1 = SSE.parseNode("<http://test/g1>"); 
+    public static Node g2 = SSE.parseNode("<http://test/g2>"); 
+    public static Node g3 = SSE.parseNode("<http://test/g3>"); 
+    public static Node g4 = SSE.parseNode("<http://test/g4>"); 
+
+    public static void addTestData(DatasetGraph dsg) {
+        Txn.executeWrite(dsg, ()->{
+            RDFParser.create().fromString(dataStr).lang(Lang.TRIG).parse(dsg);
+        });
+    }
+    
+    public TestDatasetGraphFilteredView(String name, Creator<DatasetGraph> source) {
+        basedsg = source.create();
+        addTestData(basedsg);
+    }
+    
+    @Test public void filtered1() {
+        Predicate<Quad> filter = x->true;
+        Txn.executeRead(basedsg, ()->{
+            DatasetGraph dsg = new DatasetGraphFilteredView(basedsg, filter, Iter.toList(basedsg.listGraphNodes()));
+            assertSame(basedsg, dsg);
+        });
+    }
+
+    @Test public void filtered2() {
+        Predicate<Quad> filter = x->x.getGraph().equals(g2);
+        Txn.executeRead(basedsg, ()->{
+            DatasetGraph dsg = new DatasetGraphFilteredView(basedsg, filter, Collections.singleton(g1));
+            long x0 = Iter.count(dsg.find(null, null, null, null));
+            assertEquals(2,x0);
+            long x1 = Iter.count(dsg.find(g2, null, null, null));
+            assertEquals(2,x1);
+            long x2 = Iter.count(dsg.find(null, s2, null, null));
+            assertEquals(2,x2);
+            long x3 = Iter.count(dsg.find(g1, null, null, null));
+            assertEquals(0,x3);
+            assertEquals(1, dsg.size());
+        });
+    }
+
+    @Test public void filtered3() {
+        Predicate<Quad> filter = x->x.getSubject().equals(s2);
+        Txn.executeRead(basedsg, ()->{
+            DatasetGraph dsg = new DatasetGraphFilteredView(basedsg, filter, Collections.singleton(g1));
+            long x0 = Iter.count(dsg.find(null, null, null, null));
+            assertEquals(2,x0);
+            long x1 = Iter.count(dsg.find(g2, null, null, null));
+            assertEquals(2,x1);
+            long x2 = Iter.count(dsg.find(null, s2, null, null));
+            assertEquals(2,x2);
+            long x3 = Iter.count(dsg.find(g1, s2, null, null));
+            assertEquals(0,x3);
+            assertEquals(1, dsg.size());
+        });
+    }
+    
+    @Test public void filtered4() {
+        Predicate<Quad> filter = x->x.getSubject().equals(s2);
+        Txn.executeRead(basedsg, ()->{
+            DatasetGraph dsg = new DatasetGraphFilteredView(basedsg, filter, Arrays.asList(g1, g2));
+            long x0 = Iter.count(dsg.find(null, null, null, null));
+            assertEquals(2,x0);
+            long x1 = Iter.count(dsg.find(g2, null, null, null));
+            assertEquals(2,x1);
+            long x2 = Iter.count(dsg.find(null, s2, null, null));
+            assertEquals(2,x2);
+            long x3 = Iter.count(dsg.find(g1, s2, null, null));
+            assertEquals(0,x3);
+            assertEquals(2, dsg.size());
+        });
+    }
+    
+    @Test public void filtered5() {
+        Predicate<Quad> filter = x-> x.getSubject().equals(s2) || x.getSubject().equals(s1);  
+        Txn.executeRead(basedsg, ()->{
+            DatasetGraph dsg = new DatasetGraphFilteredView(basedsg, filter, Arrays.asList(g1, g2));
+            long x0 = Iter.count(dsg.find(null, null, null, null));
+            assertEquals(3,x0);
+            long x1 = Iter.count(dsg.find(g2, null, null, null));
+            assertEquals(2,x1);
+        });
+    }
+
+    private void assertSame(DatasetGraph dsg1, DatasetGraph dsg2) {
+        Set<Quad> quads1 = Iter.toSet(dsg1.find());
+        Set<Quad> quads2 = Iter.toSet(dsg2.find());
+        assertEquals(quads1, quads2);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/99b99463/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessCtl.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessCtl.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessCtl.java
index 85b1f31..dc8c913 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessCtl.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessCtl.java
@@ -32,6 +32,7 @@ import org.apache.jena.query.Dataset;
 import org.apache.jena.query.DatasetFactory;
 import org.apache.jena.riot.WebContent;
 import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.core.DatasetGraphFilteredView;
 import org.apache.jena.sparql.util.Context;
 import org.apache.jena.sparql.util.Symbol;
 import org.eclipse.jetty.security.SecurityHandler;
@@ -54,47 +55,24 @@ public class DataAccessCtl {
     /** Get the user from the servlet context via {@link HttpServletRequest#getRemoteUser} */ 
     public static final Function<HttpAction, String> requestUserServlet = (action)->action.request.getRemoteUser();
 
-//    /** Build a fuseki server with controlled access enabled. */
-//    public static FusekiServer controlledFuseki(int port, Function<HttpAction, String> determineUser, Consumer<FusekiServer.Builder> configure) {
-//        FusekiServer.Builder b = controlledFuseki(port, determineUser);
-//        configure.accept(b);
-//        return b.build();
-//    }
-//    
-//    /** Builder for a read-only Fuseki server with controlled access actions enabled. */ 
-//    public static FusekiServer.Builder controlledFuseki(int port, Function<HttpAction, String> determineUser) {
-//        ServiceDispatchRegistry sdr = new ServiceDispatchRegistry(false);
-//        ActionService actionQuery   = new Filtered_SPARQL_QueryDataset(determineUser);
-//        ActionService actionGspR    = new Filtered_SPARQL_GSP_R(determineUser);
-//        ActionService actionQuadsR  = new Filtered_REST_Quads_R(determineUser);
-//        
-//        // Create empty and only add "read" operations.
-//        FusekiServer.Builder builder = FusekiServer.create(sdr)
-//            .port(port)
-//            .registerOperation(Operation.Query,     WebContent.contentTypeSPARQLQuery, actionQuery)
-//            .registerOperation(Operation.GSP_R,     actionGspR)
-//            .registerOperation(Operation.Quads_R,   actionQuadsR);
-//        return builder;
-//    }
-
     /**
-     * Enable data access control on a {@link DatasetGraph}. This modifies the
+     * Add data access control information on a {@link DatasetGraph}. This modifies the
      * {@link DatasetGraph}'s {@link Context}.
      */
-    public static void controlledDataset(DatasetGraph dsg, SecurityRegistry reg) {
+    private static void controlledDataset(DatasetGraph dsg, SecurityRegistry reg) {
         // Or wrapper.
         dsg.getContext().set(symControlledAccess, true);
         dsg.getContext().set(symSecurityRegistry, reg);
     }
 
-    /**
-     * Enable data access control on a {@link Dataset}. This modifies the
-     * {@link Dataset}'s {@link Context}.
-     */
-    public static void controlledDataset(Dataset ds, SecurityRegistry reg) {
-        ds.getContext().set(symControlledAccess, true);
-        ds.getContext().set(symSecurityRegistry, reg);
-    }
+//    /**
+//     * Enable data access control on a {@link Dataset}. This modifies the
+//     * {@link Dataset}'s {@link Context}.
+//     */
+//    private static void controlledDataset(Dataset ds, SecurityRegistry reg) {
+//        ds.getContext().set(symControlledAccess, true);
+//        ds.getContext().set(symSecurityRegistry, reg);
+//    }
 
     /**
      * Return a {@link DatasetGraph} with added data access control. Use of the original
@@ -149,7 +127,7 @@ public class DataAccessCtl {
      * query/GSP/Quads go to the data-filtering versions of the {@link ActionService ActionServices}.
      * (It is better to create the server via {@link #DataAccessCtl.builder} first rather than modify afterwards.) 
      */
-    public static void enable(FusekiServer server, Function<HttpAction, String> determineUser) {
+    public static void modifyForAccessCtl(FusekiServer server, Function<HttpAction, String> determineUser) {
         /* 
          * Reconfigure standard Jena Fuseki, replacing the default implementation of "query"
          * with a filtering one.  This for this server only. 
@@ -176,4 +154,11 @@ public class DataAccessCtl {
             return true;
         return false;
     }
+
+    /**
+     * Return a read-only {@link DatasetGraphFilteredView} that fulfils the {@link SecurityPolicy}.
+     */
+    public static DatasetGraphFilteredView filteredDataset(DatasetGraph dsg, SecurityPolicy sCxt) {
+        return new DatasetGraphFilteredView(dsg, sCxt.predicateQuad(), sCxt.visibleGraphs());
+    }
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/99b99463/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessLib.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessLib.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessLib.java
index 4329cc6..127aa92 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessLib.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessLib.java
@@ -40,7 +40,6 @@ class DataAccessLib {
             sCxt = noSecurityPolicy();
         return sCxt;
     }
-
     
     /** Get the {@link SecurityRegistry} for an action/query/dataset */
     static SecurityRegistry getSecurityRegistry(HttpAction action, DatasetGraph dsg) {
@@ -54,5 +53,18 @@ class DataAccessLib {
         // Should not get here.
         throw new InternalError();
     }
+    
+    static DatasetGraph decideDataset(HttpAction action, Function<HttpAction, String> requestUser) {
+        DatasetGraph dsg = action.getDataset();
+        if ( dsg == null )
+            return dsg;//super.actOn(action);
+        if ( ! DataAccessCtl.isAccessControlled(dsg) )
+            // Not access controlled.
+            return dsg;//super.actOn(action);
+        SecurityPolicy sCxt = DataAccessLib.getSecurityPolicy(action, dsg, requestUser);
+        dsg = DatasetGraphAccessControl.removeWrapper(dsg);
+        dsg = DataAccessCtl.filteredDataset(dsg, sCxt);
+        return dsg;
+    }
 }
 

http://git-wip-us.apache.org/repos/asf/jena/blob/99b99463/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DatasetGraphAccessControl.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DatasetGraphAccessControl.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DatasetGraphAccessControl.java
index d770ade..f58bdcd 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DatasetGraphAccessControl.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DatasetGraphAccessControl.java
@@ -41,9 +41,19 @@ class DatasetGraphAccessControl extends DatasetGraphWrapper {
      * Return the underlying {@code DatasetGraph}. If the argument is not a
      * {@code DatasetGraphAccessControl}, return the argument.
      */
-    public static DatasetGraph unwrap(DatasetGraph dsg) {
+    public static DatasetGraph removeWrapper(DatasetGraph dsg) {
         if ( ! ( dsg instanceof DatasetGraphAccessControl ) )
             return dsg;
         return ((DatasetGraphAccessControl)dsg).getWrapped();
     }
+    
+    /**
+     * Return the underlying {@code DatasetGraph}. If the argument is not a
+     * {@code DatasetGraphAccessControl}, return null.
+     */
+    public static DatasetGraph unwrapOrNull(DatasetGraph dsg) {
+        if ( ! ( dsg instanceof DatasetGraphAccessControl ) )
+            return null;
+        return ((DatasetGraphAccessControl)dsg).getWrapped();
+    }
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/99b99463/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DatasetGraphFiltered.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DatasetGraphFiltered.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DatasetGraphFiltered.java
deleted file mode 100644
index a7842ba..0000000
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DatasetGraphFiltered.java
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.jena.fuseki.access;
-
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.function.Predicate;
-
-import org.apache.jena.atlas.iterator.Iter;
-import org.apache.jena.graph.Graph;
-import org.apache.jena.graph.Node;
-import org.apache.jena.sparql.core.DatasetGraph;
-import org.apache.jena.sparql.core.DatasetGraphReadOnly;
-import org.apache.jena.sparql.core.GraphView;
-import org.apache.jena.sparql.core.Quad;
-import org.apache.jena.sparql.graph.GraphReadOnly;
-import org.apache.jena.sparql.graph.GraphUnionRead;
-
-/**
- * A read-only {@link DatasetGraph} that applies a filter testing all triples and quads
- * returned by accessing the data. Only quads where the filter tests for "true" are exposed. 
- * 
- */
-public class DatasetGraphFiltered extends DatasetGraphReadOnly {
-    
-    // Or DatasetGraphTriplesQuads
-    
-    // Core operations at the triple/quad level.
-    // This would work for TDB where the root access is triples/quads
-    // but not for graph-style root access. 
-    /*
-  Write operations
-    add(Quad)
-    delete(Quad)
-    add(Node, Node, Node, Node)
-    delete(Node, Node, Node, Node)
-    deleteAny(Node, Node, Node, Node)
-    clear()
-    
-  Read operations  
-    listGraphNodes()
-    isEmpty()
-    find()
-    find(Quad)
-    find(Node, Node, Node, Node)
-    findNG(Node, Node, Node, Node)
-    contains(Quad)
-    contains(Node, Node, Node, Node)
-    size()
-    toString()
-     */
-    /*
-    getGraph(Node)
-    getDefaultGraph()
-    containsGraph(Node)
-     */
-
-    private final Predicate<Quad> quadFilter;
-    private final Collection<Node> visibleGraphs;
-
-    public DatasetGraphFiltered(DatasetGraph dsg, Predicate<Quad> filter, Collection<Node> visibleGraphs) {
-        super(dsg);
-        this.quadFilter = filter;
-        this.visibleGraphs = visibleGraphs;
-    }
-    
-    private boolean filter(Quad quad) {
-        return quadFilter.test(quad);
-    }
-
-    private Iterator<Quad> filter(Iterator<Quad> iter) {
-        return Iter.filter(iter, this::filter);
-    }
-    
-    // Need to intercept these because otherwise that are a GraphView of the wrapped "dsg", not this one.  
-
-    @Override
-    public Graph getDefaultGraph()
-    {
-        Graph g = GraphView.createDefaultGraph(this);
-        return new GraphReadOnly(g);
-    }
-    
-    @Override
-    public Graph getGraph(Node graphNode) {
-        if ( Quad.isUnionGraph(graphNode)) 
-            return getUnionGraph(); 
-        Graph g = GraphView.createNamedGraph(this, graphNode);
-        return new GraphReadOnly(g);
-   }
-
-    @Override
-    public Graph getUnionGraph() {
-        return new GraphUnionRead(get(), visibleGraphs);
-   }
-
-    @Override
-    public Iterator<Quad> find() {
-        return filter(super.find());
-    }
-
-    @Override public Iterator<Quad> find(Node g, Node s, Node p, Node o) {
-        // Need union handling if for general API use.
-        return filter(super.find(g, s, p, o));
-    }
-    
-    @Override public Iterator<Quad> find(Quad quad) {
-        // union
-        return filter(super.find(quad));
-    }
-    
-    @Override public Iterator<Quad> findNG(Node g, Node s, Node p , Node o) {
-        // union
-        return filter(super.findNG(g, s, p, o));
-    }
-
-    @Override public boolean contains(Node g, Node s, Node p , Node o) {
-        return filter(super.find(g, s, p, o)).hasNext();
-    }
-
-    @Override public boolean contains(Quad quad) {
-        return filter(super.find(quad)).hasNext();
-    }
-    
-    @Override public boolean isEmpty() {
-        return ! this.find().hasNext(); 
-    }
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/99b99463/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_REST_Quads_R.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_REST_Quads_R.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_REST_Quads_R.java
index 9a7fdd0..f5e7a78 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_REST_Quads_R.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_REST_Quads_R.java
@@ -19,12 +19,10 @@
 package org.apache.jena.fuseki.access;
 
 import java.util.function.Function;
-import java.util.function.Predicate;
 
 import org.apache.jena.fuseki.servlets.HttpAction;
 import org.apache.jena.fuseki.servlets.REST_Quads_R;
 import org.apache.jena.sparql.core.DatasetGraph;
-import org.apache.jena.sparql.core.Quad;
 
 /**
  * Filter for {@link REST_Quads_R} that inserts a security filter on read-access to the
@@ -39,23 +37,7 @@ public class Filtered_REST_Quads_R extends REST_Quads_R {
     }
 
     @Override
-    protected void validate(HttpAction action) {
-        super.validate(action);
-    }
-
-    // Where? REST_Quads_R < REST_Quads < ActionREST < ActionService
-    @Override
-    protected DatasetGraph actOn(HttpAction action) {
-        DatasetGraph dsg = action.getDataset();
-        if ( dsg == null )
-            return dsg;//super.actOn(action);
-        if ( ! DataAccessCtl.isAccessControlled(dsg) )
-            // Not access controlled.
-            return dsg;//super.actOn(action);
-        SecurityPolicy sCxt = DataAccessLib.getSecurityPolicy(action, dsg, requestUser);
-        dsg = DatasetGraphAccessControl.unwrap(dsg);
-        Predicate<Quad> filter = sCxt.predicateQuad();
-        dsg = new DatasetGraphFiltered(dsg, filter, sCxt.visibleGraphs());
-        return dsg;
+    protected DatasetGraph decideDataset(HttpAction action) {
+        return DataAccessLib.decideDataset(action, requestUser);
     }
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/99b99463/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_GSP_R.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_GSP_R.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_GSP_R.java
index d2803cc..5634c7c 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_GSP_R.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_GSP_R.java
@@ -19,12 +19,10 @@
 package org.apache.jena.fuseki.access;
 
 import java.util.function.Function;
-import java.util.function.Predicate;
 
 import org.apache.jena.fuseki.servlets.HttpAction;
 import org.apache.jena.fuseki.servlets.SPARQL_GSP_R;
 import org.apache.jena.sparql.core.DatasetGraph;
-import org.apache.jena.sparql.core.Quad;
 
 public class Filtered_SPARQL_GSP_R extends SPARQL_GSP_R {
     
@@ -34,19 +32,8 @@ public class Filtered_SPARQL_GSP_R extends SPARQL_GSP_R {
         this.requestUser = determineUser;
     }
 
-    // Where? SPARQL_GSP_R < SPARQL_GSP < ActionREST < ActionService
     @Override
-    protected DatasetGraph actOn(HttpAction action) {
-        DatasetGraph dsg = action.getDataset();
-        if ( dsg == null )
-            return dsg;//super.actOn(action);
-        if ( ! DataAccessCtl.isAccessControlled(dsg) )
-            // Not access controlled.
-            return dsg;//super.actOn(action);
-        SecurityPolicy sCxt = DataAccessLib.getSecurityPolicy(action, dsg, requestUser);
-        dsg = DatasetGraphAccessControl.unwrap(dsg);
-        Predicate<Quad> filter = sCxt.predicateQuad();
-        dsg = new DatasetGraphFiltered(dsg, filter, sCxt.visibleGraphs());
-        return dsg;
+    protected DatasetGraph decideDataset(HttpAction action) {
+        return DataAccessLib.decideDataset(action, requestUser);
     }
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/99b99463/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_QueryDataset.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_QueryDataset.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_QueryDataset.java
index 596bef1..49efc4f 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_QueryDataset.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_QueryDataset.java
@@ -24,7 +24,6 @@ import org.apache.jena.fuseki.servlets.ActionService;
 import org.apache.jena.fuseki.servlets.HttpAction;
 import org.apache.jena.fuseki.servlets.SPARQL_QueryDataset;
 import org.apache.jena.query.Dataset;
-import org.apache.jena.query.DatasetFactory;
 import org.apache.jena.query.Query;
 import org.apache.jena.query.QueryExecution;
 import org.apache.jena.sparql.core.DatasetGraph;
@@ -41,25 +40,26 @@ public class Filtered_SPARQL_QueryDataset extends SPARQL_QueryDataset {
     @Override
     protected QueryExecution createQueryExecution(HttpAction action, Query query, Dataset dataset) {
         // Server database, not the possibly dynamically built "dataset"
-        // ---- XXX DRY
         DatasetGraph dsg = action.getDataset();
         if ( dsg == null )
             return super.createQueryExecution(action, query, dataset);
         if ( ! DataAccessCtl.isAccessControlled(dsg) )
             return super.createQueryExecution(action, query, dataset);
-        
+
+        // XXX Generalize to any DSG.
         SecurityPolicy sCxt = DataAccessLib.getSecurityPolicy(action, dataset.asDatasetGraph(), requestUser);
-        if ( dsg instanceof DatasetGraphAccessControl ) {
-            // Take off one layer.
-            dsg = DatasetGraphAccessControl.unwrap(dsg);
-            // Add back the Dataset for the createQueryExecution call.
-            dataset = DatasetFactory.wrap(dsg);
-        }
-        // ----
         
-        QueryExecution qExec = super.createQueryExecution(action, query, dataset);
-        if ( sCxt != null )
-            sCxt.filterTDB(dsg, qExec);
+        QueryExecution qExec = sCxt.createQueryExecution(query, dsg);
+        
+//        if ( dsg instanceof DatasetGraphAccessControl ) {
+//            // Take off one layer.
+//            dsg = DatasetGraphAccessControl.removeWrapper(dsg);
+//            // Add back the Dataset for the createQueryExecution call.
+//            dataset = DatasetFactory.wrap(dsg);
+//        }
+//        QueryExecution qExec = super.createQueryExecution(action, query, dataset);
+//        if ( sCxt != null )
+//            sCxt.filterTDB(dsg, qExec);
         return qExec;
     }
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/99b99463/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityPolicy.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityPolicy.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityPolicy.java
index 8d59911..c0d420a 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityPolicy.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityPolicy.java
@@ -25,7 +25,10 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Predicate;
 
 import org.apache.jena.graph.Node;
+import org.apache.jena.query.Query;
 import org.apache.jena.query.QueryExecution;
+import org.apache.jena.query.QueryExecutionFactory;
+import org.apache.jena.query.QueryFactory;
 import org.apache.jena.sparql.core.DatasetGraph;
 import org.apache.jena.sparql.core.Quad;
 import org.apache.jena.sparql.util.Context;
@@ -61,9 +64,13 @@ public class SecurityPolicy {
         this(Arrays.asList(graphNames));
     }
 
-    public SecurityPolicy(Collection<Node> graphNames) {
-        this.graphNames.addAll(graphNames);
-        this.matchDefaultGraph = graphNames.stream().anyMatch(Quad::isDefaultGraph);
+    public SecurityPolicy(Collection<Node> visibleGraphs) {
+        this.graphNames.addAll(visibleGraphs);
+        this.matchDefaultGraph = visibleGraphs.stream().anyMatch(Quad::isDefaultGraph);
+        if ( matchDefaultGraph ) {
+            this.graphNames.remove(Quad.defaultGraphIRI);
+            this.graphNames.remove(Quad.defaultGraphNodeGenerated);
+        }
     }
     
     public Collection<Node> visibleGraphs() {
@@ -74,17 +81,29 @@ public class SecurityPolicy {
      * Apply a filter suitable for the TDB-backed {@link DatasetGraph}, to the {@link Context} of the
      * {@link QueryExecution}. This does not modify the {@link DatasetGraph}
      */
-    public void filterTDB(DatasetGraph dsg, QueryExecution qExec) {
+    /*package*/ void filterTDB(DatasetGraph dsg, QueryExecution qExec) {
         GraphFilter<?> predicate = predicate(dsg);
         qExec.getContext().set(predicate.getContextKey(), predicate);
     }
+
+    public QueryExecution createQueryExecution(String queryString, DatasetGraph dsg) {
+        return createQueryExecution(QueryFactory.create(queryString), dsg);
+    }
     
-    /** Modify the {@link Context} of the TDB-backed {@link DatasetGraph}. */
-    public void filterTDB(DatasetGraph dsg) {
-        GraphFilter<?> predicate = predicate(dsg);
-        dsg.getContext().set(predicate.getContextKey(), predicate);
+    public QueryExecution createQueryExecution(Query query, DatasetGraph dsg) {
+        if ( ! ( dsg instanceof DatasetGraphAccessControl ) ) {
+            return QueryExecutionFactory.create(query, dsg);
+        }
+        if ( isAccessControlledTDB(dsg) ) {
+            QueryExecution qExec = QueryExecutionFactory.create(query, dsg);
+            filterTDB(dsg, qExec);
+            return qExec;
+        }
+        
+        DatasetGraph dsgA = DataAccessCtl.filteredDataset(dsg, this);
+        return QueryExecutionFactory.create(query, dsgA); 
     }
-
+    
     @Override
     public String toString() {
         return "dft:"+matchDefaultGraph+" / "+graphNames.toString();
@@ -111,7 +130,7 @@ public class SecurityPolicy {
      *             TDB database.
      */
     public GraphFilter<?> predicate(DatasetGraph dsg) {
-        dsg = DatasetGraphAccessControl.unwrap(dsg);
+        dsg = DatasetGraphAccessControl.removeWrapper(dsg);
         // dsg has to be the database dataset, not wrapped.
         //  DatasetGraphSwitchable is wrapped but should not be unwrapped. 
         if ( TDBFactory.isTDB1(dsg) )
@@ -121,6 +140,17 @@ public class SecurityPolicy {
         throw new IllegalArgumentException("Not a TDB1 or TDB2 database: "+dsg.getClass().getSimpleName());
     }
 
+    public boolean isAccessControlledTDB(DatasetGraph dsg) {
+        DatasetGraph dsgBase = DatasetGraphAccessControl.unwrapOrNull(dsg);
+        if ( dsgBase == null )
+            return false;
+        if ( TDBFactory.isTDB1(dsgBase) )
+            return true;
+        if ( DatabaseMgr.isTDB2(dsgBase) )
+            return true;
+        return false;
+    }
+    
     public GraphFilterTDB2 filterTDB2(DatasetGraph dsg) {
         GraphFilterTDB2 f = GraphFilterTDB2.graphFilter(dsg, graphNames, matchDefaultGraph);
         return f;

http://git-wip-us.apache.org/repos/asf/jena/blob/99b99463/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/AbstractTestSecurityAssembler.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/AbstractTestSecurityAssembler.java b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/AbstractTestSecurityAssembler.java
index c062012..9c62c28 100644
--- a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/AbstractTestSecurityAssembler.java
+++ b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/AbstractTestSecurityAssembler.java
@@ -20,8 +20,8 @@ package org.apache.jena.fuseki.access;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
+import static org.apache.jena.fuseki.access.AccessTestLib.assertSeen;
 
-import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
@@ -323,9 +323,4 @@ public abstract class AbstractTestSecurityAssembler {
         });
         return results;
     }
-
-    private static void assertSeen(Set<Node> visible, Node ... expected) {
-        Set<Node> expectedNodes = new HashSet<>(Arrays.asList(expected));
-        assertEquals(expectedNodes, visible);
-    }
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/99b99463/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/AccessTestLib.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/AccessTestLib.java b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/AccessTestLib.java
new file mode 100644
index 0000000..e95a3e9
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/AccessTestLib.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.fuseki.access;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.jena.atlas.lib.StrUtils;
+import org.apache.jena.graph.Node;
+import org.apache.jena.riot.Lang;
+import org.apache.jena.riot.RDFParser;
+import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.sse.SSE;
+import org.apache.jena.system.Txn;
+
+
+/** Some data and functions common to access control tests. */ 
+public class AccessTestLib {
+    private static String dataStr = StrUtils.strjoinNL 
+        ("PREFIX : <http://test/>"
+            ,""
+            ,":s0 :p 0 ."
+            ,":g1 { :s1 :p 1 }"
+            ,":g2 { :s2 :p 2 }"
+            ,":g3 { :s3 :p 3 }"
+            ,":g4 { :s4 :p 4 }"
+            );
+
+
+    public static Node s0 = SSE.parseNode("<http://test/s0>"); 
+    public static Node s1 = SSE.parseNode("<http://test/s1>"); 
+    public static Node s2 = SSE.parseNode("<http://test/s2>"); 
+    public static Node s3 = SSE.parseNode("<http://test/s3>"); 
+    public static Node s4 = SSE.parseNode("<http://test/s4>"); 
+
+    public static Node g1 = SSE.parseNode("<http://test/g1>"); 
+    public static Node g2 = SSE.parseNode("<http://test/g2>"); 
+    public static Node g3 = SSE.parseNode("<http://test/g3>"); 
+    public static Node g4 = SSE.parseNode("<http://test/g4>"); 
+
+    public static void addTestData(DatasetGraph dsg) {
+        Txn.executeWrite(dsg, ()->{
+            RDFParser.create().fromString(dataStr).lang(Lang.TRIG).parse(dsg);
+        });
+    }
+    
+    public static void assertSeen(Set<Node> visible, Node ... expected) {
+        Set<Node> expectedNodes = new HashSet<>(Arrays.asList(expected));
+        assertEquals(expectedNodes, visible);
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/99b99463/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/GraphData.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/GraphData.java b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/GraphData.java
deleted file mode 100644
index 9712b71..0000000
--- a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/GraphData.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.jena.fuseki.access;
-
-import org.apache.jena.atlas.lib.StrUtils;
-import org.apache.jena.graph.Node;
-import org.apache.jena.riot.Lang;
-import org.apache.jena.riot.RDFParser;
-import org.apache.jena.sparql.core.DatasetGraph;
-import org.apache.jena.sparql.sse.SSE;
-import org.apache.jena.system.Txn;
-
-/** Build graph data for the filter/security tests. */
-
-class GraphData {
-    private static String dataStr = StrUtils.strjoinNL 
-        ("PREFIX : <http://test/>"
-        ,""
-        ,":s0 :p 0 ."
-        ,":g1 { :s1 :p 1 }"
-        ,":g2 { :s2 :p 2 }"
-        ,":g3 { :s3 :p 3 }"
-        ,":g4 { :s4 :p 4 }"
-        );
-    
-    
-    static Node s0 = SSE.parseNode("<http://test/s0>"); 
-    static Node s1 = SSE.parseNode("<http://test/s1>"); 
-    static Node s2 = SSE.parseNode("<http://test/s2>"); 
-    static Node s3 = SSE.parseNode("<http://test/s3>"); 
-    static Node s4 = SSE.parseNode("<http://test/s4>"); 
- 
-    static Node g1 = SSE.parseNode("<http://test/g1>"); 
-    static Node g2 = SSE.parseNode("<http://test/g2>"); 
-    static Node g3 = SSE.parseNode("<http://test/g3>"); 
-    static Node g4 = SSE.parseNode("<http://test/g4>"); 
-
-    public static void fill(DatasetGraph dsg) {
-         Txn.executeWrite(dsg, ()->{
-             RDFParser.create().fromString(dataStr).lang(Lang.TRIG).parse(dsg);
-         });
-    }
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/99b99463/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterFuseki.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterFuseki.java b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterFuseki.java
index c44540c..7050ef0 100644
--- a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterFuseki.java
+++ b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterFuseki.java
@@ -18,8 +18,7 @@
 
 package org.apache.jena.fuseki.access;
 
-import static org.apache.jena.fuseki.access.GraphData.s0;
-import static org.apache.jena.fuseki.access.GraphData.s1;
+import static org.apache.jena.fuseki.access.AccessTestLib.*;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
@@ -69,15 +68,15 @@ public class TestSecurityFilterFuseki {
     }
 
     private final String baseUrl;
-    private static final DatasetGraph testdsg1 =  TDBFactory.createDatasetGraph();
-    private static final DatasetGraph testdsg2 =  DatabaseMgr.createDatasetGraph();
+    private static DatasetGraph testdsg1 =  TDBFactory.createDatasetGraph();
+    private static DatasetGraph testdsg2 =  DatabaseMgr.createDatasetGraph();
     private static FusekiServer fusekiServer;
 
     // Set up Fuseki with two datasets, "data1" backed by TDB and "data2" backed by TDB2.
     @BeforeClass public static void beforeClass() {
         int port = FusekiLib.choosePort();
-        GraphData.fill(testdsg1);
-        GraphData.fill(testdsg2);
+        addTestData(testdsg1);
+        addTestData(testdsg2);
         
         SecurityRegistry reg = new SecurityRegistry();
         reg.put("userNone", SecurityPolicy.NONE);
@@ -86,9 +85,9 @@ public class TestSecurityFilterFuseki {
         reg.put("user1", new SecurityPolicy("http://test/g1", Quad.defaultGraphIRI.getURI()));
         reg.put("user2", new SecurityPolicy("http://test/g1", "http://test/g2", "http://test/g3"));
         
-        // XXXX Also need wrapped tests
-        DataAccessCtl.controlledDataset(testdsg1, reg);
-        DataAccessCtl.controlledDataset(testdsg2, reg);
+        // XXX Also need wrapped tests
+        testdsg1 = DataAccessCtl.wrapControlledDataset(testdsg1, reg);
+        testdsg2 = DataAccessCtl.wrapControlledDataset(testdsg2, reg);
 
         UserStore userStore = userStore();
         SecurityHandler sh = JettyLib.makeSecurityHandler("/*", "DatasetRealm", userStore);
@@ -126,11 +125,6 @@ public class TestSecurityFilterFuseki {
         baseUrl = "http://localhost:"+port+"/"+dsName;
     }
 
-    private static void assertSeen(Set<Node> visible, Node ... expected) {
-        Set<Node> expectedNodes = new HashSet<>(Arrays.asList(expected));
-        assertEquals(expectedNodes, visible);
-    }
-
     private static String queryAll        = "SELECT * { { ?s ?p ?o } UNION { GRAPH ?g { ?s ?p ?o } } }";
     private static String queryDft        = "SELECT * { ?s ?p ?o }";
     private static String queryNamed      = "SELECT * { GRAPH ?g { ?s ?p ?o } }";

http://git-wip-us.apache.org/repos/asf/jena/blob/99b99463/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterLocal.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterLocal.java b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterLocal.java
index 3c4ead4..0268b5b 100644
--- a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterLocal.java
+++ b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterLocal.java
@@ -18,24 +18,22 @@
 
 package org.apache.jena.fuseki.access;
 
-import static org.apache.jena.fuseki.access.GraphData.*;
+import static org.apache.jena.fuseki.access.AccessTestLib.*;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
-import java.util.function.Consumer;
+import java.util.function.Function;
 import java.util.stream.Stream;
 
 import org.apache.jena.atlas.iterator.Iter;
 import org.apache.jena.atlas.lib.Creator;
 import org.apache.jena.atlas.lib.SetUtils;
-import org.apache.jena.fuseki.access.DataAccessCtl;
-import org.apache.jena.fuseki.access.SecurityPolicy;
-import org.apache.jena.fuseki.access.SecurityRegistry;
 import org.apache.jena.graph.Graph;
 import org.apache.jena.graph.Node;
 import org.apache.jena.query.Dataset;
@@ -47,41 +45,60 @@ import org.apache.jena.rdf.model.Model;
 import org.apache.jena.rdf.model.ModelFactory;
 import org.apache.jena.rdf.model.RDFNode;
 import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.core.DatasetGraphFactory;
 import org.apache.jena.sparql.core.Quad;
 import org.apache.jena.system.Txn;
-import org.apache.jena.tdb.TDB;
 import org.apache.jena.tdb.TDBFactory;
 import org.apache.jena.tdb2.DatabaseMgr;
-import org.apache.jena.tdb2.TDB2;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 import org.junit.runners.Parameterized.Parameters;
 
+/** Test a controlled Dataset with access by TDB filter or general DatasetGraphFiltered. */
 @RunWith(Parameterized.class)
 public class TestSecurityFilterLocal {
     @Parameters(name = "{index}: {0}")
     public static Iterable<Object[]> data() {
+        // By filtering on the TDB database
         Creator<DatasetGraph> c1 = TDBFactory::createDatasetGraph;
-        Object[] obj1 = { "TDB", c1};
         Creator<DatasetGraph> c2 = DatabaseMgr::createDatasetGraph;
-        Object[] obj2 = { "TDB2", c2 };
-        return Arrays.asList(obj1, obj2);
+        Creator<DatasetGraph> c3 = DatasetGraphFactory::createTxnMem;
+        Creator<DatasetGraph> c4 = DatasetGraphFactory::create;
+
+        Object[] obj1 = { "TDB/db", c1, true};
+        Object[] obj2 = { "TDB2/db", c2, true };
+        
+        // By adding the general, but slower, DatasetGraphFilter
+        Object[] obj3 = { "TDB/filtered", c1, false };
+        Object[] obj4 = { "TDB2/filtered", c2, false };
+        Object[] obj5 = { "TIM/filtered", c3, false };
+        Object[] obj6 = { "Plain/filtered", c4, false };
+        
+        List<Object[]> x = new ArrayList<>();
+        return Arrays.asList(obj1, obj2, obj3, obj4, obj5, obj6);
+//        x.add(obj1);
+//        x.add(obj3);
+//        x.add(obj5);
+//        return x;
     }
     
-    private DatasetGraph testdsg;
+    private final DatasetGraph testdsg;
     private SecurityRegistry reg = new SecurityRegistry();
+    private final boolean applyFilterDSG;
+    private final boolean applyFilterQExec;
     
-    public TestSecurityFilterLocal(String name, Creator<DatasetGraph> source) {
-        testdsg = source.create();
-        fill(testdsg);
+    public TestSecurityFilterLocal(String name, Creator<DatasetGraph> source, boolean applyFilterTDB) {
+        DatasetGraph dsgBase = source.create();
+        AccessTestLib.addTestData(dsgBase);
         reg.put("userNone", SecurityPolicy.NONE);
         reg.put("userDft", SecurityPolicy.DFT_GRAPH);
         reg.put("user0", new SecurityPolicy(Quad.defaultGraphIRI.getURI()));
         reg.put("user1", new SecurityPolicy("http://test/g1", Quad.defaultGraphIRI.getURI()));
         reg.put("user2", new SecurityPolicy("http://test/g1", "http://test/g2", "http://test/g3"));
-        DataAccessCtl.controlledDataset(testdsg, reg);
-        //testdsg = DataAccessCtl.wrapControlledDataset(testdsg, reg);
+        testdsg = DataAccessCtl.wrapControlledDataset(dsgBase, reg);
+        this.applyFilterDSG = ! applyFilterTDB;
+        this.applyFilterQExec = applyFilterTDB;
     }
     
     private static void assertSeen(Set<Node> visible, Node ... expected) {
@@ -96,13 +113,16 @@ public class TestSecurityFilterLocal {
     private static String queryG2         = "SELECT * { GRAPH <http://test/graph2> { ?s ?p ?o } }";
     private static String queryGraphNames = "SELECT * { GRAPH ?g { } }";
 
-    private Set<Node> subjects(DatasetGraph dsg, String queryString, Consumer<QueryExecution> modifier) {
-        Dataset ds = DatasetFactory.wrap(dsg);
+    private Set<Node> subjects(DatasetGraph dsg, String queryString, SecurityPolicy sCxt) {
+        final DatasetGraph dsg1 = applyFilterDSG
+            ? DataAccessCtl.filteredDataset(dsg, sCxt)
+            : dsg;
+        Dataset ds = DatasetFactory.wrap(dsg1);
         return
             Txn.calculateRead(ds, ()->{
                 try(QueryExecution qExec = QueryExecutionFactory.create(queryString, ds)) {
-                    if ( modifier != null )
-                        modifier.accept(qExec);
+                    if ( applyFilterQExec )
+                        sCxt.filterTDB(dsg1, qExec);
                     List<QuerySolution> results = Iter.toList(qExec.execSelect());
                     Stream<Node> stream = results.stream()
                         .map(qs->qs.get("s"))
@@ -113,13 +133,34 @@ public class TestSecurityFilterLocal {
             });
     }
     
-    private Set<Node> graphs(DatasetGraph dsg, Consumer<QueryExecution> modifier) {
-        Dataset ds = DatasetFactory.wrap(dsg);
+    private Set<Node> subjects(DatasetGraph dsg,  Function<DatasetGraph, Graph> graphChoice, String queryString, SecurityPolicy sCxt) {
+        final DatasetGraph dsg1 = applyFilterDSG
+            ? DataAccessCtl.filteredDataset(dsg, sCxt)
+            : dsg;
+        Graph graph = graphChoice.apply(dsg1);
+        Model model = ModelFactory.createModelForGraph(graph);
+        return
+            Txn.calculateRead(testdsg, ()->{
+                try(QueryExecution qExec = QueryExecutionFactory.create(queryString, model)) {
+                    if ( applyFilterQExec )
+                        sCxt.filterTDB(dsg1, qExec);
+                    List<QuerySolution> results = Iter.toList(qExec.execSelect());
+                    Stream<Node> stream = results.stream().map(qs->qs.get("s")).filter(Objects::nonNull).map(RDFNode::asNode);
+                    return SetUtils.toSet(stream);
+                }
+            });
+    }
+
+    private Set<Node> graphs(DatasetGraph dsg, SecurityPolicy sCxt) {
+        final DatasetGraph dsg1 = applyFilterDSG
+            ? DataAccessCtl.filteredDataset(dsg, sCxt)
+            : dsg;
+        Dataset ds = DatasetFactory.wrap(dsg1);
         return
             Txn.calculateRead(ds, ()->{
                 try(QueryExecution qExec = QueryExecutionFactory.create(queryGraphNames, ds)) {
-                    if ( modifier != null )
-                        modifier.accept(qExec);
+                    if ( applyFilterQExec )
+                        sCxt.filterTDB(dsg1, qExec);
                     List<QuerySolution> results = Iter.toList(qExec.execSelect());
                     Stream<Node> stream = results.stream().map(qs->qs.get("g")).filter(Objects::nonNull).map(RDFNode::asNode);
                     return SetUtils.toSet(stream);
@@ -128,19 +169,15 @@ public class TestSecurityFilterLocal {
     }
 
     @Test public void filter_setup() {
-        Set<Node> visible = subjects(testdsg, queryAll, null);
-        assertEquals(5, visible.size());
-        assertSeen(visible, s0, s1, s2, s3, s4);
-    }
-
-    private static Consumer<QueryExecution> qExecAddFiler(DatasetGraph dsg, SecurityPolicy sCxt) {
-        return qExec->sCxt.filterTDB(dsg, qExec);
+        Set<Node> visible = subjects(testdsg, queryAll, SecurityPolicy.NONE);
+        assertEquals(0, visible.size());
+        assertSeen(visible);
     }
 
     // QueryExecution
     private void filter_user(String user, Node ... expected) {
         SecurityPolicy sCxt = reg.get(user);
-        Set<Node> visible = subjects(testdsg, queryAll, qExecAddFiler(testdsg, sCxt));
+        Set<Node> visible = subjects(testdsg, queryAll, sCxt);
         assertSeen(visible, expected);
     }
 
@@ -171,55 +208,58 @@ public class TestSecurityFilterLocal {
     // "Access Denied"
     @Test public void no_access_user1() {
         SecurityPolicy sCxt = reg.get("user1");
-        Set<Node> visible = subjects(testdsg, queryG2, qExecAddFiler(testdsg, sCxt));
+        Set<Node> visible = subjects(testdsg, queryG2, sCxt);
         assertTrue(visible.isEmpty());
     }
 
     @Test public void graph_names_userNone() {
         SecurityPolicy sCxt = reg.get("userNone");
-        Set<Node> visible = graphs(testdsg, qExecAddFiler(testdsg, sCxt)); 
+        Set<Node> visible = graphs(testdsg, sCxt); 
         assertSeen(visible);
     }
     
     @Test public void graph_names_userDft() {
         SecurityPolicy sCxt = reg.get("userDft");
-        Set<Node> visible = graphs(testdsg, qExecAddFiler(testdsg, sCxt)); 
+        Set<Node> visible = graphs(testdsg, sCxt); 
         assertSeen(visible);
     }
     
     @Test public void graph_names_user0() {
         SecurityPolicy sCxt = reg.get("user0");
-        Set<Node> visible = graphs(testdsg, qExecAddFiler(testdsg, sCxt)); 
+        Set<Node> visible = graphs(testdsg, sCxt); 
         assertSeen(visible);
     }
     
     @Test public void graph_names_user1() {
         SecurityPolicy sCxt = reg.get("user1");
-        Set<Node> visible = graphs(testdsg, qExecAddFiler(testdsg, sCxt)); 
+        Set<Node> visible = graphs(testdsg, sCxt); 
         assertSeen(visible, g1);
     }
 
     @Test public void graph_names_user2() {
         SecurityPolicy sCxt = reg.get("user2");
-        Set<Node> visible = graphs(testdsg, qExecAddFiler(testdsg, sCxt)); 
+        Set<Node> visible = graphs(testdsg, sCxt); 
         assertSeen(visible, g1, g2, g3);
     }
 
     @Test public void graph_names_userX() {
         SecurityPolicy sCxt = reg.get("userX");
-        Set<Node> visible = graphs(testdsg, qExecAddFiler(testdsg, sCxt)); 
+        Set<Node> visible = graphs(testdsg, sCxt); 
         assertSeen(visible);
     }
 
     // QueryExecution w/ Union default graph
     private void filter_union_user(String user, Node ... expected) {
         SecurityPolicy sCxt = reg.get(user);
-        Consumer<QueryExecution> modifier = qExec-> {
-            qExec.getContext().set(TDB.symUnionDefaultGraph, true);
-            qExec.getContext().set(TDB2.symUnionDefaultGraph, true);    // Not strictly necessary.
-            sCxt.filterTDB(testdsg, qExec); 
-        };
-        Set<Node> visible = subjects(testdsg, queryDft, modifier);
+        // XXX Need to set union.
+//        Consumer<QueryExecution> modifier = qExec-> {
+//            qExec.getContext().set(TDB.symUnionDefaultGraph, true);
+//            qExec.getContext().set(TDB2.symUnionDefaultGraph, true);
+//            sCxt.filterTDB(testdsg, qExec); 
+//        };
+//        Set<Node> visible = subjects(testdsg, queryDft, sCxt);
+        
+        Set<Node> visible = subjects(testdsg, dsg->dsg.getUnionGraph(), queryDft, sCxt);
         assertSeen(visible, expected);
     }
     
@@ -249,72 +289,59 @@ public class TestSecurityFilterLocal {
         filter_union_user("userX");
     }
     
-    private Set<Node> subjects(Graph graph, String queryString, Consumer<QueryExecution> modifier) {
-        Model model = ModelFactory.createModelForGraph(graph);
-        return
-            Txn.calculateRead(testdsg, ()->{
-                try(QueryExecution qExec = QueryExecutionFactory.create(queryString, model)) {
-                    if ( modifier != null )
-                        modifier.accept(qExec);
-                    List<QuerySolution> results = Iter.toList(qExec.execSelect());
-                    Stream<Node> stream = results.stream().map(qs->qs.get("s")).filter(Objects::nonNull).map(RDFNode::asNode);
-                    return SetUtils.toSet(stream);
-                }
-            });
-    }
-
+    
     // Graph/Model
     @Test public void query_model_userNone() {
-        query_model_user(testdsg.getDefaultGraph(), "userNone");
+        query_model_user(testdsg, dsg->dsg.getDefaultGraph(), "userNone");
     }
     
     @Test public void query_model_userDft() {
-        query_model_user(testdsg.getDefaultGraph(), "userDft", s0);
+        query_model_user(testdsg, dsg->dsg.getDefaultGraph(), "userDft", s0);
     }
 
     @Test public void query_model_user0() {
-        query_model_user(testdsg.getDefaultGraph(), "user0", s0);
+        query_model_user(testdsg, dsg->dsg.getDefaultGraph(), "user0", s0);
     }
 
     @Test public void query_model_user1() {
-        query_model_user(testdsg.getDefaultGraph(), "user1", s0);
+        query_model_user(testdsg, dsg->dsg.getDefaultGraph(), "user1", s0);
     }
 
     @Test public void query_model_user2() {
-        query_model_user(testdsg.getDefaultGraph(), "user2");
+        query_model_user(testdsg, dsg->dsg.getDefaultGraph(), "user2");
     }
 
     @Test public void query_model_ng_userNone() {
-        query_model_user(testdsg.getGraph(g1), "userNone");
+        query_model_user(testdsg, dsg->dsg.getGraph(g1), "userNone");
     }
 
     @Test public void query_model_ng_user11() {
-        query_model_user(testdsg.getGraph(g1), "user1", s1);
+        query_model_user(testdsg, dsg->dsg.getGraph(g1), "user1", s1);
     }
 
     @Test public void query_model_ng_user21() {
-        query_model_user(testdsg.getGraph(g1), "user2", s1);
+        query_model_user(testdsg, dsg->dsg.getGraph(g1), "user2", s1);
     }
 
     @Test public void query_model_ng_user12() {
-        query_model_user(testdsg.getGraph(g2), "user1");
+        query_model_user(testdsg, dsg->dsg.getGraph(g2), "user1");
     }
 
     @Test public void query_model_ng_user22() {
-        query_model_user(testdsg.getGraph(g2), "user2", s2);
+        query_model_user(testdsg, dsg->dsg.getGraph(g2), "user2", s2);
     }
     
     @Test public void query_model_userXa() {
-        query_model_user(testdsg.getDefaultGraph(), "userX");
+        query_model_user(testdsg, dsg->dsg.getDefaultGraph(), "userX");
     }
 
     @Test public void query_model_userXb() {
-        query_model_user(testdsg.getGraph(g1), "userX");
+        query_model_user(testdsg, dsg->dsg.getGraph(g1), "userX");
     }
 
-    private void query_model_user(Graph g, String user, Node ... expected) {
+    private void query_model_user(DatasetGraph dsg, Function<DatasetGraph, Graph> graphChoice, String user, Node ... expected) {
         SecurityPolicy sCxt = reg.get(user);
-        Set<Node> visible = subjects(g, queryDft, qExecAddFiler(testdsg, sCxt));
+        Set<Node> visible = subjects(dsg, graphChoice, queryDft, sCxt);
         assertSeen(visible, expected);
     }
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/99b99463/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionREST.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionREST.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionREST.java
index f9f0443..e16e793 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionREST.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionREST.java
@@ -24,6 +24,7 @@ import javax.servlet.http.HttpServletRequest ;
 import javax.servlet.http.HttpServletResponse ;
 
 import org.apache.jena.fuseki.server.CounterName ;
+import org.apache.jena.sparql.core.DatasetGraph;
 
 /** Common point for operations that are "REST"ish (use GET/PUT etc as operations). */ 
 public abstract class ActionREST extends ActionService
@@ -68,6 +69,14 @@ public abstract class ActionREST extends ActionService
             ServletOps.errorNotImplemented("Unknown method: "+method) ;
     }
 
+    /**
+     * Decide on the dataset to use for the operation. This can be overridden
+     * by specialist subclasses e.g. data access control. 
+     */
+    protected DatasetGraph decideDataset(HttpAction action) {
+        return action.getActiveDSG() ;
+    }
+    
     // Counter wrappers
     
     private final void doGet$(HttpAction action) {

http://git-wip-us.apache.org/repos/asf/jena/blob/99b99463/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads_R.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads_R.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads_R.java
index 668f210..845ef16 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads_R.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads_R.java
@@ -80,7 +80,7 @@ public class REST_Quads_R extends REST_Quads {
 
         action.beginRead() ;
         try {
-            DatasetGraph dsg = actOn(action); 
+            DatasetGraph dsg = decideDataset(action); 
             action.response.setHeader("Content-type", lang.getContentType().toHeaderString());
             // ActionLib.contentNegotationQuads above
             // RDF/XML is not a choice but this code is general.
@@ -101,14 +101,6 @@ public class REST_Quads_R extends REST_Quads {
         }
     }
 
-    /**
-     * Decide on the dataset to use for the operation. Can be overrided by specialist
-     * subclasses.
-     */
-    protected DatasetGraph actOn(HttpAction action) {
-        return action.getActiveDSG() ;
-    }
-
     @Override
     protected void doOptions(HttpAction action) {
         action.response.setHeader(HttpNames.hAllow, "GET, HEAD, OPTIONS") ;

http://git-wip-us.apache.org/repos/asf/jena/blob/99b99463/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads_RW.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads_RW.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads_RW.java
index 98589c9..859ef95 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads_RW.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads_RW.java
@@ -90,7 +90,7 @@ public class REST_Quads_RW extends REST_Quads_R {
         UploadDetails details = null ;
         action.beginWrite() ;
         try {
-            DatasetGraph dsg = action.getActiveDSG() ;
+            DatasetGraph dsg = decideDataset(action);
             if ( clearFirst )
                 dsg.clear() ;
             StreamRDF dest = StreamRDFLib.dataset(dsg) ;
@@ -128,7 +128,7 @@ public class REST_Quads_RW extends REST_Quads_R {
         // Now insert into dataset
         action.beginWrite() ;
         try {
-            DatasetGraph dsg = action.getActiveDSG() ;
+            DatasetGraph dsg = decideDataset(action);
             if ( clearFirst )
                 dsg.clear() ;
             FusekiLib.addDataInto(dsgTmp, dsg) ;

http://git-wip-us.apache.org/repos/asf/jena/blob/99b99463/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP.java
index 1321125..af4d42a 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP.java
@@ -33,11 +33,6 @@ import org.apache.jena.sparql.core.DatasetGraph ;
 
 public abstract class SPARQL_GSP extends ActionREST
 {
-    protected final static Target determineTarget(HttpAction action) {
-        DatasetGraph dsg = action.getActiveDSG(); 
-        return determineTarget(dsg, action);
-    }
-    
     protected final static Target determineTarget(DatasetGraph dsg, HttpAction action) {
         // Delayed until inside a transaction.
         if ( dsg == null )

http://git-wip-us.apache.org/repos/asf/jena/blob/99b99463/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP_R.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP_R.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP_R.java
index 8204136..e3f0925 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP_R.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP_R.java
@@ -63,7 +63,7 @@ public class SPARQL_GSP_R extends SPARQL_GSP
         action.beginRead() ;
         setCommonHeaders(action.response) ;
         try {
-            DatasetGraph dsg = actOn(action);
+            DatasetGraph dsg = decideDataset(action);
             Target target = determineTarget(dsg, action) ;
             if ( action.log.isDebugEnabled() )
                 action.log.debug("GET->"+target) ;
@@ -95,14 +95,6 @@ public class SPARQL_GSP_R extends SPARQL_GSP
         } finally { action.endRead() ; }
     }
     
-    /**
-     * Decide on the dataset to use for the operation. Can be overrided by specialist
-     * subclasses.
-     */
-    protected DatasetGraph actOn(HttpAction action) {
-        return action.getActiveDSG() ;
-    }
-    
     @Override
     protected void doOptions(HttpAction action) {
         setCommonHeadersForOptions(action.response) ;
@@ -115,8 +107,9 @@ public class SPARQL_GSP_R extends SPARQL_GSP
     protected void doHead(HttpAction action) {
         action.beginRead() ;
         setCommonHeaders(action.response) ;
-        try { 
-            Target target = determineTarget(action) ;
+        try {
+            DatasetGraph dsg = decideDataset(action);
+            Target target = determineTarget(dsg, action) ;
             if ( action.log.isDebugEnabled() )
                 action.log.debug("HEAD->"+target) ;
             if ( ! target.exists() )

http://git-wip-us.apache.org/repos/asf/jena/blob/99b99463/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP_RW.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP_RW.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP_RW.java
index 4ba5ffe..2b4952f 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP_RW.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_GSP_RW.java
@@ -34,6 +34,7 @@ import org.apache.jena.riot.RiotException ;
 import org.apache.jena.riot.system.StreamRDF ;
 import org.apache.jena.riot.system.StreamRDFLib ;
 import org.apache.jena.riot.web.HttpNames ;
+import org.apache.jena.sparql.core.DatasetGraph;
 import org.apache.jena.sparql.graph.GraphFactory ;
 import org.apache.jena.web.HttpSC ;
 
@@ -55,7 +56,8 @@ public class SPARQL_GSP_RW extends SPARQL_GSP_R
     protected void doDelete(HttpAction action) {
         action.beginWrite() ;
         try {
-            Target target = determineTarget(action) ;
+            DatasetGraph dsg = decideDataset(action);
+            Target target = determineTarget(dsg, action) ;
             if ( action.log.isDebugEnabled() )
                 action.log.debug("DELETE->"+target) ;
             boolean existedBefore = target.exists() ; 
@@ -113,10 +115,11 @@ public class SPARQL_GSP_RW extends SPARQL_GSP_R
      * @param cleanDest Whether to remove data first (true = PUT, false = POST)
      * @return whether the target existed beforehand
      */
-    protected static UploadDetails addDataIntoTxn(HttpAction action, boolean overwrite) {   
+    protected UploadDetails addDataIntoTxn(HttpAction action, boolean overwrite) {   
         action.beginWrite();
         try {
-            Target target = determineTarget(action) ;
+            DatasetGraph dsg = decideDataset(action);
+            Target target = determineTarget(dsg, action) ;
             if ( action.log.isDebugEnabled() )
                 action.log.debug(action.request.getMethod().toUpperCase()+"->"+target) ;
             boolean existedBefore = target.exists() ;
@@ -156,7 +159,7 @@ public class SPARQL_GSP_RW extends SPARQL_GSP_R
      * @return whether the target existed beforehand.
      */
     
-    protected static UploadDetails addDataIntoNonTxn(HttpAction action, boolean overwrite) {
+    protected UploadDetails addDataIntoNonTxn(HttpAction action, boolean overwrite) {
         Graph graphTmp = GraphFactory.createGraphMem() ;
         StreamRDF dest = StreamRDFLib.graph(graphTmp) ;
 
@@ -168,7 +171,8 @@ public class SPARQL_GSP_RW extends SPARQL_GSP_R
         }
         // Now insert into dataset
         action.beginWrite() ;
-        Target target = determineTarget(action) ;
+        DatasetGraph dsg = decideDataset(action);
+        Target target = determineTarget(dsg, action) ;
         boolean existedBefore = false ;
         try {
             if ( action.log.isDebugEnabled() )
@@ -194,8 +198,9 @@ public class SPARQL_GSP_RW extends SPARQL_GSP_R
     /** Delete a graph. This removes the storage choice and looses the setup.
      * The default graph is cleared, not removed.
      */
-    protected static void deleteGraph(HttpAction action) {
-        Target target = determineTarget(action) ;
+    protected void deleteGraph(HttpAction action) {
+        DatasetGraph dsg = decideDataset(action);
+        Target target = determineTarget(dsg, action) ;
         if ( target.isDefault )
             clearGraph(target) ;
         else
@@ -203,7 +208,7 @@ public class SPARQL_GSP_RW extends SPARQL_GSP_R
     }
 
     /** Clear a graph - this leaves the storage choice and setup in-place */ 
-    protected static void clearGraph(Target target) {
+    protected void clearGraph(Target target) {
         Graph g = target.graph() ;
         g.getPrefixMapping().clearNsPrefixMap() ;
         g.clear() ;


[19/27] jena git commit: General use DatasetGraphFilteredView

Posted by an...@apache.org.
General use DatasetGraphFilteredView


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

Branch: refs/heads/master
Commit: 7b0661fbb8d4f99fbc6b02726e082fb7d99042b0
Parents: dc7f38f
Author: Andy Seaborne <an...@apache.org>
Authored: Sun Aug 26 18:07:47 2018 +0100
Committer: Andy Seaborne <an...@apache.org>
Committed: Sun Aug 26 18:07:47 2018 +0100

----------------------------------------------------------------------
 .../sparql/core/DatasetGraphFilteredView.java   | 153 +++++++++++++++++++
 1 file changed, 153 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/7b0661fb/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphFilteredView.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphFilteredView.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphFilteredView.java
new file mode 100644
index 0000000..a9fa8f2
--- /dev/null
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphFilteredView.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.sparql.core;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.function.Predicate;
+
+import org.apache.jena.atlas.iterator.Iter;
+import org.apache.jena.atlas.logging.Log;
+import org.apache.jena.graph.Graph;
+import org.apache.jena.graph.Node;
+import org.apache.jena.sparql.graph.GraphReadOnly;
+import org.apache.jena.sparql.graph.GraphUnionRead;
+
+/**
+ * A read-only {@link DatasetGraph} that applies a filter testing all triples and quads
+ * returned by accessing the data. Only quads where the filter tests for "true" are exposed. 
+ */
+public class DatasetGraphFilteredView extends DatasetGraphReadOnly {
+  /* 
+  Write operations
+    add(Quad)
+    delete(Quad)
+    add(Node, Node, Node, Node)
+    delete(Node, Node, Node, Node)
+    deleteAny(Node, Node, Node, Node)
+    clear()
+    
+  Read operations  
+    listGraphNodes()
+    isEmpty()
+    find()
+    find(Quad)
+    find(Node, Node, Node, Node)
+    findNG(Node, Node, Node, Node)
+    contains(Quad)
+    contains(Node, Node, Node, Node)
+    size()
+    toString()
+    
+  Graph operations
+    listGraphNodes()
+    getGraph(Node)
+    getDefaultGraph()
+    containsGraph(Node)
+  */
+
+    private final Predicate<Quad> quadFilter;
+    private final Collection<Node> visibleGraphs;
+
+    public DatasetGraphFilteredView(DatasetGraph dsg, Predicate<Quad> filter, Collection<Node> visibleGraphs) {
+        super(dsg);
+        this.quadFilter = filter;
+        if ( visibleGraphs.contains(Quad.defaultGraphIRI) || visibleGraphs.contains(Quad.defaultGraphNodeGenerated) ) {
+            Log.warn(DatasetGraphFilteredView.class, "default graph Node in visibleGraphs colelction - fix up applied");
+            visibleGraphs = new HashSet<>(visibleGraphs);
+            visibleGraphs.remove(Quad.defaultGraphIRI);
+            visibleGraphs.remove(Quad.defaultGraphNodeGenerated);
+        }
+        this.visibleGraphs = visibleGraphs;
+    }
+    
+    private boolean filter(Quad quad) {
+        return quadFilter.test(quad);
+    }
+
+    private Iterator<Quad> filter(Iterator<Quad> iter) {
+        return Iter.filter(iter, this::filter);
+    }
+    
+    // Need to intercept these because otherwise that are a GraphView of the wrapped "dsg", not this one.  
+
+    @Override
+    public Graph getDefaultGraph() {
+        Graph g = GraphView.createDefaultGraph(this);
+        return new GraphReadOnly(g);
+    }
+    
+    @Override
+    public Graph getGraph(Node graphNode) {
+        if ( Quad.isUnionGraph(graphNode)) 
+            return getUnionGraph(); 
+        Graph g = GraphView.createNamedGraph(this, graphNode);
+        return new GraphReadOnly(g);
+    }
+
+    @Override
+    public Iterator<Node> listGraphNodes() {
+//        Predicate<Node> notSpecial = x-> ! ( Quad.isDefaultGraph(x)|| Quad.isUnionGraph(x) );
+//        return Iter.filter(visibleGraphs.iterator(), notSpecial);
+        return visibleGraphs.iterator();
+    }
+
+    @Override
+    public Graph getUnionGraph() {
+        return new GraphUnionRead(this, visibleGraphs);
+    }
+
+    @Override
+    public Iterator<Quad> find() {
+        return filter(super.find());
+    }
+
+    @Override public Iterator<Quad> find(Node g, Node s, Node p, Node o) {
+        // Need union handling if for general API use.
+        return filter(super.find(g, s, p, o));
+    }
+    
+    @Override public Iterator<Quad> find(Quad quad) {
+        // union
+        return filter(super.find(quad));
+    }
+    
+    @Override public Iterator<Quad> findNG(Node g, Node s, Node p , Node o) {
+        // union
+        return filter(super.findNG(g, s, p, o));
+    }
+
+    @Override public boolean contains(Node g, Node s, Node p , Node o) {
+        return filter(super.find(g, s, p, o)).hasNext();
+    }
+
+    @Override public boolean contains(Quad quad) {
+        return filter(super.find(quad)).hasNext();
+    }
+    
+    @Override public boolean isEmpty() {
+        return ! this.find().hasNext(); 
+    }
+    
+//    @Override public String toString() {
+//        return  
+//    }
+
+}


[10/27] jena git commit: JENA-1595: Put log4j and slf4j-log4j into scope test for all modules

Posted by an...@apache.org.
JENA-1595: Put log4j and slf4j-log4j into scope test for all modules


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

Branch: refs/heads/master
Commit: a17c6dd732ca32ef4122dc089c8717fccc5b75b1
Parents: 344ae5d
Author: Andy Seaborne <an...@apache.org>
Authored: Thu Aug 23 18:27:39 2018 +0100
Committer: Andy Seaborne <an...@apache.org>
Committed: Thu Aug 23 18:27:39 2018 +0100

----------------------------------------------------------------------
 jena-base/pom.xml | 7 +++++++
 pom.xml           | 4 ++--
 2 files changed, 9 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/a17c6dd7/jena-base/pom.xml
----------------------------------------------------------------------
diff --git a/jena-base/pom.xml b/jena-base/pom.xml
index 5fd602a..438f9e2 100644
--- a/jena-base/pom.xml
+++ b/jena-base/pom.xml
@@ -44,7 +44,14 @@
       <version>3.9.0-SNAPSHOT</version>
     </dependency>
 
+    <!-- For compiling the log4j1 helper code. -->
     <dependency>
+      <groupId>log4j</groupId>
+      <artifactId>log4j</artifactId>
+      <optional>true</optional>
+    </dependency>
+    
+   <dependency>
       <groupId>org.apache.commons</groupId>
       <artifactId>commons-csv</artifactId>
     </dependency> 

http://git-wip-us.apache.org/repos/asf/jena/blob/a17c6dd7/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 9744eb3..8ffc688 100644
--- a/pom.xml
+++ b/pom.xml
@@ -558,13 +558,13 @@
     <dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-log4j12</artifactId>
-      <optional>true</optional>
+      <scope>test</scope>
     </dependency>
 
     <dependency>
       <groupId>log4j</groupId>
       <artifactId>log4j</artifactId>
-      <optional>true</optional>
+      <scope>test</scope>
     </dependency>
 
   </dependencies>


[22/27] jena git commit: JENA-1494: Handle any dataset

Posted by an...@apache.org.
JENA-1494: Handle any dataset


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

Branch: refs/heads/master
Commit: 80a2f7b543137fd35cb1e8c2b55fa432854d5f18
Parents: 2ec9065
Author: Andy Seaborne <an...@apache.org>
Authored: Sun Aug 26 21:46:07 2018 +0100
Committer: Andy Seaborne <an...@apache.org>
Committed: Sun Aug 26 23:27:13 2018 +0100

----------------------------------------------------------------------
 .../sparql/core/DatasetGraphFilteredView.java   |  3 ++
 .../jena/fuseki/access/DataAccessCtl.java       | 35 +++++++++---------
 .../access/Filtered_SPARQL_QueryDataset.java    | 21 +++++------
 .../jena/fuseki/access/SecurityPolicy.java      |  3 +-
 .../fuseki/access/TestSecurityFilterFuseki.java | 18 ++++++----
 .../fuseki/access/TestSecurityFilterLocal.java  | 38 ++++++++++++--------
 .../apache/jena/fuseki/cmds/FusekiBasicCmd.java |  4 +--
 .../jena/fuseki/servlets/SPARQL_Query.java      |  6 ++--
 8 files changed, 69 insertions(+), 59 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/80a2f7b5/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphFilteredView.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphFilteredView.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphFilteredView.java
index 3084e33..4c49bd1 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphFilteredView.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphFilteredView.java
@@ -114,6 +114,9 @@ public class DatasetGraphFilteredView extends DatasetGraphReadOnly implements Da
 
     @Override
     public Graph getUnionGraph() {
+        // Does not exploit TDB-isms, but is general.
+        // To exploit TDB, we'd have to modify the dataset
+        // to set union graph but that's not per-request.
         return new GraphUnionRead(this, visibleGraphs);
     }
 

http://git-wip-us.apache.org/repos/asf/jena/blob/80a2f7b5/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessCtl.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessCtl.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessCtl.java
index dc8c913..3bc0812 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessCtl.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/DataAccessCtl.java
@@ -35,20 +35,23 @@ import org.apache.jena.sparql.core.DatasetGraph;
 import org.apache.jena.sparql.core.DatasetGraphFilteredView;
 import org.apache.jena.sparql.util.Context;
 import org.apache.jena.sparql.util.Symbol;
+import org.apache.jena.sys.JenaSystem;
 import org.eclipse.jetty.security.SecurityHandler;
 
 /** A library of operations related to data acess sexurity for Fuseki */  
 public class DataAccessCtl {
-
+    static { JenaSystem.init(); }
+    
     /**
      * Flag for whether this is data access controlled or not - boolean false or undef for "not
-     * controlled".
+     * controlled". This is an alternative to {@link DatasetGraphAccessControl}.
      */
     public static final Symbol   symControlledAccess      = Symbol.create(VocabSecurity.getURI() + "controlled");
     
     /**
      * Symbol for the {@link SecurityRegistry}. Must be present if
      * {@link #symControlledAccess} indicates data access control.
+     * This is an alternative to {@link DatasetGraphAccessControl}.
      */
     public static final Symbol   symSecurityRegistry      = Symbol.create(VocabSecurity.getURI() + "registry");
 
@@ -56,30 +59,26 @@ public class DataAccessCtl {
     public static final Function<HttpAction, String> requestUserServlet = (action)->action.request.getRemoteUser();
 
     /**
+     * Get the user from {@code ?user} query string parameter. Use carefully; for situations where the user name has
+     * been authenticated already and is being passed on securely. Also for testing.
+     */
+    public static final Function<HttpAction, String> paramUserServlet = (action)->action.request.getParameter("user");
+
+    /**
      * Add data access control information on a {@link DatasetGraph}. This modifies the
      * {@link DatasetGraph}'s {@link Context}.
      */
-    private static void controlledDataset(DatasetGraph dsg, SecurityRegistry reg) {
-        // Or wrapper.
+    private static void addSecurityRegistry(DatasetGraph dsg, SecurityRegistry reg) {
         dsg.getContext().set(symControlledAccess, true);
         dsg.getContext().set(symSecurityRegistry, reg);
     }
 
-//    /**
-//     * Enable data access control on a {@link Dataset}. This modifies the
-//     * {@link Dataset}'s {@link Context}.
-//     */
-//    private static void controlledDataset(Dataset ds, SecurityRegistry reg) {
-//        ds.getContext().set(symControlledAccess, true);
-//        ds.getContext().set(symSecurityRegistry, reg);
-//    }
-
     /**
-     * Return a {@link DatasetGraph} with added data access control. Use of the original
-     * {@code DatasetGraph} is not controlled.
+     * Return a {@link DatasetGraph} with added data access control. 
+     * Use of the original {@code DatasetGraph} is not controlled.
      */
-    public static Dataset wrapControlledDataset(Dataset dsBase, SecurityRegistry reg) {
-        DatasetGraph dsg = wrapControlledDataset(dsBase.asDatasetGraph(), reg);
+    public static Dataset controlledDataset(Dataset dsBase, SecurityRegistry reg) {
+        DatasetGraph dsg = controlledDataset(dsBase.asDatasetGraph(), reg);
         return DatasetFactory.wrap(dsg);
     }
     
@@ -87,7 +86,7 @@ public class DataAccessCtl {
      * Return a {@link DatasetGraph} with added data access control. Use of the original
      * {@code DatasetGraph} is not controlled.
      */
-    public static DatasetGraph wrapControlledDataset(DatasetGraph dsgBase, SecurityRegistry reg) {
+    public static DatasetGraph controlledDataset(DatasetGraph dsgBase, SecurityRegistry reg) {
         if ( dsgBase instanceof DatasetGraphAccessControl ) {
             DatasetGraphAccessControl dsgx = (DatasetGraphAccessControl)dsgBase;
             if ( reg == dsgx.getRegistry() )

http://git-wip-us.apache.org/repos/asf/jena/blob/80a2f7b5/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_QueryDataset.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_QueryDataset.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_QueryDataset.java
index 49efc4f..8162f91 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_QueryDataset.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/Filtered_SPARQL_QueryDataset.java
@@ -18,6 +18,8 @@
 
 package org.apache.jena.fuseki.access;
 
+import java.util.Collection;
+import java.util.Collections;
 import java.util.function.Function;
 
 import org.apache.jena.fuseki.servlets.ActionService;
@@ -38,6 +40,12 @@ public class Filtered_SPARQL_QueryDataset extends SPARQL_QueryDataset {
     }
 
     @Override
+    protected Collection<String> customParams() {
+        // The additional ?user.
+        return Collections.singletonList("user");
+    }
+    
+    @Override
     protected QueryExecution createQueryExecution(HttpAction action, Query query, Dataset dataset) {
         // Server database, not the possibly dynamically built "dataset"
         DatasetGraph dsg = action.getDataset();
@@ -46,20 +54,9 @@ public class Filtered_SPARQL_QueryDataset extends SPARQL_QueryDataset {
         if ( ! DataAccessCtl.isAccessControlled(dsg) )
             return super.createQueryExecution(action, query, dataset);
 
-        // XXX Generalize to any DSG.
         SecurityPolicy sCxt = DataAccessLib.getSecurityPolicy(action, dataset.asDatasetGraph(), requestUser);
-        
+        // A QueryExecution for controlled access
         QueryExecution qExec = sCxt.createQueryExecution(query, dsg);
-        
-//        if ( dsg instanceof DatasetGraphAccessControl ) {
-//            // Take off one layer.
-//            dsg = DatasetGraphAccessControl.removeWrapper(dsg);
-//            // Add back the Dataset for the createQueryExecution call.
-//            dataset = DatasetFactory.wrap(dsg);
-//        }
-//        QueryExecution qExec = super.createQueryExecution(action, query, dataset);
-//        if ( sCxt != null )
-//            sCxt.filterTDB(dsg, qExec);
         return qExec;
     }
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/80a2f7b5/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityPolicy.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityPolicy.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityPolicy.java
index c0d420a..ca19794 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityPolicy.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityPolicy.java
@@ -114,8 +114,7 @@ public class SecurityPolicy {
             if ( quad.isDefaultGraph() )
                 return matchDefaultGraph;
             if ( quad.isUnionGraph() ) 
-                // XXX What to do here.
-                // Need special union graph?
+                // Union graph is automatically there but its visible contents are different.
                 return true;
             return graphNames.contains(quad.getGraph());
         };

http://git-wip-us.apache.org/repos/asf/jena/blob/80a2f7b5/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterFuseki.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterFuseki.java b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterFuseki.java
index 7050ef0..c9d188a 100644
--- a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterFuseki.java
+++ b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterFuseki.java
@@ -41,6 +41,7 @@ import org.apache.jena.rdf.model.RDFNode;
 import org.apache.jena.rdfconnection.RDFConnection;
 import org.apache.jena.rdfconnection.RDFConnectionFactory;
 import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.core.DatasetGraphFactory;
 import org.apache.jena.sparql.core.Quad;
 import org.apache.jena.sparql.engine.http.QueryExceptionHTTP;
 import org.apache.jena.tdb.TDBFactory;
@@ -62,14 +63,17 @@ public class TestSecurityFilterFuseki {
 
     @Parameters(name = "{index}: {0}")
     public static Iterable<Object[]> data() {
-        Object[] obj1 = { "TDB", "data1" };
+        Object[] obj1 = { "TDB",  "data1" };
         Object[] obj2 = { "TDB2", "data2" };
-        return Arrays.asList(obj1, obj2);
+        Object[] obj3 = { "TIM",  "data3" };
+        return Arrays.asList(obj1, obj2, obj3);
     }
 
     private final String baseUrl;
     private static DatasetGraph testdsg1 =  TDBFactory.createDatasetGraph();
     private static DatasetGraph testdsg2 =  DatabaseMgr.createDatasetGraph();
+    private static DatasetGraph testdsg3 =  DatasetGraphFactory.createTxnMem();
+    
     private static FusekiServer fusekiServer;
 
     // Set up Fuseki with two datasets, "data1" backed by TDB and "data2" backed by TDB2.
@@ -77,6 +81,7 @@ public class TestSecurityFilterFuseki {
         int port = FusekiLib.choosePort();
         addTestData(testdsg1);
         addTestData(testdsg2);
+        addTestData(testdsg3);
         
         SecurityRegistry reg = new SecurityRegistry();
         reg.put("userNone", SecurityPolicy.NONE);
@@ -85,10 +90,10 @@ public class TestSecurityFilterFuseki {
         reg.put("user1", new SecurityPolicy("http://test/g1", Quad.defaultGraphIRI.getURI()));
         reg.put("user2", new SecurityPolicy("http://test/g1", "http://test/g2", "http://test/g3"));
         
-        // XXX Also need wrapped tests
-        testdsg1 = DataAccessCtl.wrapControlledDataset(testdsg1, reg);
-        testdsg2 = DataAccessCtl.wrapControlledDataset(testdsg2, reg);
-
+        testdsg1 = DataAccessCtl.controlledDataset(testdsg1, reg);
+        testdsg2 = DataAccessCtl.controlledDataset(testdsg2, reg);
+        testdsg3 = DataAccessCtl.controlledDataset(testdsg3, reg);
+        
         UserStore userStore = userStore();
         SecurityHandler sh = JettyLib.makeSecurityHandler("/*", "DatasetRealm", userStore);
         
@@ -96,6 +101,7 @@ public class TestSecurityFilterFuseki {
             .port(port)
             .add("data1", testdsg1)
             .add("data2", testdsg2)
+            .add("data3", testdsg3)
             .build();
         fusekiServer.start();
     }

http://git-wip-us.apache.org/repos/asf/jena/blob/80a2f7b5/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterLocal.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterLocal.java b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterLocal.java
index 0268b5b..1e03aba 100644
--- a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterLocal.java
+++ b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterLocal.java
@@ -48,8 +48,10 @@ import org.apache.jena.sparql.core.DatasetGraph;
 import org.apache.jena.sparql.core.DatasetGraphFactory;
 import org.apache.jena.sparql.core.Quad;
 import org.apache.jena.system.Txn;
+import org.apache.jena.tdb.TDB;
 import org.apache.jena.tdb.TDBFactory;
 import org.apache.jena.tdb2.DatabaseMgr;
+import org.apache.jena.tdb2.TDB2;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
@@ -86,7 +88,7 @@ public class TestSecurityFilterLocal {
     private final DatasetGraph testdsg;
     private SecurityRegistry reg = new SecurityRegistry();
     private final boolean applyFilterDSG;
-    private final boolean applyFilterQExec;
+    private final boolean applyFilterTDB;
     
     public TestSecurityFilterLocal(String name, Creator<DatasetGraph> source, boolean applyFilterTDB) {
         DatasetGraph dsgBase = source.create();
@@ -96,9 +98,9 @@ public class TestSecurityFilterLocal {
         reg.put("user0", new SecurityPolicy(Quad.defaultGraphIRI.getURI()));
         reg.put("user1", new SecurityPolicy("http://test/g1", Quad.defaultGraphIRI.getURI()));
         reg.put("user2", new SecurityPolicy("http://test/g1", "http://test/g2", "http://test/g3"));
-        testdsg = DataAccessCtl.wrapControlledDataset(dsgBase, reg);
+        testdsg = DataAccessCtl.controlledDataset(dsgBase, reg);
+        this.applyFilterTDB = applyFilterTDB;
         this.applyFilterDSG = ! applyFilterTDB;
-        this.applyFilterQExec = applyFilterTDB;
     }
     
     private static void assertSeen(Set<Node> visible, Node ... expected) {
@@ -121,7 +123,7 @@ public class TestSecurityFilterLocal {
         return
             Txn.calculateRead(ds, ()->{
                 try(QueryExecution qExec = QueryExecutionFactory.create(queryString, ds)) {
-                    if ( applyFilterQExec )
+                    if ( applyFilterTDB )
                         sCxt.filterTDB(dsg1, qExec);
                     List<QuerySolution> results = Iter.toList(qExec.execSelect());
                     Stream<Node> stream = results.stream()
@@ -142,7 +144,7 @@ public class TestSecurityFilterLocal {
         return
             Txn.calculateRead(testdsg, ()->{
                 try(QueryExecution qExec = QueryExecutionFactory.create(queryString, model)) {
-                    if ( applyFilterQExec )
+                    if ( applyFilterTDB )
                         sCxt.filterTDB(dsg1, qExec);
                     List<QuerySolution> results = Iter.toList(qExec.execSelect());
                     Stream<Node> stream = results.stream().map(qs->qs.get("s")).filter(Objects::nonNull).map(RDFNode::asNode);
@@ -159,7 +161,7 @@ public class TestSecurityFilterLocal {
         return
             Txn.calculateRead(ds, ()->{
                 try(QueryExecution qExec = QueryExecutionFactory.create(queryGraphNames, ds)) {
-                    if ( applyFilterQExec )
+                    if ( applyFilterTDB )
                         sCxt.filterTDB(dsg1, qExec);
                     List<QuerySolution> results = Iter.toList(qExec.execSelect());
                     Stream<Node> stream = results.stream().map(qs->qs.get("g")).filter(Objects::nonNull).map(RDFNode::asNode);
@@ -251,15 +253,21 @@ public class TestSecurityFilterLocal {
     // QueryExecution w/ Union default graph
     private void filter_union_user(String user, Node ... expected) {
         SecurityPolicy sCxt = reg.get(user);
-        // XXX Need to set union.
-//        Consumer<QueryExecution> modifier = qExec-> {
-//            qExec.getContext().set(TDB.symUnionDefaultGraph, true);
-//            qExec.getContext().set(TDB2.symUnionDefaultGraph, true);
-//            sCxt.filterTDB(testdsg, qExec); 
-//        };
-//        Set<Node> visible = subjects(testdsg, queryDft, sCxt);
-        
-        Set<Node> visible = subjects(testdsg, dsg->dsg.getUnionGraph(), queryDft, sCxt);
+        Set<Node> visible;
+        if ( applyFilterTDB ) {
+            // TDB special version. Set the TDB flags for union default graph
+            try {
+                testdsg.getContext().set(TDB.symUnionDefaultGraph, true);
+                testdsg.getContext().set(TDB2.symUnionDefaultGraph, true);
+                visible = subjects(testdsg, queryDft, sCxt);
+            } finally {
+                // And unset them.
+                testdsg.getContext().unset(TDB.symUnionDefaultGraph);
+                testdsg.getContext().unset(TDB2.symUnionDefaultGraph);
+            }
+        } else {
+            visible = subjects(testdsg, dsg->dsg.getUnionGraph(), queryDft, sCxt);
+        }
         assertSeen(visible, expected);
     }
     

http://git-wip-us.apache.org/repos/asf/jena/blob/80a2f7b5/jena-fuseki2/jena-fuseki-basic/src/main/java/org/apache/jena/fuseki/cmds/FusekiBasicCmd.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-basic/src/main/java/org/apache/jena/fuseki/cmds/FusekiBasicCmd.java b/jena-fuseki2/jena-fuseki-basic/src/main/java/org/apache/jena/fuseki/cmds/FusekiBasicCmd.java
index baa3b28..f1ba1f2 100644
--- a/jena-fuseki2/jena-fuseki-basic/src/main/java/org/apache/jena/fuseki/cmds/FusekiBasicCmd.java
+++ b/jena-fuseki2/jena-fuseki-basic/src/main/java/org/apache/jena/fuseki/cmds/FusekiBasicCmd.java
@@ -510,10 +510,8 @@ public class FusekiBasicCmd {
             }
             
             if ( serverConfig.datasetPath != null ) {
-                if ( mapDatasetEndpoints.size() != 1 ) {
-                    System.err.println(serverConfig.datasetPath);
+                if ( mapDatasetEndpoints.size() != 1 )
                     log.error("Expected only one dataset");
-                }
                 List<String> endpoints = mapDatasetEndpoints.get(serverConfig.datasetPath); 
                 FmtLog.info(log,  "Dataset Type = %s", serverConfig.datasetDescription);
                 FmtLog.info(log,  "Path = %s; Services = %s", serverConfig.datasetPath, endpoints);

http://git-wip-us.apache.org/repos/asf/jena/blob/80a2f7b5/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Query.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Query.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Query.java
index a14e75a..931f27f 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Query.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Query.java
@@ -108,9 +108,9 @@ public abstract class SPARQL_Query extends SPARQL_Protocol
         if ( acceptedParams_ == null ) {
             synchronized(this) {
                 if ( acceptedParams_ == null )
-                // Does not matter about race condition here because the same Set should be
-                // created on any call to generateAcceptedParams.
-                acceptedParams_ = generateAcceptedParams();
+                    // Does not matter about race condition here because the same Set should be
+                    // created on any call to generateAcceptedParams.
+                    acceptedParams_ = generateAcceptedParams();
             }
         }
         return acceptedParams_;


[04/27] jena git commit: Copy dataset context to execution for query over model

Posted by an...@apache.org.
Copy dataset context to execution for query over model


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

Branch: refs/heads/master
Commit: d8e51a82fea80ea21e7cdef8445dc00adec42ee2
Parents: 0a0b831
Author: Andy Seaborne <an...@apache.org>
Authored: Mon Aug 20 20:34:04 2018 +0100
Committer: Andy Seaborne <an...@apache.org>
Committed: Thu Aug 23 17:15:05 2018 +0100

----------------------------------------------------------------------
 .../jena/query/QueryExecutionFactory.java       | 29 ++++++++++++++++++--
 1 file changed, 27 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/d8e51a82/jena-arq/src/main/java/org/apache/jena/query/QueryExecutionFactory.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/query/QueryExecutionFactory.java b/jena-arq/src/main/java/org/apache/jena/query/QueryExecutionFactory.java
index 2414d3a..f784d1a 100644
--- a/jena-arq/src/main/java/org/apache/jena/query/QueryExecutionFactory.java
+++ b/jena-arq/src/main/java/org/apache/jena/query/QueryExecutionFactory.java
@@ -22,8 +22,11 @@ import java.util.List ;
 import org.apache.http.client.HttpClient;
 import org.apache.http.protocol.HttpContext;
 import org.apache.jena.atlas.logging.Log ;
+import org.apache.jena.graph.Graph;
+import org.apache.jena.graph.impl.WrappedGraph;
 import org.apache.jena.rdf.model.Model ;
 import org.apache.jena.sparql.core.DatasetGraph ;
+import org.apache.jena.sparql.core.GraphView;
 import org.apache.jena.sparql.engine.Plan ;
 import org.apache.jena.sparql.engine.QueryEngineFactory ;
 import org.apache.jena.sparql.engine.QueryEngineRegistry ;
@@ -31,6 +34,7 @@ import org.apache.jena.sparql.engine.QueryExecutionBase ;
 import org.apache.jena.sparql.engine.binding.Binding ;
 import org.apache.jena.sparql.engine.binding.BindingRoot ;
 import org.apache.jena.sparql.engine.http.QueryEngineHTTP ;
+import org.apache.jena.sparql.graph.GraphWrapper;
 import org.apache.jena.sparql.syntax.Element ;
 import org.apache.jena.sparql.util.Context ;
 
@@ -126,7 +130,7 @@ public class QueryExecutionFactory
     static public QueryExecution create(Query query, Model model) {
         checkArg(query) ;
         checkArg(model) ;
-        return make(query, DatasetFactory.wrap(model)) ;
+        return make(query, model) ;
     }
 
     /** Create a QueryExecution to execute over the Model.
@@ -569,7 +573,28 @@ public class QueryExecutionFactory
     }
     
     static protected QueryExecution make(Query query) {
-        return make(query, null) ;
+        return make(query, (Dataset)null) ;
+    }
+
+    protected  static QueryExecution make(Query query, Model model) { 
+        Dataset dataset = DatasetFactory.wrap(model);
+        Graph g = unwrap(model.getGraph());
+        if ( g instanceof GraphView ) {
+            GraphView gv = (GraphView)model.getGraph();
+            // Copy context of the storage dataset to the wrapper dataset. 
+            dataset.getContext().putAll(gv.getDataset().getContext());
+        }
+        return make(query, dataset);
+    }
+    
+    private static Graph unwrap(Graph graph) {
+        for(;;) {
+            if ( graph instanceof GraphWrapper )
+                graph = ((GraphWrapper)graph).get();
+            else if ( graph instanceof WrappedGraph )
+                graph = ((WrappedGraph)graph).getWrapped();
+            else return graph;
+        }
     }
 
     protected  static QueryExecution make(Query query, Dataset dataset)


[17/27] jena git commit: Provide DatasetGraph operation

Posted by an...@apache.org.
Provide DatasetGraph operation


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

Branch: refs/heads/master
Commit: ec54d3ddc3eeab1e33cf0a8f0d4e44be40c4d16b
Parents: cdb9619
Author: Andy Seaborne <an...@apache.org>
Authored: Sun Aug 26 18:04:53 2018 +0100
Committer: Andy Seaborne <an...@apache.org>
Committed: Sun Aug 26 18:04:53 2018 +0100

----------------------------------------------------------------------
 .../jena/query/QueryExecutionFactory.java       | 35 +++++++++++++------
 .../jena/sparql/engine/QueryExecutionBase.java  | 36 +++++++++++++++++---
 2 files changed, 57 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/ec54d3dd/jena-arq/src/main/java/org/apache/jena/query/QueryExecutionFactory.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/query/QueryExecutionFactory.java b/jena-arq/src/main/java/org/apache/jena/query/QueryExecutionFactory.java
index f784d1a..1894ca5 100644
--- a/jena-arq/src/main/java/org/apache/jena/query/QueryExecutionFactory.java
+++ b/jena-arq/src/main/java/org/apache/jena/query/QueryExecutionFactory.java
@@ -18,6 +18,7 @@
 
 package org.apache.jena.query;
 import java.util.List ;
+import java.util.Objects;
 
 import org.apache.http.client.HttpClient;
 import org.apache.http.protocol.HttpContext;
@@ -93,7 +94,19 @@ public class QueryExecutionFactory
         // checkArg(dataset) ; // Allow null
         return make(query, dataset) ;
     }
-
+    
+    /**
+     * Create a QueryExecution to execute over the {@link DatasetGraph}.
+     * 
+     * @param query Query
+     * @param datasetGraph Target of the query
+     * @return QueryExecution
+     */
+    static public QueryExecution create(Query query, DatasetGraph datasetGraph) {
+        Objects.requireNonNull(query, "Query is null") ;
+        Objects.requireNonNull(datasetGraph, "DatasetGraph is null") ;
+        return make(query, datasetGraph) ;
+    }
     /** Create a QueryExecution to execute over the Dataset.
      * 
      * @param queryStr     Query string
@@ -580,7 +593,7 @@ public class QueryExecutionFactory
         Dataset dataset = DatasetFactory.wrap(model);
         Graph g = unwrap(model.getGraph());
         if ( g instanceof GraphView ) {
-            GraphView gv = (GraphView)model.getGraph();
+            GraphView gv = (GraphView)g;
             // Copy context of the storage dataset to the wrapper dataset. 
             dataset.getContext().putAll(gv.getDataset().getContext());
         }
@@ -597,23 +610,25 @@ public class QueryExecutionFactory
         }
     }
 
-    protected  static QueryExecution make(Query query, Dataset dataset)
-    { return make(query, dataset, null) ; }
+    protected static QueryExecution make(Query query, Dataset dataset)
+    { return make(query, dataset, null, null) ; }
 
-    
-    protected static QueryExecution make(Query query, Dataset dataset, Context context) {
+    protected static QueryExecution make(Query query, DatasetGraph datasetGraph)
+    { return make(query, null, datasetGraph, null) ; }
+
+    // Both Dataset and DatasetGraph for full backwards compatibility 
+    protected static QueryExecution make(Query query, Dataset dataset, DatasetGraph dsg, Context context) {
+        if ( dsg == null && dataset != null )
+            dsg = dataset.asDatasetGraph();
         query.setResultVars() ;
         if ( context == null )
             context = ARQ.getContext() ;  // .copy done in QueryExecutionBase -> Context.setupContext.
-        DatasetGraph dsg = null ;
-        if ( dataset != null )
-            dsg = dataset.asDatasetGraph() ;
         QueryEngineFactory f = findFactory(query, dsg, context) ;
         if ( f == null ) {
             Log.warn(QueryExecutionFactory.class, "Failed to find a QueryEngineFactory") ;
             return null ;
         }
-        return new QueryExecutionBase(query, dataset, context, f) ;
+        return new QueryExecutionBase(query, dataset, dsg, context, f) ;
     }
 
     static private QueryEngineFactory findFactory(Query query, DatasetGraph dataset, Context context) {

http://git-wip-us.apache.org/repos/asf/jena/blob/ec54d3dd/jena-arq/src/main/java/org/apache/jena/sparql/engine/QueryExecutionBase.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/engine/QueryExecutionBase.java b/jena-arq/src/main/java/org/apache/jena/sparql/engine/QueryExecutionBase.java
index e86c857..16aea26 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/engine/QueryExecutionBase.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/engine/QueryExecutionBase.java
@@ -33,7 +33,11 @@ import org.apache.jena.atlas.logging.Log;
 import org.apache.jena.graph.Node;
 import org.apache.jena.graph.Triple;
 import org.apache.jena.query.* ;
-import org.apache.jena.rdf.model.* ;
+import org.apache.jena.rdf.model.Model;
+import org.apache.jena.rdf.model.ModelFactory;
+import org.apache.jena.rdf.model.RDFNode;
+import org.apache.jena.rdf.model.Resource;
+import org.apache.jena.rdf.model.Statement;
 import org.apache.jena.shared.PrefixMapping;
 import org.apache.jena.sparql.ARQConstants;
 import org.apache.jena.sparql.core.DatasetGraph;
@@ -82,16 +86,40 @@ public class QueryExecutionBase implements QueryExecution
     private long                     timeout2         = TIMEOUT_UNSET;
     private final AlarmClock         alarmClock       = AlarmClock.get();
 
-    public QueryExecutionBase(Query query, Dataset dataset, 
+    public QueryExecutionBase(Query query, Dataset dataset, Context context, QueryEngineFactory qeFactory) {
+        this(query, dataset, null, context, qeFactory);
+    }
+
+    public QueryExecutionBase(Query query, DatasetGraph datasetGraph, Context context, QueryEngineFactory qeFactory) {
+        this(query, null, datasetGraph, context, qeFactory);
+    }
+
+    public QueryExecutionBase(Query query, Dataset dataset, DatasetGraph datasetGraph, 
                               Context context, QueryEngineFactory qeFactory) {
         this.query = query;
-        this.dataset = dataset ;
+        this.dataset = formDataset(dataset, datasetGraph);
         this.qeFactory = qeFactory ;
-        this.dsg = (dataset == null) ? null : dataset.asDatasetGraph() ;
+        this.dsg = formDatasetGraph(datasetGraph, dataset);
         this.context = Context.setupContextExec(context, dsg) ;
         init() ;
     }
     
+    private static Dataset formDataset(Dataset dataset, DatasetGraph datasetGraph) {
+        if ( dataset != null ) 
+            return dataset;
+        if ( datasetGraph != null )
+            return DatasetFactory.wrap(datasetGraph);
+        return null;
+    }
+
+    private static DatasetGraph formDatasetGraph(DatasetGraph datasetGraph, Dataset dataset) {
+        if ( datasetGraph != null ) 
+            return datasetGraph;
+        if ( dataset != null )
+            return dataset.asDatasetGraph();
+        return null;
+    }
+    
     private void init() {
         if ( query != null )
             context.put(ARQConstants.sysCurrentQuery, query) ;


[09/27] jena git commit: Fix javadoc

Posted by an...@apache.org.
Fix javadoc


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

Branch: refs/heads/master
Commit: ba3b4b6d1e6d64073db90f69b8a5feda195f3854
Parents: 9a60253
Author: Andy Seaborne <an...@apache.org>
Authored: Wed Aug 22 16:39:28 2018 +0100
Committer: Andy Seaborne <an...@apache.org>
Committed: Thu Aug 23 17:15:05 2018 +0100

----------------------------------------------------------------------
 .../jena/permissions/SecurityEvaluator.java     | 67 ++++++++------------
 1 file changed, 28 insertions(+), 39 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/ba3b4b6d/jena-permissions/src/main/java/org/apache/jena/permissions/SecurityEvaluator.java
----------------------------------------------------------------------
diff --git a/jena-permissions/src/main/java/org/apache/jena/permissions/SecurityEvaluator.java b/jena-permissions/src/main/java/org/apache/jena/permissions/SecurityEvaluator.java
index b442dd3..41c157a 100644
--- a/jena-permissions/src/main/java/org/apache/jena/permissions/SecurityEvaluator.java
+++ b/jena-permissions/src/main/java/org/apache/jena/permissions/SecurityEvaluator.java
@@ -30,62 +30,51 @@ import org.apache.jena.shared.AuthenticationRequiredException;
 /**
  * SecurityEvaluator.
  * <p>
- * The security evaluator is the link between the graph security system and an
- * external security system. This interface specifies the methods that are
- * required by the graph security system. It is assumed that the implementation
- * will handle tracking the current user and will query some underlying data
- * source to determine what actions the user can and can not take.
+ * The security evaluator is the link between the graph security system and an external
+ * security system. This interface specifies the methods that are required by the graph
+ * security system. It is assumed that the implementation will handle tracking the current
+ * user and will query some underlying data source to determine what actions the user can
+ * and can not take.
  * </p>
  * <p>
- * All questions of white listing or black listing will be handled in the
- * concrete implementation.
+ * All questions of white listing or black listing will be handled in the concrete
+ * implementation.
  * </p>
  * <p>
- * Implementations of this class should probably cache any evaluate calculations
- * as the evaluate methods are called frequently. However, the underlying
- * classes do cache results within a single method check.
+ * Implementations of this class should probably cache any evaluate calculations as the
+ * evaluate methods are called frequently. However, the underlying classes do cache
+ * results within a single method check.
  * </p>
  * <p>
  * <dl>
  * <dt>Secured operations</dt>
- * <dd>The security system recognizes and secures each of the CRUD (Create,
- * Read, Update and Delete) operations as represented by the Action enumeration.
- * </dd>
- * </dl>
- * <dl>
+ * <dd>The security system recognizes and secures each of the CRUD (Create, Read, Update
+ * and Delete) operations as represented by the Action enumeration.</dd>
  * <dt>Levels of security</dt>
- * <dd>The security interfaces operates at two (2) levels: graph (or Model) and
- * triple.
+ * <dd>The security interfaces operates at two (2) levels: graph (or Model) and triple.
  * <p>
- * At the the graph level the security evaluator may restrict CRUD access to the
- * graph or model as a whole. When evaluating the restriction, if the user it
- * not permitted to perform the operation on the graph or model access is
- * denied. If the user is permitted any triple restrictions are evaluated.
+ * At the the graph level the security evaluator may restrict CRUD access to the graph or
+ * model as a whole. When evaluating the restriction, if the user it not permitted to
+ * perform the operation on the graph or model access is denied. If the user is permitted
+ * any triple restrictions are evaluated.
  * </p>
  * <p>
- * At the triple level the security evaluator may restrict CRUD access to
- * specific triples. In order to skip potentially expensive triple security
- * checks the system will generally ask if the user is permitted the CRUD action
- * on any triple. This is represented by the SecTriple
- * <code>(ANY, ANY, ANY)</code>.
+ * At the triple level the security evaluator may restrict CRUD access to specific
+ * triples. In order to skip potentially expensive triple security checks the system will
+ * generally ask if the user is permitted the CRUD action on any triple. This is
+ * represented by the SecTriple <code>(ANY, ANY, ANY)</code>.
+ * </p>
  * <ul>
- * <li>
- * If the system does not support triple level security the system should always
- * return <code>true</code>.</li>
- * If the system does support triple level security and is unable to verify that
- * the user can execute the CRUD action against any arbitrary triple the system
- * should return <code>false</code>. </li>
+ * <li>If the system does not support triple level security the system should always
+ * return <code>true</code>.</li> If the system does support triple level security and is
+ * unable to verify that the user can execute the CRUD action against any arbitrary triple
+ * the system should return <code>false</code>.</li>
  * <li>See <code>Node.ANY</code>, <code>SecurityEvaluator.FUTURE</code>, and
- * <code>SecurityEvaluator.VARIABLE</code> for discussion of specifics of their
- * respective usages.</li>
+ * <code>SecurityEvaluator.VARIABLE</code> for discussion of specifics of their respective
+ * usages.</li>
  * </ul>
- * </p>
  * </dd>
  * </dl>
- * <dl>
- * <dt>
- *
- * </p>
  */
 public interface SecurityEvaluator {
 	/**


[21/27] jena git commit: travis.yaml: put openjdk8 first

Posted by an...@apache.org.
travis.yaml: put openjdk8 first


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

Branch: refs/heads/master
Commit: 2ec9065c23923379dbe437a9cd37c1390bfe91ba
Parents: 99b9946
Author: Andy Seaborne <an...@apache.org>
Authored: Sun Aug 26 21:20:26 2018 +0100
Committer: Andy Seaborne <an...@apache.org>
Committed: Sun Aug 26 21:20:26 2018 +0100

----------------------------------------------------------------------
 .travis.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/2ec9065c/.travis.yml
----------------------------------------------------------------------
diff --git a/.travis.yml b/.travis.yml
index 4b582ee..7b39dcc 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,7 +3,7 @@ sudo: false
 install: true
 script: mvn -B clean install
 jdk:
-  - oraclejdk8
   - openjdk8
+  - oraclejdk8
 env:
   - JAVA_OPTS="-Xmx3072M -Xms512M -XX:+UseG1GC"


[16/27] jena git commit: JENA-901: Allow for the GC having freed some weak values.

Posted by an...@apache.org.
JENA-901: Allow for the GC having freed some weak values.

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

Branch: refs/heads/master
Commit: cdb9619b559fd262b54e3e7d48973e96ebd90825
Parents: 2490112
Author: Andy Seaborne <an...@apache.org>
Authored: Sun Aug 26 14:14:27 2018 +0100
Committer: Andy Seaborne <an...@apache.org>
Committed: Sun Aug 26 14:14:27 2018 +0100

----------------------------------------------------------------------
 .../org/apache/jena/reasoner/rulesys/impl/TestLPBRuleEngine.java | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/cdb9619b/jena-core/src/test/java/org/apache/jena/reasoner/rulesys/impl/TestLPBRuleEngine.java
----------------------------------------------------------------------
diff --git a/jena-core/src/test/java/org/apache/jena/reasoner/rulesys/impl/TestLPBRuleEngine.java b/jena-core/src/test/java/org/apache/jena/reasoner/rulesys/impl/TestLPBRuleEngine.java
index fd940dd..4e5075a 100644
--- a/jena-core/src/test/java/org/apache/jena/reasoner/rulesys/impl/TestLPBRuleEngine.java
+++ b/jena-core/src/test/java/org/apache/jena/reasoner/rulesys/impl/TestLPBRuleEngine.java
@@ -162,8 +162,8 @@ public class TestLPBRuleEngine extends TestCase {
 				it.close();
 			}
 
-			// Let's see how many were cached
-			assertEquals(MAX, engine.tabledGoals.size());
+			// Let's see how many were cached - should be MAX or less (less if a GC freed weak values in the cache). 
+			assertTrue(engine.tabledGoals.size() <= MAX);
 			// and no leaks of activeInterpreters (this will happen if we forget
 			// to call hasNext above)
 			assertEquals(0, engine.activeInterpreters.size());


[18/27] jena git commit: ARQ cleanup

Posted by an...@apache.org.
ARQ cleanup


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

Branch: refs/heads/master
Commit: dc7f38f5ff67444c2d8da26d5230f99ceaa4254b
Parents: ec54d3d
Author: Andy Seaborne <an...@apache.org>
Authored: Sun Aug 26 18:05:43 2018 +0100
Committer: Andy Seaborne <an...@apache.org>
Committed: Sun Aug 26 18:05:43 2018 +0100

----------------------------------------------------------------------
 .../jena/sparql/core/DatasetGraphCopyAdd.java   |  41 ----
 .../org/apache/jena/sparql/expr/ExprSystem.java |   9 +-
 .../jena/sparql/sse/builders/BuilderLib.java    | 245 ++++++++-----------
 .../org/apache/jena/sparql/util/FmtUtils.java   |   2 +-
 4 files changed, 111 insertions(+), 186 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/dc7f38f5/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphCopyAdd.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphCopyAdd.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphCopyAdd.java
deleted file mode 100644
index 296b555..0000000
--- a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphCopyAdd.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.jena.sparql.core;
-
-import org.apache.jena.graph.Graph ;
-import org.apache.jena.graph.Node ;
-
-/** Override {@link DatasetGraph#addGraph} so that it always copies
- * content from the added graph data.
- */
-
-/*package*/ class DatasetGraphCopyAdd extends DatasetGraphWrapper 
-{
-    public DatasetGraphCopyAdd(boolean x , DatasetGraph dsg) {
-        super(dsg);
-    }
-    
-    @Override
-    public void addGraph(Node graphName, Graph graph) {
-        graph.find(null,null,null).forEachRemaining(t-> {
-            Quad q = Quad.create(graphName, t) ;
-            super.add(q) ;
-        }) ;
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jena/blob/dc7f38f5/jena-arq/src/main/java/org/apache/jena/sparql/expr/ExprSystem.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/expr/ExprSystem.java b/jena-arq/src/main/java/org/apache/jena/sparql/expr/ExprSystem.java
index dc31292..62cd25e 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/expr/ExprSystem.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/ExprSystem.java
@@ -25,7 +25,7 @@ import org.apache.jena.sparql.util.Symbol ;
 
 public abstract class ExprSystem extends ExprFunction0
 {
-    private final Symbol systemSymbol ;
+    protected final Symbol systemSymbol ;
 
     protected ExprSystem(String fName, Symbol systemSymbol)
     {
@@ -41,12 +41,11 @@ public abstract class ExprSystem extends ExprFunction0
         if ( obj == null )
             throw new ExprEvalException("null for system symbol: "+systemSymbol) ;
         if ( ! ( obj instanceof Node ) )
-            throw new ExprEvalException("Not a Node: "+Lib.className(obj)) ;
+            throw new ExprEvalException("ExprSystem: Not a Node: "+Lib.className(obj)) ;
         
         Node n = (Node)obj ;
-//        if ( n == null )
-//            throw new ExprEvalException("No value for system variable: "+systemSymbol) ;  
-        // NodeValue.makeNode could have a cache.
+        if ( n == null )
+            throw new ExprEvalException("No value for system variable: "+systemSymbol) ;  
         NodeValue nv = NodeValue.makeNode(n) ;
         return nv ;
     }

http://git-wip-us.apache.org/repos/asf/jena/blob/dc7f38f5/jena-arq/src/main/java/org/apache/jena/sparql/sse/builders/BuilderLib.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/sse/builders/BuilderLib.java b/jena-arq/src/main/java/org/apache/jena/sparql/sse/builders/BuilderLib.java
index a154ac2..ed7b3e8 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/sse/builders/BuilderLib.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/sse/builders/BuilderLib.java
@@ -18,168 +18,135 @@
 
 package org.apache.jena.sparql.sse.builders;
 
-import org.apache.jena.sparql.sse.Item ;
-import org.apache.jena.sparql.sse.ItemList ;
-import org.apache.jena.sparql.sse.ItemLocation ;
-
-public class BuilderLib
-{
- 
-    public static void checkNode(Item item)
-    {
-        if ( item.isNode() ) 
-            return ;
-        broken(item, "Not a node: "+item.shortString()) ;
-    }
-    
-    public static void checkSymbol(Item item)
-    {
-        if ( item.isSymbol() ) 
-            return ;
-        broken(item, "Not a symbol: "+item.shortString()) ;
-    }
-    
-    public static void checkTagged(Item item, String tag, String msg)
-    {
-        if ( item.isTagged(tag) ) return ;
-        broken(item, msg, item) ;
-    }
-
-    public static void checkTagged(Item item, int len, String tag, String msg)
-    {
-        if ( item.isTagged(tag) && item.getList().size() == len ) 
-            return ;
-        broken(item, msg, item) ;
-    }
-
-    
-    public static void checkTag(ItemList list, String tag)
-    {
+import org.apache.jena.sparql.sse.Item;
+import org.apache.jena.sparql.sse.ItemList;
+import org.apache.jena.sparql.sse.ItemLocation;
+
+public class BuilderLib {
+
+    public static void checkNode(Item item) {
+        if ( item.isNode() )
+            return;
+        broken(item, "Not a node: " + item.shortString());
+    }
+
+    public static void checkSymbol(Item item) {
+        if ( item.isSymbol() )
+            return;
+        broken(item, "Not a symbol: " + item.shortString());
+    }
+
+    public static void checkTagged(Item item, String tag, String msg) {
+        if ( item.isTagged(tag) )
+            return;
+        broken(item, msg, item);
+    }
+
+    public static void checkTagged(Item item, int len, String tag, String msg) {
+        if ( item.isTagged(tag) && item.getList().size() == len )
+            return;
+        broken(item, msg, item);
+    }
+
+    public static void checkTag(ItemList list, String tag) {
         if ( list.size() == 0 )
-            broken(list, "Empty list") ;
-        if ( ! list.get(0).isSymbolIgnoreCase(tag) )
-            broken(list, "List does not start ("+tag+ "...) : "+list.shortString()) ;
+            broken(list, "Empty list");
+        if ( !list.get(0).isSymbolIgnoreCase(tag) )
+            broken(list, "List does not start (" + tag + "...) : " + list.shortString());
     }
 
-    public static void checkList(Item item)
-    {
-        if ( item.isList() ) 
-            return ;
-        broken(item, "Not a list: "+item.shortString()) ;
+    public static void checkList(Item item) {
+        if ( item.isList() )
+            return;
+        broken(item, "Not a list: " + item.shortString());
     }
 
-    public static void checkList(Item item, String msg)
-    {
+    public static void checkList(Item item, String msg) {
         if ( item.isList() )
-            return ;
-        if ( msg == null && item.isSymbol())
-            msg = "Attempt to use a symbol where list expected: "+item.shortString() ;
-        if ( msg == null && item.isNode())
-            msg = "Attempt to use a node where list expected: "+item.shortString() ;
+            return;
+        if ( msg == null && item.isSymbol() )
+            msg = "Attempt to use a symbol where list expected: " + item.shortString();
+        if ( msg == null && item.isNode() )
+            msg = "Attempt to use a node where list expected: " + item.shortString();
         if ( msg == null )
-            msg = "Not a list" ;
-        broken(item, msg) ; 
-    }
-    
-    public static void warning(ItemLocation location, String msg)
-    {
-        msg = msg(location, msg) ;
-        System.err.println(msg) ;
+            msg = "Not a list";
+        broken(item, msg);
     }
 
-    public static void checkLength(int len1, int len2, ItemList list, String msg)
-    {
+    public static void checkLength(int len1, int len2, ItemList list, String msg) {
         if ( list.size() >= len1 && list.size() <= len2 )
-            return ; 
+            return;
         if ( msg == null )
-            msg =  "Wrong number of arguments: ("+len1+"-"+len2+")/"+list.size()+" : "+list.shortString() ;
+            msg = "Wrong number of arguments: (" + len1 + "-" + len2 + ")/" + list.size() + " : " + list.shortString();
         else
-            msg = msg+" : "+list.shortString() ;
-        broken(list, msg) ;
-    }
-    
-    
-    
-    public static void checkLength(int len, ItemList list, String msg)
-    {
+            msg = msg + " : " + list.shortString();
+        broken(list, msg);
+    }
+
+    public static void checkLength(int len, ItemList list, String msg) {
         if ( list.size() == len )
-            return ;
-        
+            return;
+
         if ( msg == null )
-            msg =  "Wrong number of arguments: "+len+"/"+list.size()+" : "+list.shortString() ;
+            msg = "Wrong number of arguments: " + len + "/" + list.size() + " : " + list.shortString();
         else
-            msg = msg+" : "+list.shortString() ;
-        broken(list, msg) ;
+            msg = msg + " : " + list.shortString();
+        broken(list, msg);
     }
 
-    public static void checkLengthAtLeast(int len, ItemList list, String msg)
-    {
-        if ( list.size()>= len )
-            return ;
-        
+    public static void checkLengthAtLeast(int len, ItemList list, String msg) {
+        if ( list.size() >= len )
+            return;
+
         if ( msg == null )
-            msg =  "Too few arguments: want > "+len+" :got : "+list.size()+" : "+list.shortString() ;
+            msg = "Too few arguments: want > " + len + " :got : " + list.size() + " : " + list.shortString();
         else
-            msg = msg+" : "+list.shortString() ;
-        broken(list, msg) ;
-    }
-    
-    public static void broken(Item item, String msg)
-    {
-        broken(item, msg, item) ;
-    }
-    
-    public static void broken(String msg)
-    {
-        System.err.println(msg) ;
-        exception(msg) ;
-    }
-    
-    public static void exception(String msg)
-    {
-        throw new ExprBuildException(msg) ;
-    }
-    
-    public static void broken(ItemLocation location, String msg, Item item)
-    {
-        msg = msg(location, msg) ;
-        System.err.println(msg+": "+item.shortString()) ;
-        exception(msg) ;
-    }
-
-    public static void broken(ItemList list, String msg)
-    {
-        broken(list, msg, list) ; 
-    }
-
-    public static void broken(ItemLocation location, String msg, ItemList list)
-    {
-        msg = msg(location, msg) ;
-        System.err.println(msg+": "+list.shortString()) ;
-        exception(msg) ;
-    }
-    
-    public static void broken(ItemLocation location, String msg)
-    {
-        msg = msg(location, msg) ;
-        System.err.println(msg) ;
-        exception(msg) ;
-    }
-    
-    public static String msg(ItemLocation location, String msg)
-    {
+            msg = msg + " : " + list.shortString();
+        broken(list, msg);
+    }
+
+    public static void broken(Item item, String msg) {
+        broken(item, msg, item);
+    }
+
+    public static void broken(String msg) {
+        exception(msg);
+    }
+
+    public static void exception(String msg) {
+        throw new ExprBuildException(msg);
+    }
+
+    public static void broken(ItemLocation location, String msg, Item item) {
+        msg = msg(location, msg);
+        exception(msg);
+    }
+
+    public static void broken(ItemList list, String msg) {
+        broken(list, msg, list);
+    }
+
+    public static void broken(ItemLocation location, String msg, ItemList list) {
+        msg = msg(location, msg);
+        exception(msg);
+    }
+
+    public static void broken(ItemLocation location, String msg) {
+        msg = msg(location, msg);
+        exception(msg);
+    }
+
+    public static String msg(ItemLocation location, String msg) {
         if ( location != null )
-            msg = location.location()+": "+msg ;
-        return msg ;
+            msg = location.location() + ": " + msg;
+        return msg;
     }
 
-    public static ItemList skipTag(ItemList list, String tag)
-    {
-        if ( list.size() > 0 )
-        {
+    public static ItemList skipTag(ItemList list, String tag) {
+        if ( list.size() > 0 ) {
             if ( list.get(0).isSymbol(tag) )
-                list = list.cdr() ;
+                list = list.cdr();
         }
-        return list ;
+        return list;
     }
 }

http://git-wip-us.apache.org/repos/asf/jena/blob/dc7f38f5/jena-arq/src/main/java/org/apache/jena/sparql/util/FmtUtils.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/util/FmtUtils.java b/jena-arq/src/main/java/org/apache/jena/sparql/util/FmtUtils.java
index 8452fbe..e9a3ada 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/util/FmtUtils.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/util/FmtUtils.java
@@ -49,7 +49,7 @@ public class FmtUtils
 {
     // OLD CODE - being replaced by riot.NodeFmtLib
     
-    // Consider withdrawing non-serialzation context forms of this.
+    // Consider withdrawing non-serialization context forms of this.
     // Or a temporary SerialzationContext does not abbreviate bNodes.
     static final String indentPrefix = "  " ;
     public static boolean multiLineExpr = false ;


[06/27] jena git commit: JENA-1585: Refactoring webapp code to separate from the core server.

Posted by an...@apache.org.
JENA-1585: Refactoring webapp code to separate from the core server.


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

Branch: refs/heads/master
Commit: 06f59125002992a13160fbc0d855ee819f035816
Parents: 6265664
Author: Andy Seaborne <an...@apache.org>
Authored: Thu Aug 23 16:32:48 2018 +0100
Committer: Andy Seaborne <an...@apache.org>
Committed: Thu Aug 23 17:15:05 2018 +0100

----------------------------------------------------------------------
 .../jena/fuseki/build/FusekiBuildLib.java       |   5 +-
 .../apache/jena/fuseki/build/FusekiConfig.java  |   3 +-
 .../apache/jena/fuseki/build/FusekiConst.java   |  41 +++
 .../org/apache/jena/fuseki/cmd/FusekiCmd.java   |   1 -
 .../org/apache/jena/fuseki/cmd/JettyFuseki.java | 325 +++++++++++++++++++
 .../apache/jena/fuseki/ctl/ActionAsyncTask.java |   1 -
 .../apache/jena/fuseki/ctl/ActionBackup.java    |  65 ----
 .../jena/fuseki/ctl/ActionBackupList.java       |  94 ------
 .../org/apache/jena/fuseki/ctl/ActionItem.java  |  46 +++
 .../org/apache/jena/fuseki/ctl/ActionStats.java |  11 +-
 .../apache/jena/fuseki/ctl/JsonDescription.java |  73 +++++
 .../org/apache/jena/fuseki/ctl/TaskBase.java    |   4 +-
 .../apache/jena/fuseki/jetty/JettyFuseki.java   | 325 -------------------
 .../apache/jena/fuseki/mgt/ActionBackup.java    |  68 ++++
 .../jena/fuseki/mgt/ActionBackupList.java       |  95 ++++++
 .../apache/jena/fuseki/mgt/ActionDatasets.java  |   7 +-
 .../org/apache/jena/fuseki/mgt/ActionItem.java  |  46 ---
 .../jena/fuseki/mgt/ActionServerStatus.java     |  12 +-
 .../apache/jena/fuseki/mgt/JsonDescription.java |  72 ----
 .../org/apache/jena/fuseki/mgt/MgtConst.java    |  52 ---
 .../java/org/apache/jena/fuseki/mgt/MgtJMX.java |  61 ----
 .../apache/jena/fuseki/mgt/ServerMgtConst.java  |  39 +++
 .../apache/jena/fuseki/mgt/ServiceMXBean.java   |  32 --
 .../apache/jena/fuseki/server/ServerConst.java  |  41 +++
 .../apache/jena/fuseki/webapp/SystemState.java  |  16 -
 .../src/main/webapp/WEB-INF/web.xml             |   4 +-
 .../src/main/webapp/js/app/controllers/.svnkeep |   0
 .../src/main/webapp/js/app/layouts/.svnkeep     |   0
 .../src/main/webapp/js/app/routers/.svnkeep     |   0
 .../src/main/webapp/js/app/views/.svnkeep       |   0
 .../java/org/apache/jena/fuseki/ServerCtl.java  |   2 +-
 .../java/org/apache/jena/fuseki/TestAdmin.java  |  19 +-
 32 files changed, 763 insertions(+), 797 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/06f59125/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiBuildLib.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiBuildLib.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiBuildLib.java
index e71edce..a964c9d 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiBuildLib.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiBuildLib.java
@@ -19,7 +19,6 @@
 package org.apache.jena.fuseki.build;
 
 import org.apache.jena.fuseki.FusekiConfigException;
-import org.apache.jena.fuseki.webapp.SystemState;
 import org.apache.jena.query.* ;
 import org.apache.jena.rdf.model.Literal ;
 import org.apache.jena.rdf.model.Model ;
@@ -55,7 +54,7 @@ public class FusekiBuildLib {
     }
 
     public static ResultSet query(String string, Model m, String varName, RDFNode value) {
-        Query query = QueryFactory.create(SystemState.PREFIXES + string) ;
+        Query query = QueryFactory.create(FusekiConst.PREFIXES + string) ;
         QuerySolutionMap initValues = null ;
         if ( varName != null )
             initValues = querySolution(varName, value) ;
@@ -65,7 +64,7 @@ public class FusekiBuildLib {
     }
 
     public static ResultSet query(String string, Dataset ds, String varName, RDFNode value) {
-        Query query = QueryFactory.create(SystemState.PREFIXES + string) ;
+        Query query = QueryFactory.create(FusekiConst.PREFIXES + string) ;
         QuerySolutionMap initValues = null ;
         if ( varName != null )
             initValues = querySolution(varName, value) ;

http://git-wip-us.apache.org/repos/asf/jena/blob/06f59125/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiConfig.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiConfig.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiConfig.java
index 7919099..9673cfa 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiConfig.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiConfig.java
@@ -38,7 +38,6 @@ import org.apache.jena.atlas.lib.StrUtils ;
 import org.apache.jena.fuseki.Fuseki ;
 import org.apache.jena.fuseki.FusekiConfigException ;
 import org.apache.jena.fuseki.server.* ;
-import org.apache.jena.fuseki.webapp.SystemState;
 import org.apache.jena.query.Dataset ;
 import org.apache.jena.query.QuerySolution ;
 import org.apache.jena.query.ReadWrite ;
@@ -202,7 +201,7 @@ public class FusekiConfig {
     public static List<DataAccessPoint> readSystemDatabase(Dataset ds) {
         DatasetDescriptionRegistry dsDescMap = new DatasetDescriptionRegistry() ;
         String qs = StrUtils.strjoinNL
-            (SystemState.PREFIXES ,
+            (FusekiConst.PREFIXES ,
              "SELECT * {" ,
              "  GRAPH ?g {",
              "     ?s fu:name ?name ;" ,

http://git-wip-us.apache.org/repos/asf/jena/blob/06f59125/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiConst.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiConst.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiConst.java
new file mode 100644
index 0000000..3e75555
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiConst.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.fuseki.build;
+
+import org.apache.jena.atlas.lib.StrUtils;
+
+/** Internal constants */
+public class FusekiConst {
+
+    public static String PREFIXES = StrUtils.strjoinNL
+    ("BASE            <http://example/base#>",
+     "PREFIX ja:      <http://jena.hpl.hp.com/2005/11/Assembler#>",
+     "PREFIX fu:      <http://jena.apache.org/fuseki#>",
+     "PREFIX fuseki:  <http://jena.apache.org/fuseki#>",
+     "PREFIX rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#>",
+     "PREFIX rdfs:    <http://www.w3.org/2000/01/rdf-schema#>",
+     "PREFIX tdb:     <http://jena.hpl.hp.com/2008/tdb#>",
+     "PREFIX sdb:     <http://jena.hpl.hp.com/2007/sdb#>",
+     "PREFIX list:    <http://jena.apache.org/ARQ/list#>",
+     "PREFIX xsd:     <http://www.w3.org/2001/XMLSchema#>",
+     "PREFIX apf:     <http://jena.apache.org/ARQ/property#>",
+     "PREFIX afn:     <http://jena.apache.org/ARQ/function#>",
+     "") ;
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/06f59125/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/cmd/FusekiCmd.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/cmd/FusekiCmd.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/cmd/FusekiCmd.java
index 9fd5075..e62b118 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/cmd/FusekiCmd.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/cmd/FusekiCmd.java
@@ -30,7 +30,6 @@ import org.apache.jena.atlas.lib.FileOps ;
 import org.apache.jena.fuseki.Fuseki ;
 import org.apache.jena.fuseki.FusekiException;
 import org.apache.jena.fuseki.build.Template ;
-import org.apache.jena.fuseki.jetty.JettyFuseki ;
 import org.apache.jena.fuseki.jetty.JettyServerConfig ;
 import org.apache.jena.fuseki.server.FusekiInitialConfig ;
 import org.apache.jena.fuseki.system.FusekiLogging;

http://git-wip-us.apache.org/repos/asf/jena/blob/06f59125/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/cmd/JettyFuseki.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/cmd/JettyFuseki.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/cmd/JettyFuseki.java
new file mode 100644
index 0000000..29608d0
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/cmd/JettyFuseki.java
@@ -0,0 +1,325 @@
+/*
+ * 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.fuseki.cmd ;
+
+import static java.lang.String.format ;
+import static org.apache.jena.fuseki.Fuseki.serverLog ;
+
+import java.io.FileInputStream ;
+
+import javax.servlet.ServletContext ;
+
+import org.apache.jena.atlas.lib.DateTimeUtils ;
+import org.apache.jena.atlas.lib.FileOps ;
+import org.apache.jena.fuseki.Fuseki ;
+import org.apache.jena.fuseki.FusekiException ;
+import org.apache.jena.fuseki.jetty.FusekiErrorHandler;
+import org.apache.jena.fuseki.jetty.JettyServerConfig;
+import org.apache.jena.fuseki.server.DataAccessPointRegistry ;
+import org.apache.jena.fuseki.webapp.FusekiEnv;
+import org.eclipse.jetty.security.* ;
+import org.eclipse.jetty.security.authentication.BasicAuthenticator ;
+import org.eclipse.jetty.server.HttpConnectionFactory ;
+import org.eclipse.jetty.server.Server ;
+import org.eclipse.jetty.server.ServerConnector ;
+import org.eclipse.jetty.server.handler.AllowSymLinkAliasChecker ;
+import org.eclipse.jetty.server.handler.ContextHandler ;
+import org.eclipse.jetty.server.handler.gzip.GzipHandler ;
+import org.eclipse.jetty.servlet.ServletContextHandler ;
+import org.eclipse.jetty.util.security.Constraint ;
+import org.eclipse.jetty.webapp.WebAppContext ;
+import org.eclipse.jetty.xml.XmlConfiguration ;
+
+/** Standalone full server, not run as a WAR file.
+ * Used in testing and development.
+ * 
+ * SPARQLServer is the Jena server instance which wraps/utilizes 
+ * {@link org.eclipse.jetty.server.Server}. This class provides
+ * immediate access to the {@link org.eclipse.jetty.server.Server#start()} and 
+ * {@link org.eclipse.jetty.server.Server#stop()} commands as well as obtaining
+ * instances of the server and server configuration. Finally we can obtain 
+ * instances of {@link org.apache.jena.fuseki.jetty.JettyServerConfig}.
+ */
+public class JettyFuseki {
+    // Jetty specific.
+    // This class is becoming less important - it now sets up a Jetty server for in-process use
+    // either for the command line in development  
+    // and in testing but not direct webapp deployments. 
+    static { Fuseki.init() ; }
+
+    public static JettyFuseki  instance    = null ;
+
+    private ServerConnector serverConnector = null ;
+    // If a separate ...
+    private ServerConnector mgtConnector    = null ;
+    
+    private JettyServerConfig serverConfig ;
+
+    // The jetty server.
+    
+    private Server              server         = null ;
+    private ServletContext      servletContext = null ;
+    
+    // webapp setup - standard maven layout
+    public static       String contextpath     = "/" ;
+    // Standalone jar
+    public static final String resourceBase1   = "webapp" ;
+    // Development
+    public static final String resourceBase2   = "src/main/webapp" ;
+
+    /**
+     * Default setup which requires a {@link org.apache.jena.fuseki.jetty.JettyServerConfig}
+     * object as input.  We use this config to pass in the command line arguments for dataset, 
+     * name etc. 
+     * @param config
+     */
+    
+    public static void initializeServer(JettyServerConfig config) {
+        instance = new JettyFuseki(config) ;
+    }
+    
+    private JettyFuseki(JettyServerConfig config) {
+        this.serverConfig = config ;
+        buildServerWebapp(serverConfig.contextPath, serverConfig.jettyConfigFile) ;
+        if ( mgtConnector == null )
+            mgtConnector = serverConnector ;
+
+        if ( config.enableCompression ) {
+            GzipHandler gzipHandler = new GzipHandler();
+            gzipHandler.setHandler(server.getHandler());
+            server.setHandler(gzipHandler); 
+        }
+    }
+
+    /**
+     * Initialize the {@link JettyFuseki} instance.
+     */
+    public void start() {
+        
+        String version = Fuseki.VERSION ;
+        String buildDate = Fuseki.BUILD_DATE ;
+        
+        if ( version != null && version.equals("${project.version}") )
+            version = null ;
+        if ( buildDate != null && buildDate.equals("${build.time.xsd}") )
+            buildDate = DateTimeUtils.nowAsXSDDateTimeString() ;
+        
+        if ( version != null ) {
+            if ( Fuseki.developmentMode && buildDate != null )
+                serverLog.info(format("%s %s %s", Fuseki.NAME, version, buildDate)) ;
+            else
+                serverLog.info(format("%s %s", Fuseki.NAME, version)) ;
+        }
+        // This does not get set usefully for Jetty as we use it.
+        // String jettyVersion = org.eclipse.jetty.server.Server.getVersion() ;
+        // serverLog.info(format("Jetty %s",jettyVersion)) ;
+        
+        String host = serverConnector.getHost() ;
+        if ( host != null )
+            serverLog.info("Incoming connections limited to " + host) ;
+
+        try {
+            server.start() ;
+        } catch (java.net.BindException ex) {
+            serverLog.error("SPARQLServer (port="+serverConnector.getPort()+"): Failed to start server: " + ex.getMessage()) ;
+            throw new FusekiException("BindException: port="+serverConnector.getPort()+": Failed to start server: " + ex.getMessage(), ex) ;
+        } catch (Exception ex) {
+            serverLog.error("SPARQLServer: Failed to start server: " + ex.getMessage(), ex) ;
+            throw new FusekiException("Failed to start server: " + ex.getMessage(), ex) ;
+        }
+        String now = DateTimeUtils.nowAsString() ;
+        serverLog.info(format("Started %s on port %d", now, serverConnector.getPort())) ;
+    }
+
+    /**
+     * Sync with the {@link JettyFuseki} instance.
+     * Returns only if the server exits cleanly 
+     */
+    public void join() {
+        try {
+            server.join() ;
+        } catch (InterruptedException ex) { }
+    }
+
+        /**
+     * Stop the {@link JettyFuseki} instance.
+     */
+    public void stop() {
+        String now = DateTimeUtils.nowAsString() ;
+        serverLog.info(format("Stopped %s on port %d", now, serverConnector.getPort())) ;
+        try {
+            server.stop() ;
+        } catch (Exception ex) {
+            Fuseki.serverLog.warn("SPARQLServer: Exception while stopping server: " + ex.getMessage(), ex) ;
+        }
+    }
+
+    public static WebAppContext createWebApp(String contextPath) {
+        FusekiEnv.setEnvironment();
+        WebAppContext webapp = new WebAppContext();
+        webapp.getServletContext().getContextHandler().setMaxFormContentSize(10 * 1000 * 1000) ;
+
+        // Hunt for the webapp for the standalone jar (or development system). 
+        // Note that Path FUSEKI_HOME is not initialized until the webapp starts
+        // so it is not available here.
+
+        String resourceBase3 = null ;
+        String resourceBase4 = null ;
+        if ( FusekiEnv.FUSEKI_HOME != null ) {
+            String HOME = FusekiEnv.FUSEKI_HOME.toString() ;
+            resourceBase3 = HOME+"/"+resourceBase1 ;
+            resourceBase4 = HOME+"/"+resourceBase2 ;
+        }
+
+        String resourceBase = tryResourceBase(resourceBase1, null) ;
+        resourceBase = tryResourceBase(resourceBase2, resourceBase) ;
+        resourceBase = tryResourceBase(resourceBase3, resourceBase) ;
+        resourceBase = tryResourceBase(resourceBase4, resourceBase) ;
+
+        if ( resourceBase == null ) {
+            if ( resourceBase3 == null )
+                Fuseki.serverLog.error("Can't find resourceBase (tried "+resourceBase1+" and "+resourceBase2+")") ;
+            else
+                Fuseki.serverLog.error("Can't find resourceBase (tried "+resourceBase1+", "+resourceBase2+", "+resourceBase3+" and "+resourceBase4+")") ;
+            Fuseki.serverLog.error("Failed to start") ;
+            throw new FusekiException("Failed to start") ;
+        }
+
+        webapp.setDescriptor(resourceBase+"/WEB-INF/web.xml");
+        webapp.setResourceBase(resourceBase);
+        webapp.setContextPath(contextPath);
+
+        //-- Jetty setup for the ServletContext logger.
+        // The name of the Jetty-allocated slf4j/log4j logger is
+        // the display name or, if null, the context path name.   
+        // It is set, without checking for a previous call of setLogger in "doStart"
+        // which happens during server startup. 
+        // This the name of the ServletContext logger as well
+        webapp.setDisplayName(Fuseki.servletRequestLogName);  
+        webapp.setParentLoaderPriority(true);  // Normal Java classloader behaviour.
+        webapp.setErrorHandler(new FusekiErrorHandler()) ;
+        return webapp ;
+    }
+
+    public static String getenv(String name) {
+        String x = System.getenv(name) ;
+        if ( x == null )
+            x = System.getProperty(name) ;
+        return x ;
+    }
+    
+    public DataAccessPointRegistry getDataAccessPointRegistry() {
+        return DataAccessPointRegistry.get(servletContext) ;
+    }
+
+    private static String tryResourceBase(String maybeResourceBase, String currentResourceBase) {
+        if ( currentResourceBase != null )
+            return currentResourceBase ;
+        if ( maybeResourceBase != null && FileOps.exists(maybeResourceBase) )
+            return maybeResourceBase ;
+        return currentResourceBase ;
+    }
+    
+    private void buildServerWebapp(String contextPath, String jettyConfig) {
+        if ( jettyConfig != null )
+            // --jetty-config=jetty-fuseki.xml
+            // for detailed configuration of the server using Jetty features.
+            configServer(jettyConfig) ;
+        else
+            defaultServerConfig(serverConfig.port, serverConfig.loopback) ;
+
+        WebAppContext webapp = createWebApp(contextPath) ;
+        if ( false /*enable symbolic links */ ) {
+            // See http://www.eclipse.org/jetty/documentation/current/serving-aliased-files.html
+            // Record what would be needed:
+            // 1 - Allow all symbolic links without checking
+            webapp.addAliasCheck(new ContextHandler.ApproveAliases());
+            // 2 - Check links are to valid resources. But default for Unix?
+            webapp.addAliasCheck(new AllowSymLinkAliasChecker()) ;
+        }
+        servletContext = webapp.getServletContext() ;
+        server.setHandler(webapp) ;
+        // Replaced by Shiro.
+        if ( jettyConfig == null && serverConfig.authConfigFile != null )
+            security(webapp, serverConfig.authConfigFile) ;
+    }
+    
+    // This is now provided by Shiro.
+    private static void security(ServletContextHandler context, String authfile) {
+        Constraint constraint = new Constraint() ;
+        constraint.setName(Constraint.__BASIC_AUTH) ;
+        constraint.setRoles(new String[]{"fuseki"}) ;
+        constraint.setAuthenticate(true) ;
+
+        ConstraintMapping mapping = new ConstraintMapping() ;
+        mapping.setConstraint(constraint) ;
+        mapping.setPathSpec("/*") ;
+
+        IdentityService identService = new DefaultIdentityService() ;
+
+        ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler() ;
+        securityHandler.addConstraintMapping(mapping) ;
+        securityHandler.setIdentityService(identService) ;
+
+        HashLoginService loginService = new HashLoginService("Fuseki Authentication", authfile) ;
+        loginService.setIdentityService(identService) ;
+
+        securityHandler.setLoginService(loginService) ;
+        securityHandler.setAuthenticator(new BasicAuthenticator()) ;
+
+        context.setSecurityHandler(securityHandler) ;
+
+        serverLog.debug("Basic Auth Configuration = " + authfile) ;
+    }
+
+    private void configServer(String jettyConfig) {
+        try {
+            serverLog.info("Jetty server config file = " + jettyConfig) ;
+            server = new Server() ;
+            XmlConfiguration configuration = new XmlConfiguration(new FileInputStream(jettyConfig)) ;
+            configuration.configure(server) ;
+            serverConnector = (ServerConnector)server.getConnectors()[0] ;
+        } catch (Exception ex) {
+            serverLog.error("SPARQLServer: Failed to configure server: " + ex.getMessage(), ex) ;
+            throw new FusekiException("Failed to configure a server using configuration file '" + jettyConfig + "'") ;
+        }
+    }
+
+    private void defaultServerConfig(int port, boolean loopback) {
+        server = new Server() ;
+        HttpConnectionFactory f1 = new HttpConnectionFactory() ;
+        // Some people do try very large operations ... really, should use POST.
+        f1.getHttpConfiguration().setRequestHeaderSize(512 * 1024);
+        f1.getHttpConfiguration().setOutputBufferSize(5 * 1024 * 1024) ;
+        // Do not add "Server: Jetty(....) when not a development system.
+        if ( ! Fuseki.outputJettyServerHeader )
+            f1.getHttpConfiguration().setSendServerVersion(false) ;
+
+        // https is better done with a Jetty configuration file
+        // because there are several things to configure. 
+        // See "examples/fuseki-jetty-https.xml"
+
+        ServerConnector connector = new ServerConnector(server, f1) ;
+        connector.setPort(port) ;
+        server.addConnector(connector);
+        if ( loopback )
+            connector.setHost("localhost");
+        serverConnector = connector ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/06f59125/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionAsyncTask.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionAsyncTask.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionAsyncTask.java
index 5c3ec01..4310c6f 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionAsyncTask.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionAsyncTask.java
@@ -22,7 +22,6 @@ import org.apache.jena.atlas.json.JsonValue ;
 import org.apache.jena.atlas.lib.InternalErrorException ;
 import org.apache.jena.fuseki.async.AsyncPool ;
 import org.apache.jena.fuseki.async.AsyncTask ;
-import org.apache.jena.fuseki.mgt.ActionItem;
 import org.apache.jena.fuseki.servlets.HttpAction ;
 import org.apache.jena.fuseki.servlets.ServletOps ;
 

http://git-wip-us.apache.org/repos/asf/jena/blob/06f59125/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionBackup.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionBackup.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionBackup.java
deleted file mode 100644
index b83c736..0000000
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionBackup.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.jena.fuseki.ctl;
-
-import static java.lang.String.format ;
-
-import org.apache.jena.fuseki.servlets.HttpAction ;
-import org.apache.jena.fuseki.servlets.ServletOps ;
-import org.slf4j.Logger ;
-import org.slf4j.LoggerFactory ;
-
-public class ActionBackup extends ActionAsyncTask
-{
-    public ActionBackup() { super() ; }
-
-    @Override
-    protected Runnable createRunnable(HttpAction action) {
-        String name = action.getDatasetName() ;
-        if ( name == null ) {
-            action.log.error("Null for dataset name in item request") ;  
-            ServletOps.errorOccurred("Null for dataset name in item request");
-            return null ;
-        }
-        
-        action.log.info(format("[%d] Backup dataset %s", action.id, name)) ;
-        return new BackupTask(action) ;
-    }
-
-    static class BackupTask extends TaskBase {
-        static private Logger log = LoggerFactory.getLogger("Backup") ;
-        
-        public BackupTask(HttpAction action) {
-            super(action) ;
-        }
-
-        @Override
-        public void run() {
-            try {
-                String backupFilename = Backup.chooseFileName(datasetName) ;
-                log.info(format("[%d] >>>> Start backup %s -> %s", actionId, datasetName, backupFilename)) ;
-                Backup.backup(transactional, dataset, backupFilename) ;
-                log.info(format("[%d] <<<< Finish backup %s -> %s", actionId, datasetName, backupFilename)) ;
-            } catch (Exception ex) {
-                log.info(format("[%d] **** Exception in backup", actionId), ex) ;
-            }
-        }
-    }
-}
-    
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jena/blob/06f59125/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionBackupList.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionBackupList.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionBackupList.java
deleted file mode 100644
index 509408e..0000000
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionBackupList.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.jena.fuseki.ctl;
-
-import static java.lang.String.format ;
-
-import java.io.File ;
-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.List ;
-import java.util.stream.Collectors ;
-
-import javax.servlet.http.HttpServletRequest ;
-import javax.servlet.http.HttpServletResponse ;
-
-import org.apache.jena.atlas.json.JsonBuilder ;
-import org.apache.jena.atlas.json.JsonValue ;
-import org.apache.jena.fuseki.servlets.HttpAction ;
-import org.apache.jena.fuseki.servlets.ServletOps ;
-import org.apache.jena.fuseki.webapp.FusekiSystem;
-
-/**
- * A JSON API to list all the backups in the backup directory
- */
-public class ActionBackupList extends ActionCtl {
-
-    @Override
-    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
-        doCommon(req, resp);
-    }
-
-    @Override
-    protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
-        doCommon(req, resp);
-    }
-
-    @Override
-    protected void perform(HttpAction action) {
-        JsonValue result = description(action) ;
-        ServletOps.setNoCache(action.response) ;
-        ServletOps.sendJsonReponse(action, result);
-    }
-        
-    private static DirectoryStream.Filter<Path> filterVisibleFiles = (entry) -> {
-        File f = entry.toFile() ;
-        return f.isFile() && !f.isHidden() ;
-    } ;
-
-    private JsonValue description(HttpAction action) {
-        if ( ! Files.isDirectory(FusekiSystem.dirBackups) )
-            ServletOps.errorOccurred(format("[%d] Backup area '%s' is not a directory", action.id, FusekiSystem.dirBackups)) ;
-        
-        List<Path> paths = new ArrayList<>() ;
-        try (DirectoryStream<Path> stream = Files.newDirectoryStream(FusekiSystem.dirBackups, filterVisibleFiles)) {
-            stream.forEach(paths::add) ;
-        } catch (IOException ex) {
-            action.log.error(format("[%d] Backup file list :: IOException :: %s", action.id, ex.getMessage())) ;
-            ServletOps.errorOccurred(ex);
-        }
-
-        List<String> fileNames = paths.stream().map((p)->p.getFileName().toString()).sorted().collect(Collectors.toList()) ;
-
-        JsonBuilder builder = new JsonBuilder() ;
-        builder.startObject("top") ;
-        builder.key("backups") ;
-
-        builder.startArray() ;
-        fileNames.forEach(builder::value) ;
-        builder.finishArray() ;
-
-        builder.finishObject("top") ;
-        return builder.build() ; 
-        
-    }
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/06f59125/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionItem.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionItem.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionItem.java
new file mode 100644
index 0000000..ea015e5
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionItem.java
@@ -0,0 +1,46 @@
+/**
+ * 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.fuseki.ctl;
+
+import org.apache.jena.atlas.json.JsonValue ;
+import org.apache.jena.fuseki.ctl.ActionContainerItem;
+import org.apache.jena.fuseki.servlets.HttpAction;
+import org.apache.jena.fuseki.servlets.ServletOps;
+import org.apache.jena.web.HttpSC ;
+
+/** Action on items in a container, but not the container itself */ 
+public abstract class ActionItem extends ActionContainerItem
+{
+    public ActionItem() { super() ; }
+    
+    @Override
+    final
+    protected JsonValue execGetContainer(HttpAction action) {
+        ServletOps.error(HttpSC.METHOD_NOT_ALLOWED_405) ;
+        return null ;
+    }
+
+    @Override
+    final
+    protected JsonValue execPostContainer(HttpAction action) {
+        ServletOps.error(HttpSC.METHOD_NOT_ALLOWED_405) ;
+        return null ;
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/06f59125/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionStats.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionStats.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionStats.java
index dacd151..f9ab32f 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionStats.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionStats.java
@@ -32,7 +32,6 @@ import javax.servlet.http.HttpServletResponse ;
 import org.apache.jena.atlas.json.JsonBuilder ;
 import org.apache.jena.atlas.json.JsonObject ;
 import org.apache.jena.atlas.json.JsonValue ;
-import org.apache.jena.fuseki.mgt.MgtConst;
 import org.apache.jena.fuseki.server.* ;
 import org.apache.jena.fuseki.servlets.HttpAction ;
 
@@ -50,7 +49,7 @@ public class ActionStats extends ActionContainerItem
     public static JsonObject generateStats(DataAccessPointRegistry registry) {
         JsonBuilder builder = new JsonBuilder() ;
         builder.startObject("top") ;
-        builder.key(MgtConst.datasets) ;
+        builder.key(ServerConst.datasets) ;
         builder.startObject("datasets") ;
         registry.forEach((name, access)->statsDataset(builder, access));
         builder.finishObject("datasets") ;
@@ -66,7 +65,7 @@ public class ActionStats extends ActionContainerItem
         String datasetPath = DataAccessPoint.canonical(action.getDatasetName()) ;
         builder.startObject("TOP") ;
         
-        builder.key(MgtConst.datasets) ;
+        builder.key(ServerConst.datasets) ;
         builder.startObject("datasets") ;
         statsDataset(builder, datasetPath, action.getDataAccessPointRegistry()) ;
         builder.finishObject("datasets") ;
@@ -96,7 +95,7 @@ public class ActionStats extends ActionContainerItem
         builder.key(CounterName.RequestsGood.getName()).value(dSrv.getCounters().value(CounterName.RequestsGood)) ;
         builder.key(CounterName.RequestsBad.getName()).value(dSrv.getCounters().value(CounterName.RequestsBad)) ;
         
-        builder.key(MgtConst.endpoints).startObject("endpoints") ;
+        builder.key(ServerConst.endpoints).startObject("endpoints") ;
         
         for ( Operation operName : dSrv.getOperations() ) {
             List<Endpoint> endpoints = access.getDataService().getEndpoints(operName) ;
@@ -107,8 +106,8 @@ public class ActionStats extends ActionContainerItem
                 builder.startObject() ;
                 
                 operationCounters(builder, endpoint);
-                builder.key(MgtConst.operation).value(operName.getName()) ;
-                builder.key(MgtConst.description).value(operName.getDescription());
+                builder.key(ServerConst.operation).value(operName.getName()) ;
+                builder.key(ServerConst.description).value(operName.getDescription());
                 
                 builder.finishObject() ;
             }

http://git-wip-us.apache.org/repos/asf/jena/blob/06f59125/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/JsonDescription.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/JsonDescription.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/JsonDescription.java
new file mode 100644
index 0000000..d4d4ad5
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/JsonDescription.java
@@ -0,0 +1,73 @@
+/**
+ * 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.fuseki.ctl;
+
+import java.util.List ;
+
+import org.apache.jena.atlas.json.JsonBuilder ;
+import org.apache.jena.fuseki.server.DataAccessPoint ;
+import org.apache.jena.fuseki.server.DataAccessPointRegistry ;
+import org.apache.jena.fuseki.server.Endpoint ;
+import org.apache.jena.fuseki.server.Operation ;
+import org.apache.jena.fuseki.server.ServerConst;
+
+/** Create a description of a service */
+public class JsonDescription {
+    
+    public static void arrayDatasets(JsonBuilder builder, DataAccessPointRegistry registry) {
+        builder.startArray() ;
+        for ( String ds : registry.keys() ) {
+            DataAccessPoint access = registry.get(ds) ;
+            JsonDescription.describe(builder, access) ;
+        }
+        builder.finishArray() ;
+    }
+    
+    public static void describe(JsonBuilder builder, DataAccessPoint access) {
+        builder.startObject() ;
+        builder.key(ServerConst.dsName).value(access.getName()) ;
+        
+        builder.key(ServerConst.dsState).value(access.getDataService().isAcceptingRequests()) ;
+        
+        builder.key(ServerConst.dsService) ;
+        builder.startArray() ;
+        
+        for ( Operation operation : access.getDataService().getOperations() ) {
+            List<Endpoint> endpoints = access.getDataService().getEndpoints(operation) ;
+            describe(builder, operation, endpoints) ;
+        }
+        builder.finishArray() ;
+        builder.finishObject() ;
+    }
+    
+    private static void describe(JsonBuilder builder, Operation operation, List<Endpoint> endpoints) {
+        builder.startObject() ;
+        
+        builder.key(ServerConst.srvType).value(operation.getName()) ;
+        builder.key(ServerConst.srvDescription).value(operation.getDescription()) ;
+        builder.key(ServerConst.srvEndpoints) ;
+        builder.startArray() ;
+        for ( Endpoint endpoint : endpoints )
+            builder.value(endpoint.getEndpoint()) ;
+        builder.finishArray() ;
+
+        builder.finishObject() ;
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/06f59125/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/TaskBase.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/TaskBase.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/TaskBase.java
index f885839..8b73a98 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/TaskBase.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/TaskBase.java
@@ -22,10 +22,10 @@ import org.apache.jena.fuseki.servlets.HttpAction ;
 import org.apache.jena.sparql.core.DatasetGraph ;
 import org.apache.jena.sparql.core.Transactional ;
 
-/** Base of async tasks - this caries some useful information around, leaving the
+/** Base of async tasks - this carries some useful information around, leaving the
  * implementation of Callable.run() to the specific task.
  */
-abstract class TaskBase implements Runnable {
+public abstract class TaskBase implements Runnable {
     public final long actionId ;
     public final DatasetGraph dataset ;
     public final String datasetName ;

http://git-wip-us.apache.org/repos/asf/jena/blob/06f59125/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/jetty/JettyFuseki.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/jetty/JettyFuseki.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/jetty/JettyFuseki.java
deleted file mode 100644
index aa1340b..0000000
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/jetty/JettyFuseki.java
+++ /dev/null
@@ -1,325 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.jena.fuseki.jetty ;
-
-import static java.lang.String.format ;
-import static org.apache.jena.fuseki.Fuseki.serverLog ;
-
-import java.io.FileInputStream ;
-
-import javax.servlet.ServletContext ;
-
-import org.apache.jena.atlas.lib.DateTimeUtils ;
-import org.apache.jena.atlas.lib.FileOps ;
-import org.apache.jena.fuseki.Fuseki ;
-import org.apache.jena.fuseki.FusekiException ;
-import org.apache.jena.fuseki.mgt.MgtJMX ;
-import org.apache.jena.fuseki.server.DataAccessPointRegistry ;
-import org.apache.jena.fuseki.webapp.FusekiEnv;
-import org.eclipse.jetty.security.* ;
-import org.eclipse.jetty.security.authentication.BasicAuthenticator ;
-import org.eclipse.jetty.server.HttpConnectionFactory ;
-import org.eclipse.jetty.server.Server ;
-import org.eclipse.jetty.server.ServerConnector ;
-import org.eclipse.jetty.server.handler.AllowSymLinkAliasChecker ;
-import org.eclipse.jetty.server.handler.ContextHandler ;
-import org.eclipse.jetty.server.handler.gzip.GzipHandler ;
-import org.eclipse.jetty.servlet.ServletContextHandler ;
-import org.eclipse.jetty.util.security.Constraint ;
-import org.eclipse.jetty.webapp.WebAppContext ;
-import org.eclipse.jetty.xml.XmlConfiguration ;
-
-/** Standalone server, not run as a WAR file.
- * Used in testing and development.
- * 
- * SPARQLServer is the Jena server instance which wraps/utilizes 
- * {@link org.eclipse.jetty.server.Server}. This class provides
- * immediate access to the {@link org.eclipse.jetty.server.Server#start()} and 
- * {@link org.eclipse.jetty.server.Server#stop()} commands as well as obtaining
- * instances of the server and server configuration. Finally we can obtain 
- * instances of {@link org.apache.jena.fuseki.jetty.JettyServerConfig}.
- */
-public class JettyFuseki {
-    // Jetty specific.
-    // This class is becoming less important - it now sets up a Jetty server for in-process use
-    // either for the command line in development  
-    // and in testing but not direct webapp deployments. 
-    static { Fuseki.init() ; }
-
-    public static JettyFuseki  instance    = null ;
-
-    private ServerConnector serverConnector = null ;
-    // If a separate ...
-    private ServerConnector mgtConnector    = null ;
-    
-    private JettyServerConfig serverConfig ;
-
-    // The jetty server.
-    
-    private Server              server         = null ;
-    private ServletContext      servletContext = null ;
-    
-    // webapp setup - standard maven layout
-    public static       String contextpath     = "/" ;
-    // Standalone jar
-    public static final String resourceBase1   = "webapp" ;
-    // Development
-    public static final String resourceBase2   = "src/main/webapp" ;
-
-    /**
-     * Default setup which requires a {@link org.apache.jena.fuseki.jetty.JettyServerConfig}
-     * object as input.  We use this config to pass in the command line arguments for dataset, 
-     * name etc. 
-     * @param config
-     */
-    
-    public static void initializeServer(JettyServerConfig config) {
-        instance = new JettyFuseki(config) ;
-    }
-    
-    private JettyFuseki(JettyServerConfig config) {
-        this.serverConfig = config ;
-        buildServerWebapp(serverConfig.contextPath, serverConfig.jettyConfigFile) ;
-        if ( mgtConnector == null )
-            mgtConnector = serverConnector ;
-
-        if ( config.enableCompression ) {
-            GzipHandler gzipHandler = new GzipHandler();
-            gzipHandler.setHandler(server.getHandler());
-            server.setHandler(gzipHandler); 
-        }
-    }
-
-    /**
-     * Initialize the {@link JettyFuseki} instance.
-     */
-    public void start() {
-        
-        String version = Fuseki.VERSION ;
-        String buildDate = Fuseki.BUILD_DATE ;
-        
-        if ( version != null && version.equals("${project.version}") )
-            version = null ;
-        if ( buildDate != null && buildDate.equals("${build.time.xsd}") )
-            buildDate = DateTimeUtils.nowAsXSDDateTimeString() ;
-        
-        if ( version != null ) {
-            if ( Fuseki.developmentMode && buildDate != null )
-                serverLog.info(format("%s %s %s", Fuseki.NAME, version, buildDate)) ;
-            else
-                serverLog.info(format("%s %s", Fuseki.NAME, version)) ;
-        }
-        // This does not get set usefully for Jetty as we use it.
-        // String jettyVersion = org.eclipse.jetty.server.Server.getVersion() ;
-        // serverLog.info(format("Jetty %s",jettyVersion)) ;
-        
-        String host = serverConnector.getHost() ;
-        if ( host != null )
-            serverLog.info("Incoming connections limited to " + host) ;
-
-        try {
-            server.start() ;
-        } catch (java.net.BindException ex) {
-            serverLog.error("SPARQLServer (port="+serverConnector.getPort()+"): Failed to start server: " + ex.getMessage()) ;
-            throw new FusekiException("BindException: port="+serverConnector.getPort()+": Failed to start server: " + ex.getMessage(), ex) ;
-        } catch (Exception ex) {
-            serverLog.error("SPARQLServer: Failed to start server: " + ex.getMessage(), ex) ;
-            throw new FusekiException("Failed to start server: " + ex.getMessage(), ex) ;
-        }
-        String now = DateTimeUtils.nowAsString() ;
-        serverLog.info(format("Started %s on port %d", now, serverConnector.getPort())) ;
-    }
-
-    /**
-     * Sync with the {@link JettyFuseki} instance.
-     * Returns only if the server exits cleanly 
-     */
-    public void join() {
-        try {
-            server.join() ;
-        } catch (InterruptedException ex) { }
-    }
-
-        /**
-     * Stop the {@link JettyFuseki} instance.
-     */
-    public void stop() {
-        String now = DateTimeUtils.nowAsString() ;
-        serverLog.info(format("Stopped %s on port %d", now, serverConnector.getPort())) ;
-        try {
-            server.stop() ;
-        } catch (Exception ex) {
-            Fuseki.serverLog.warn("SPARQLServer: Exception while stopping server: " + ex.getMessage(), ex) ;
-        }
-        MgtJMX.removeJMX() ;
-    }
-
-    public static WebAppContext createWebApp(String contextPath) {
-        FusekiEnv.setEnvironment();
-        WebAppContext webapp = new WebAppContext();
-        webapp.getServletContext().getContextHandler().setMaxFormContentSize(10 * 1000 * 1000) ;
-
-        // Hunt for the webapp for the standalone jar (or development system). 
-        // Note that Path FUSEKI_HOME is not initialized until the webapp starts
-        // so it is not available here.
-
-        String resourceBase3 = null ;
-        String resourceBase4 = null ;
-        if ( FusekiEnv.FUSEKI_HOME != null ) {
-            String HOME = FusekiEnv.FUSEKI_HOME.toString() ;
-            resourceBase3 = HOME+"/"+resourceBase1 ;
-            resourceBase4 = HOME+"/"+resourceBase2 ;
-        }
-
-        String resourceBase = tryResourceBase(resourceBase1, null) ;
-        resourceBase = tryResourceBase(resourceBase2, resourceBase) ;
-        resourceBase = tryResourceBase(resourceBase3, resourceBase) ;
-        resourceBase = tryResourceBase(resourceBase4, resourceBase) ;
-
-        if ( resourceBase == null ) {
-            if ( resourceBase3 == null )
-                Fuseki.serverLog.error("Can't find resourceBase (tried "+resourceBase1+" and "+resourceBase2+")") ;
-            else
-                Fuseki.serverLog.error("Can't find resourceBase (tried "+resourceBase1+", "+resourceBase2+", "+resourceBase3+" and "+resourceBase4+")") ;
-            Fuseki.serverLog.error("Failed to start") ;
-            throw new FusekiException("Failed to start") ;
-        }
-
-        webapp.setDescriptor(resourceBase+"/WEB-INF/web.xml");
-        webapp.setResourceBase(resourceBase);
-        webapp.setContextPath(contextPath);
-
-        //-- Jetty setup for the ServletContext logger.
-        // The name of the Jetty-allocated slf4j/log4j logger is
-        // the display name or, if null, the context path name.   
-        // It is set, without checking for a previous call of setLogger in "doStart"
-        // which happens during server startup. 
-        // This the name of the ServletContext logger as well
-        webapp.setDisplayName(Fuseki.servletRequestLogName);  
-        webapp.setParentLoaderPriority(true);  // Normal Java classloader behaviour.
-        webapp.setErrorHandler(new FusekiErrorHandler()) ;
-        return webapp ;
-    }
-
-    public static String getenv(String name) {
-        String x = System.getenv(name) ;
-        if ( x == null )
-            x = System.getProperty(name) ;
-        return x ;
-    }
-    
-    public DataAccessPointRegistry getDataAccessPointRegistry() {
-        return DataAccessPointRegistry.get(servletContext) ;
-    }
-
-    private static String tryResourceBase(String maybeResourceBase, String currentResourceBase) {
-        if ( currentResourceBase != null )
-            return currentResourceBase ;
-        if ( maybeResourceBase != null && FileOps.exists(maybeResourceBase) )
-            return maybeResourceBase ;
-        return currentResourceBase ;
-    }
-    
-    private void buildServerWebapp(String contextPath, String jettyConfig) {
-        if ( jettyConfig != null )
-            // --jetty-config=jetty-fuseki.xml
-            // for detailed configuration of the server using Jetty features.
-            configServer(jettyConfig) ;
-        else
-            defaultServerConfig(serverConfig.port, serverConfig.loopback) ;
-
-        WebAppContext webapp = createWebApp(contextPath) ;
-        if ( false /*enable symbolic links */ ) {
-            // See http://www.eclipse.org/jetty/documentation/current/serving-aliased-files.html
-            // Record what would be needed:
-            // 1 - Allow all symbolic links without checking
-            webapp.addAliasCheck(new ContextHandler.ApproveAliases());
-            // 2 - Check links are to valid resources. But default for Unix?
-            webapp.addAliasCheck(new AllowSymLinkAliasChecker()) ;
-        }
-        servletContext = webapp.getServletContext() ;
-        server.setHandler(webapp) ;
-        // Replaced by Shiro.
-        if ( jettyConfig == null && serverConfig.authConfigFile != null )
-            security(webapp, serverConfig.authConfigFile) ;
-    }
-    
-    // This is now provided by Shiro.
-    private static void security(ServletContextHandler context, String authfile) {
-        Constraint constraint = new Constraint() ;
-        constraint.setName(Constraint.__BASIC_AUTH) ;
-        constraint.setRoles(new String[]{"fuseki"}) ;
-        constraint.setAuthenticate(true) ;
-
-        ConstraintMapping mapping = new ConstraintMapping() ;
-        mapping.setConstraint(constraint) ;
-        mapping.setPathSpec("/*") ;
-
-        IdentityService identService = new DefaultIdentityService() ;
-
-        ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler() ;
-        securityHandler.addConstraintMapping(mapping) ;
-        securityHandler.setIdentityService(identService) ;
-
-        HashLoginService loginService = new HashLoginService("Fuseki Authentication", authfile) ;
-        loginService.setIdentityService(identService) ;
-
-        securityHandler.setLoginService(loginService) ;
-        securityHandler.setAuthenticator(new BasicAuthenticator()) ;
-
-        context.setSecurityHandler(securityHandler) ;
-
-        serverLog.debug("Basic Auth Configuration = " + authfile) ;
-    }
-
-    private void configServer(String jettyConfig) {
-        try {
-            serverLog.info("Jetty server config file = " + jettyConfig) ;
-            server = new Server() ;
-            XmlConfiguration configuration = new XmlConfiguration(new FileInputStream(jettyConfig)) ;
-            configuration.configure(server) ;
-            serverConnector = (ServerConnector)server.getConnectors()[0] ;
-        } catch (Exception ex) {
-            serverLog.error("SPARQLServer: Failed to configure server: " + ex.getMessage(), ex) ;
-            throw new FusekiException("Failed to configure a server using configuration file '" + jettyConfig + "'") ;
-        }
-    }
-
-    private void defaultServerConfig(int port, boolean loopback) {
-        server = new Server() ;
-        HttpConnectionFactory f1 = new HttpConnectionFactory() ;
-        // Some people do try very large operations ... really, should use POST.
-        f1.getHttpConfiguration().setRequestHeaderSize(512 * 1024);
-        f1.getHttpConfiguration().setOutputBufferSize(5 * 1024 * 1024) ;
-        // Do not add "Server: Jetty(....) when not a development system.
-        if ( ! Fuseki.outputJettyServerHeader )
-            f1.getHttpConfiguration().setSendServerVersion(false) ;
-
-        // https is better done with a Jetty configuration file
-        // because there are several things to configure. 
-        // See "examples/fuseki-jetty-https.xml"
-
-        ServerConnector connector = new ServerConnector(server, f1) ;
-        connector.setPort(port) ;
-        server.addConnector(connector);
-        if ( loopback )
-            connector.setHost("localhost");
-        serverConnector = connector ;
-    }
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/06f59125/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ActionBackup.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ActionBackup.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ActionBackup.java
new file mode 100644
index 0000000..55ab7d7
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ActionBackup.java
@@ -0,0 +1,68 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.mgt;
+
+import static java.lang.String.format ;
+
+import org.apache.jena.fuseki.ctl.ActionAsyncTask;
+import org.apache.jena.fuseki.ctl.Backup;
+import org.apache.jena.fuseki.ctl.TaskBase;
+import org.apache.jena.fuseki.servlets.HttpAction ;
+import org.apache.jena.fuseki.servlets.ServletOps ;
+import org.slf4j.Logger ;
+import org.slf4j.LoggerFactory ;
+
+public class ActionBackup extends ActionAsyncTask
+{
+    public ActionBackup() { super() ; }
+
+    @Override
+    protected Runnable createRunnable(HttpAction action) {
+        String name = action.getDatasetName() ;
+        if ( name == null ) {
+            action.log.error("Null for dataset name in item request") ;  
+            ServletOps.errorOccurred("Null for dataset name in item request");
+            return null ;
+        }
+        
+        action.log.info(format("[%d] Backup dataset %s", action.id, name)) ;
+        return new BackupTask(action) ;
+    }
+
+    static class BackupTask extends TaskBase {
+        static private Logger log = LoggerFactory.getLogger("Backup") ;
+        
+        public BackupTask(HttpAction action) {
+            super(action) ;
+        }
+
+        @Override
+        public void run() {
+            try {
+                String backupFilename = Backup.chooseFileName(datasetName) ;
+                log.info(format("[%d] >>>> Start backup %s -> %s", actionId, datasetName, backupFilename)) ;
+                Backup.backup(transactional, dataset, backupFilename) ;
+                log.info(format("[%d] <<<< Finish backup %s -> %s", actionId, datasetName, backupFilename)) ;
+            } catch (Exception ex) {
+                log.info(format("[%d] **** Exception in backup", actionId), ex) ;
+            }
+        }
+    }
+}
+    
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/jena/blob/06f59125/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ActionBackupList.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ActionBackupList.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ActionBackupList.java
new file mode 100644
index 0000000..0dc540d
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ActionBackupList.java
@@ -0,0 +1,95 @@
+/**
+ * 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.fuseki.mgt;
+
+import static java.lang.String.format ;
+
+import java.io.File ;
+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.List ;
+import java.util.stream.Collectors ;
+
+import javax.servlet.http.HttpServletRequest ;
+import javax.servlet.http.HttpServletResponse ;
+
+import org.apache.jena.atlas.json.JsonBuilder ;
+import org.apache.jena.atlas.json.JsonValue ;
+import org.apache.jena.fuseki.ctl.ActionCtl;
+import org.apache.jena.fuseki.servlets.HttpAction ;
+import org.apache.jena.fuseki.servlets.ServletOps ;
+import org.apache.jena.fuseki.webapp.FusekiSystem;
+
+/**
+ * A JSON API to list all the backups in the backup directory
+ */
+public class ActionBackupList extends ActionCtl {
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
+        doCommon(req, resp);
+    }
+
+    @Override
+    protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
+        doCommon(req, resp);
+    }
+
+    @Override
+    protected void perform(HttpAction action) {
+        JsonValue result = description(action) ;
+        ServletOps.setNoCache(action.response) ;
+        ServletOps.sendJsonReponse(action, result);
+    }
+        
+    private static DirectoryStream.Filter<Path> filterVisibleFiles = (entry) -> {
+        File f = entry.toFile() ;
+        return f.isFile() && !f.isHidden() ;
+    } ;
+
+    private JsonValue description(HttpAction action) {
+        if ( ! Files.isDirectory(FusekiSystem.dirBackups) )
+            ServletOps.errorOccurred(format("[%d] Backup area '%s' is not a directory", action.id, FusekiSystem.dirBackups)) ;
+        
+        List<Path> paths = new ArrayList<>() ;
+        try (DirectoryStream<Path> stream = Files.newDirectoryStream(FusekiSystem.dirBackups, filterVisibleFiles)) {
+            stream.forEach(paths::add) ;
+        } catch (IOException ex) {
+            action.log.error(format("[%d] Backup file list :: IOException :: %s", action.id, ex.getMessage())) ;
+            ServletOps.errorOccurred(ex);
+        }
+
+        List<String> fileNames = paths.stream().map((p)->p.getFileName().toString()).sorted().collect(Collectors.toList()) ;
+
+        JsonBuilder builder = new JsonBuilder() ;
+        builder.startObject("top") ;
+        builder.key("backups") ;
+
+        builder.startArray() ;
+        fileNames.forEach(builder::value) ;
+        builder.finishArray() ;
+
+        builder.finishObject("top") ;
+        return builder.build() ; 
+        
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/06f59125/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ActionDatasets.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ActionDatasets.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ActionDatasets.java
index 229726a..79b6d13 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ActionDatasets.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ActionDatasets.java
@@ -43,12 +43,15 @@ import org.apache.jena.datatypes.xsd.XSDDatatype ;
 import org.apache.jena.fuseki.FusekiLib ;
 import org.apache.jena.fuseki.build.DatasetDescriptionRegistry;
 import org.apache.jena.fuseki.build.FusekiBuilder;
+import org.apache.jena.fuseki.build.FusekiConst;
 import org.apache.jena.fuseki.build.Template;
 import org.apache.jena.fuseki.build.TemplateFunctions;
 import org.apache.jena.fuseki.ctl.ActionContainerItem;
+import org.apache.jena.fuseki.ctl.JsonDescription;
 import org.apache.jena.fuseki.server.DataAccessPoint;
 import org.apache.jena.fuseki.server.DataService;
 import org.apache.jena.fuseki.server.FusekiVocab;
+import org.apache.jena.fuseki.server.ServerConst;
 import org.apache.jena.fuseki.servlets.ActionLib;
 import org.apache.jena.fuseki.servlets.HttpAction;
 import org.apache.jena.fuseki.servlets.ServletOps;
@@ -95,7 +98,7 @@ public class ActionDatasets extends ActionContainerItem {
         action.log.info(format("[%d] GET datasets", action.id)) ;
         JsonBuilder builder = new JsonBuilder() ;
         builder.startObject("D") ;
-        builder.key(MgtConst.datasets) ;
+        builder.key(ServerConst.datasets) ;
         JsonDescription.arrayDatasets(builder, action.getDataAccessPointRegistry());
         builder.finishObject("D") ;
         return builder.build() ;
@@ -434,7 +437,7 @@ public class ActionDatasets extends ActionContainerItem {
                 dbName = dbName.substring(1) ;
             
             String update =  StrUtils.strjoinNL
-                (SystemState.PREFIXES,
+                (FusekiConst.PREFIXES,
                  "DELETE { GRAPH ?g { ?s fu:status ?state } }",
                  "INSERT { GRAPH ?g { ?s fu:status "+FmtUtils.stringForRDFNode(newState)+" } }",
                  "WHERE {",

http://git-wip-us.apache.org/repos/asf/jena/blob/06f59125/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ActionItem.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ActionItem.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ActionItem.java
deleted file mode 100644
index 3308880..0000000
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ActionItem.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.jena.fuseki.mgt;
-
-import org.apache.jena.atlas.json.JsonValue ;
-import org.apache.jena.fuseki.ctl.ActionContainerItem;
-import org.apache.jena.fuseki.servlets.HttpAction ;
-import org.apache.jena.fuseki.servlets.ServletOps ;
-import org.apache.jena.web.HttpSC ;
-
-/** Action on items in a container, but not the container itself */ 
-public abstract class ActionItem extends ActionContainerItem
-{
-    public ActionItem() { super() ; }
-    
-    @Override
-    final
-    protected JsonValue execGetContainer(HttpAction action) {
-        ServletOps.error(HttpSC.METHOD_NOT_ALLOWED_405) ;
-        return null ;
-    }
-
-    @Override
-    final
-    protected JsonValue execPostContainer(HttpAction action) {
-        ServletOps.error(HttpSC.METHOD_NOT_ALLOWED_405) ;
-        return null ;
-    }
-}
-

http://git-wip-us.apache.org/repos/asf/jena/blob/06f59125/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ActionServerStatus.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ActionServerStatus.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ActionServerStatus.java
index 3e060cd..d3c0873 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ActionServerStatus.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ActionServerStatus.java
@@ -32,7 +32,9 @@ import org.apache.jena.atlas.json.JsonBuilder ;
 import org.apache.jena.atlas.json.JsonValue ;
 import org.apache.jena.fuseki.Fuseki ;
 import org.apache.jena.fuseki.ctl.ActionCtl;
+import org.apache.jena.fuseki.ctl.JsonDescription;
 import org.apache.jena.fuseki.server.DataAccessPointRegistry ;
+import org.apache.jena.fuseki.server.ServerConst;
 import org.apache.jena.fuseki.servlets.HttpAction ;
 import org.apache.jena.fuseki.servlets.ServletOps ;
 
@@ -98,16 +100,16 @@ public class ActionServerStatus extends ActionCtl
 //            .finishObject() ;
 
         builder
-            .key(MgtConst.version).value(versionStr)
-            .key(MgtConst.built).value(builtDateStr)
-            .key(MgtConst.startDT).value(Fuseki.serverStartedAt())
-            .key(MgtConst.uptime).value(Fuseki.serverUptimeSeconds())
+            .key(ServerMgtConst.version).value(versionStr)
+            .key(ServerMgtConst.built).value(builtDateStr)
+            .key(ServerMgtConst.startDT).value(Fuseki.serverStartedAt())
+            .key(ServerMgtConst.uptime).value(Fuseki.serverUptimeSeconds())
             ;
             
     }
 
     private void describeDatasets(JsonBuilder builder, DataAccessPointRegistry registry) {
-        builder.key(MgtConst.datasets) ;
+        builder.key(ServerConst.datasets) ;
         JsonDescription.arrayDatasets(builder, registry);
     }
 

http://git-wip-us.apache.org/repos/asf/jena/blob/06f59125/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/JsonDescription.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/JsonDescription.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/JsonDescription.java
deleted file mode 100644
index 749aa3e..0000000
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/JsonDescription.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.jena.fuseki.mgt;
-
-import java.util.List ;
-
-import org.apache.jena.atlas.json.JsonBuilder ;
-import org.apache.jena.fuseki.server.DataAccessPoint ;
-import org.apache.jena.fuseki.server.DataAccessPointRegistry ;
-import org.apache.jena.fuseki.server.Endpoint ;
-import org.apache.jena.fuseki.server.Operation ;
-
-/** Create a description of a service */
-public class JsonDescription {
-    
-    public static void arrayDatasets(JsonBuilder builder, DataAccessPointRegistry registry) {
-        builder.startArray() ;
-        for ( String ds : registry.keys() ) {
-            DataAccessPoint access = registry.get(ds) ;
-            JsonDescription.describe(builder, access) ;
-        }
-        builder.finishArray() ;
-    }
-    
-    public static void describe(JsonBuilder builder, DataAccessPoint access) {
-        builder.startObject() ;
-        builder.key(MgtConst.dsName).value(access.getName()) ;
-        
-        builder.key(MgtConst.dsState).value(access.getDataService().isAcceptingRequests()) ;
-        
-        builder.key(MgtConst.dsService) ;
-        builder.startArray() ;
-        
-        for ( Operation operation : access.getDataService().getOperations() ) {
-            List<Endpoint> endpoints = access.getDataService().getEndpoints(operation) ;
-            describe(builder, operation, endpoints) ;
-        }
-        builder.finishArray() ;
-        builder.finishObject() ;
-    }
-    
-    private static void describe(JsonBuilder builder, Operation operation, List<Endpoint> endpoints) {
-        builder.startObject() ;
-        
-        builder.key(MgtConst.srvType).value(operation.getName()) ;
-        builder.key(MgtConst.srvDescription).value(operation.getDescription()) ;
-        builder.key(MgtConst.srvEndpoints) ;
-        builder.startArray() ;
-        for ( Endpoint endpoint : endpoints )
-            builder.value(endpoint.getEndpoint()) ;
-        builder.finishArray() ;
-
-        builder.finishObject() ;
-    }
-}
-

http://git-wip-us.apache.org/repos/asf/jena/blob/06f59125/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/MgtConst.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/MgtConst.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/MgtConst.java
deleted file mode 100644
index 2659313..0000000
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/MgtConst.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.jena.fuseki.mgt;
-
-/** Various constants used in the admin functions */ 
-public class MgtConst {
-    public static final String  opDump          = "dump" ;  
-    public static final String  opPing          = "ping" ;
-    
-    public static final String  opStats         = "stats" ;  
-    public static final String  opDatasets      = "datasets" ;
-    public static final String  opListBackups   = "backups-list" ;
-    public static final String  opServer        = "server" ;
-    
-    // JSON constants
-    public static final String datasets         = "datasets" ;
-    public static final String uptime           = "uptime" ;
-    public static final String startDT          = "startDateTime" ;
-    public static final String server           = "server" ;
-    public static final String port             = "port" ;
-    public static final String hostname         = "hostname" ;
-    public static final String admin            = "admin" ;
-    public static final String version          = "version" ;
-    public static final String built            = "built" ;
-    public static final String services         = "services" ;
-    public static final String operation        = "operation" ;
-    public static final String description      = "description" ;
-    public static final String endpoints        = "endpoints" ;
-    public static final String dsName           = "ds.name" ;
-    public static final String dsState          = "ds.state" ;
-    public static final String dsService        = "ds.services" ;
-    public static final String srvType          = "srv.type" ;
-    public static final String srvDescription   = "srv.description" ;
-    public static final String srvEndpoints     = "srv.endpoints" ;
-}
-

http://git-wip-us.apache.org/repos/asf/jena/blob/06f59125/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/MgtJMX.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/MgtJMX.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/MgtJMX.java
deleted file mode 100644
index f9023fe..0000000
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/MgtJMX.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.jena.fuseki.mgt ;
-
-
-public class MgtJMX
-{
-  public static void removeJMX() {  }
-    
-//    public static void addJMX() {
-//        DatasetRegistry registry = DatasetRegistry.get() ;
-//        for (String ds : registry.keys()) {
-//            DataAccessPoint dsRef = registry.get(ds) ;
-//            addJMX(dsRef) ;
-//        }
-//    }
-//
-//    private static void addJMX(DataAccessPoint dapt) {
-//        String x = datasetNames ;
-//        // if ( x.startsWith("/") )
-//        // x = x.substring(1) ;
-//        ARQMgt.register(Fuseki.PATH + ".dataset:name=" + x, dapt) ;
-//        // For all endpoints
-//        for (ServiceRef sRef : dapt.getServiceRefs()) {
-//            ARQMgt.register(Fuseki.PATH + ".dataset:name=" + x + "/" + sRef.name, sRef) ;
-//        }
-//    }
-//
-//    public static void removeJMX() {
-//        DatasetRegistry registry = DatasetRegistry.get() ;
-//        for (String ds : registry.keys()) {
-//            DataAccessPoint ref = registry.get(ds) ;
-//            removeJMX(ref) ;
-//        }
-//    }
-//
-//    private static void removeJMX(DatasetRef dsRef) {
-//        String x = dsRef.getName() ;
-//        ARQMgt.unregister(Fuseki.PATH + ".dataset:name=" + x) ;
-//        for (ServiceRef sRef : dsRef.getServiceRefs()) {
-//            ARQMgt.unregister(Fuseki.PATH + ".dataset:name=" + x + "/" + sRef.name) ;
-//        }
-//    }
-
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/06f59125/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ServerMgtConst.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ServerMgtConst.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ServerMgtConst.java
new file mode 100644
index 0000000..2af5e44
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ServerMgtConst.java
@@ -0,0 +1,39 @@
+/*
+ * 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.fuseki.mgt;
+
+/**
+ * Various constants used in the management API functions and JSON responses in the
+ * webapp/full server.
+ */
+public class ServerMgtConst {
+    public static final String  opDatasets      = "datasets" ;
+    public static final String  opListBackups   = "backups-list" ;
+    public static final String  opServer        = "server" ;
+    
+    public static final String uptime           = "uptime" ;
+    public static final String startDT          = "startDateTime" ;
+//    public static final String server           = "server" ;
+//    public static final String port             = "port" ;
+    public static final String hostname         = "hostname" ;
+    public static final String admin            = "admin" ;
+    public static final String version          = "version" ;
+    public static final String built            = "built" ;
+    public static final String services         = "services" ;
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/06f59125/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ServiceMXBean.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ServiceMXBean.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ServiceMXBean.java
deleted file mode 100644
index 72ce3a3..0000000
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/mgt/ServiceMXBean.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.jena.fuseki.mgt;
-
-public interface ServiceMXBean
-{
-    String getName() ;
-    
-    long getRequests() ;
-    long getRequestsGood() ;
-    long getRequestsBad() ;
-    
-//    void enable() ;
-//    void disable() ;
-}
-

http://git-wip-us.apache.org/repos/asf/jena/blob/06f59125/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/ServerConst.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/ServerConst.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/ServerConst.java
new file mode 100644
index 0000000..f8b0f91
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/ServerConst.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.fuseki.server;
+
+/** Various constants used in the API functions and JSON responses. */ 
+public class ServerConst {
+    // Location under /$/
+    public static final String  opDump          = "dump" ;  
+    public static final String  opPing          = "ping" ;
+    public static final String  opStats         = "stats" ;
+    
+//    // JSON constants
+    public static final String datasets         = "datasets" ;
+    public static final String operation        = "operation" ;
+    public static final String description      = "description" ;
+    public static final String endpoints        = "endpoints" ;
+
+    public static final String dsName           = "ds.name" ;
+    public static final String dsState          = "ds.state" ;
+    public static final String dsService        = "ds.services" ;
+    public static final String srvType          = "srv.type" ;
+    public static final String srvDescription   = "srv.description" ;
+    public static final String srvEndpoints     = "srv.endpoints" ;
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/06f59125/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/webapp/SystemState.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/webapp/SystemState.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/webapp/SystemState.java
index ada2f28..c5431a2 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/webapp/SystemState.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/webapp/SystemState.java
@@ -19,7 +19,6 @@
 package org.apache.jena.fuseki.webapp;
 
 import org.apache.jena.atlas.lib.FileOps ;
-import org.apache.jena.atlas.lib.StrUtils ;
 import org.apache.jena.fuseki.Fuseki ;
 import org.apache.jena.query.Dataset ;
 import org.apache.jena.tdb.StoreConnection ;
@@ -93,20 +92,5 @@ public class SystemState {
         dsg     = (DatasetGraphTransaction)(dataset.asDatasetGraph()) ;
         dsg.getContext().set(TDB.symUnionDefaultGraph, false) ;
     }
-    
-    public static String PREFIXES = StrUtils.strjoinNL
-        ("BASE            <http://example/base#>",
-         "PREFIX ja:      <http://jena.hpl.hp.com/2005/11/Assembler#>",
-         "PREFIX fu:      <http://jena.apache.org/fuseki#>",
-         "PREFIX fuseki:  <http://jena.apache.org/fuseki#>",
-         "PREFIX rdf:     <http://www.w3.org/1999/02/22-rdf-syntax-ns#>",
-         "PREFIX rdfs:    <http://www.w3.org/2000/01/rdf-schema#>",
-         "PREFIX tdb:     <http://jena.hpl.hp.com/2008/tdb#>",
-         "PREFIX sdb:     <http://jena.hpl.hp.com/2007/sdb#>",
-         "PREFIX list:    <http://jena.apache.org/ARQ/list#>",
-         "PREFIX xsd:     <http://www.w3.org/2001/XMLSchema#>",
-         "PREFIX apf:     <http://jena.apache.org/ARQ/property#>",
-         "PREFIX afn:     <http://jena.apache.org/ARQ/function#>",
-         "") ;
 }
 


[13/27] jena git commit: Javadoc formatting.

Posted by an...@apache.org.
Javadoc formatting.

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

Branch: refs/heads/master
Commit: aa65883b159c9a3ca6f2130d45bfa7249ba3c744
Parents: dfa40be
Author: Andy Seaborne <an...@apache.org>
Authored: Fri Aug 24 17:37:09 2018 +0100
Committer: Andy Seaborne <an...@apache.org>
Committed: Fri Aug 24 17:37:09 2018 +0100

----------------------------------------------------------------------
 .../src/main/java/org/apache/jena/sparql/core/DatasetGraph.java  | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/aa65883b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraph.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraph.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraph.java
index 2ab9f68..23df9c9 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraph.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraph.java
@@ -123,10 +123,10 @@ public interface DatasetGraph extends Transactional, Closeable
      */
     public Iterator<Quad> findNG(Node g, Node s, Node p , Node o) ;
 
-    /** Test whether the dataset  (including default graph) contains a quad - may include wildcards, Node.ANY or null */
+    /** Test whether the dataset (including default graph) contains a quad - may include wildcards, Node.ANY or null */
     public boolean contains(Node g, Node s, Node p , Node o) ;
 
-    /** Test whether the dataset contains a quad  (including default graph)- may include wildcards, Node.ANY or null */
+    /** Test whether the dataset contains a quad (including default graph)- may include wildcards, Node.ANY or null */
     public boolean contains(Quad quad) ;
 
     /** Remove everything - remove all named graphs, clear the default graph */


[03/27] jena git commit: JENA-1592: Unwrap TDB2 graphs

Posted by an...@apache.org.
JENA-1592: Unwrap TDB2 graphs


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

Branch: refs/heads/master
Commit: 9a60253c00aec6763adbb0253588fe4dc0b484d4
Parents: d8e51a8
Author: Andy Seaborne <an...@apache.org>
Authored: Mon Aug 20 20:35:06 2018 +0100
Committer: Andy Seaborne <an...@apache.org>
Committed: Thu Aug 23 17:15:05 2018 +0100

----------------------------------------------------------------------
 .../apache/jena/tdb2/solver/StageGeneratorDirectTDB.java |  2 +-
 .../org/apache/jena/tdb2/store/GraphViewSwitchable.java  | 11 +++++++----
 2 files changed, 8 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/9a60253c/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/StageGeneratorDirectTDB.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/StageGeneratorDirectTDB.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/StageGeneratorDirectTDB.java
index bb9ac10..a4eba73 100644
--- a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/StageGeneratorDirectTDB.java
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/solver/StageGeneratorDirectTDB.java
@@ -51,7 +51,7 @@ public class StageGeneratorDirectTDB implements StageGenerator
         
         if ( g instanceof GraphViewSwitchable ) {
             GraphViewSwitchable gvs = (GraphViewSwitchable)g;
-            g = gvs.getGraph();
+            g = gvs.getBaseGraph();
         }
         
         if ( ! ( g instanceof GraphTDB ) )

http://git-wip-us.apache.org/repos/asf/jena/blob/9a60253c/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/GraphViewSwitchable.java
----------------------------------------------------------------------
diff --git a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/GraphViewSwitchable.java b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/GraphViewSwitchable.java
index 2a3fbf5..f008ad9 100644
--- a/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/GraphViewSwitchable.java
+++ b/jena-db/jena-tdb2/src/main/java/org/apache/jena/tdb2/store/GraphViewSwitchable.java
@@ -76,17 +76,20 @@ public class GraphViewSwitchable extends GraphView {
         return transactionHandler;
     }
     
-    /** Return the {@code DatasetGraphSwitchable} we are viewing. */
+    /** Return the {@link DatasetGraphSwitchable} we are viewing. */
     @Override
     public DatasetGraphSwitchable getDataset() {
         return getx();
     }
     
-    /** Return the {@code Graph} from the underlying switchable.
+    /** Return the {@code Graph} from the underlying DatasetGraph
      *  Do not hold onto this reference across switches. 
      */
-    public Graph getGraph() {
-        return getx().getGraph(getGraphName());
+    public Graph getBaseGraph() {
+        if ( getGraphName() == null )
+            return getDSG().getDefaultGraph();
+        else
+            return getDSG().getGraph(getGraphName());
     }
 
     // Super uses find. Override to call GraphTDB.size()


[11/27] jena git commit: Use only getW() for the denying write operations.

Posted by an...@apache.org.
Use only getW() for the denying write operations.

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

Branch: refs/heads/master
Commit: 09f0e8b1c829013e377a42be84a0ce977973ee21
Parents: a17c6dd
Author: Andy Seaborne <an...@apache.org>
Authored: Fri Aug 24 12:02:20 2018 +0100
Committer: Andy Seaborne <an...@apache.org>
Committed: Fri Aug 24 12:02:20 2018 +0100

----------------------------------------------------------------------
 .../jena/sparql/core/DatasetGraphReadOnly.java  | 21 +++++++-------------
 1 file changed, 7 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/09f0e8b1/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphReadOnly.java
----------------------------------------------------------------------
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphReadOnly.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphReadOnly.java
index dffb52b..06aae4f 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphReadOnly.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/DatasetGraphReadOnly.java
@@ -18,8 +18,8 @@
 
 package org.apache.jena.sparql.core;
 
-import java.util.HashMap ;
 import java.util.Map ;
+import java.util.concurrent.ConcurrentHashMap;
 
 import org.apache.jena.atlas.logging.Log ;
 import org.apache.jena.graph.Graph ;
@@ -31,6 +31,9 @@ import org.apache.jena.sparql.graph.GraphReadOnly ;
  */
 public class DatasetGraphReadOnly extends DatasetGraphWrapper
 {
+    // Add a read-only wrapper any graphs returned.
+    // Block write access at getW().
+
     public DatasetGraphReadOnly(DatasetGraph dsg) { super(dsg) ; }
     
     private Graph dftGraph = null ;
@@ -50,10 +53,12 @@ public class DatasetGraphReadOnly extends DatasetGraphWrapper
         get().begin(mode) ; 
     }
     
-    private Map<Node, Graph> namedGraphs = new HashMap<>() ;
+    private Map<Node, Graph> namedGraphs = new ConcurrentHashMap<>() ;
 
     @Override
     public Graph getGraph(Node graphNode) {
+        // Cache GraphReadOnly wrappers. This also makes == work (a nicety)
+        // if the underlying DatasetGraph isn't changing.
         if ( namedGraphs.containsKey(graphNode) ) {
             if ( !super.containsGraph(graphNode) ) {
                 namedGraphs.remove(graphNode) ;
@@ -70,18 +75,6 @@ public class DatasetGraphReadOnly extends DatasetGraphWrapper
         return g ;
     }
 
-    @Override
-    public void setDefaultGraph(Graph g)
-    { throw new UnsupportedOperationException("read-only dataset") ; }
-
-    @Override
-    public void addGraph(Node graphName, Graph graph)
-    { throw new UnsupportedOperationException("read-only dataset") ; }
-
-    @Override
-    public void removeGraph(Node graphName)
-    { throw new UnsupportedOperationException("read-only dataset") ; }
-
     /** For operations that write the DatasetGraph. */
     @Override
     protected DatasetGraph getW()