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/09/21 10:16:08 UTC

[57/70] [abbrv] jena git commit: JENA-1597: Modules jena-fuseki-main and jena-fuseki-server

http://git-wip-us.apache.org/repos/asf/jena/blob/7e6d03af/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/TestFusekiCustomOperation.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/TestFusekiCustomOperation.java b/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/TestFusekiCustomOperation.java
deleted file mode 100644
index 17eaff6..0000000
--- a/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/TestFusekiCustomOperation.java
+++ /dev/null
@@ -1,188 +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.embedded;
-
-import static org.junit.Assert.*;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.fail;
-
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-
-import org.apache.commons.io.IOUtils;
-import org.apache.jena.atlas.io.IO;
-import org.apache.jena.atlas.web.HttpException;
-import org.apache.jena.atlas.web.TypedInputStream;
-import org.apache.jena.fuseki.FusekiConfigException;
-import org.apache.jena.fuseki.FusekiLib;
-import org.apache.jena.fuseki.build.FusekiBuilder;
-import org.apache.jena.fuseki.server.DataService;
-import org.apache.jena.fuseki.server.Operation;
-import org.apache.jena.fuseki.servlets.ActionService;
-import org.apache.jena.query.QueryExecution;
-import org.apache.jena.rdfconnection.RDFConnection;
-import org.apache.jena.rdfconnection.RDFConnectionFactory;
-import org.apache.jena.riot.web.HttpOp;
-import org.apache.jena.sparql.core.DatasetGraph;
-import org.apache.jena.sparql.core.DatasetGraphFactory;
-import org.junit.Test;
-
-/** Test for adding a new operation */
-public class TestFusekiCustomOperation {
-    private static final Operation newOp = Operation.register("Special", "Custom operation");
-    private static final String contentType = "application/special";
-    private static final String endpointName = "special";
-
-    private final ActionService customHandler = new CustomService();
-    private final int port = FusekiLib.choosePort();
-    private final String url = "http://localhost:"+port;
-
-    @Test
-    public void cfg_dataservice() {
-        // Create a DataService and add the endpoint -> operation association.
-        // This still needs the server to have the operation registered.  
-        DatasetGraph dsg = DatasetGraphFactory.createTxnMem();
-        DataService dataService = new DataService(dsg);
-        FusekiBuilder.populateStdServices(dataService, true);
-        FusekiBuilder.addServiceEP(dataService, newOp, endpointName);
-        
-        FusekiServer server = 
-            FusekiServer.create()
-                .port(port)
-                .registerOperation(newOp, contentType, customHandler)
-                .add("/ds", dataService)
-                .build();
-        testServer(server, true, true);
-    }
-        
-    @Test
-    public void cfg_builder_CT() {
-        FusekiServer server = 
-            FusekiServer.create()
-                .port(port)
-                .registerOperation(newOp, contentType, customHandler)
-                .add("/ds", DatasetGraphFactory.createTxnMem(), true)
-                .addOperation("/ds", endpointName, newOp)
-                .build();
-        testServer(server, true, true);
-    }
-
-    @Test
-    public void cfg_builder_noCT() {
-        FusekiServer server = 
-            FusekiServer.create()
-                .port(port)
-                .registerOperation(newOp, null, customHandler)
-                .add("/ds", DatasetGraphFactory.createTxnMem(), true)
-                .addOperation("/ds", endpointName, newOp)
-                .build();
-        testServer(server, true, false);
-    }
-    
-    @Test(expected=FusekiConfigException.class)
-    public void cfg_bad_01() {
-        FusekiServer.create()
-        .port(port)
-        .registerOperation(newOp, null, customHandler)
-        .addOperation("/UNKNOWN", endpointName, newOp);
-        //.build();
-    }
-    
-    @Test(expected=FusekiConfigException.class)
-    public void cfg_bad_02() {
-        FusekiServer.create()
-        .port(port)
-        //.registerOperation(newOp, null, customHandler)
-        .add("/ds", DatasetGraphFactory.createTxnMem(), true)
-        // Unregistered.
-        .addOperation("/ds", endpointName, newOp);
-        //.build();
-    }
-    
-    public void cfg_bad_ct_not_enabkled_here() {
-        FusekiServer server = FusekiServer.create()
-            .port(port)
-            .registerOperation(newOp, "app/special", customHandler)
-            .add("/ds", DatasetGraphFactory.createTxnMem(), true)
-            // Unregistered.
-            .addOperation("/ds", endpointName, newOp)
-            .build();
-        testServer(server, false, false);
-    }
-
-
-    private void testServer(FusekiServer server, boolean withEndpoint, boolean withContentType) {
-        try { 
-            server.start();
-            // Try query (no extension required)
-            try(RDFConnection rconn = RDFConnectionFactory.connect(url+"/ds")) {
-                try(QueryExecution qExec = rconn.query("ASK {}")) {
-                    qExec.execAsk();
-                }
-            }
-
-            if ( withEndpoint ) {
-                // Service endpoint name : GET
-                String s1 = HttpOp.execHttpGetString(url+"/ds/"+endpointName);
-    
-                // Service endpoint name : POST
-                try ( TypedInputStream stream = HttpOp.execHttpPostStream(url+"/ds/"+endpointName, "ignored", "", "text/plain") ) {
-                    String x = IOUtils.toString(stream, StandardCharsets.UTF_8);
-                    assertNotNull(x);
-                } catch (IOException ex) {
-                    IO.exception(ex);
-                }
-            } else {
-                // No endpoint so we expect a 404.
-                try {
-                    // Service endpoint name : GET
-                    HttpOp.execHttpGet(url+"/ds/"+endpointName);
-                    fail("Expected to fail HTTP GET");
-                } catch (HttpException ex) {
-                    assertEquals(404, ex.getResponseCode());   
-                }   
-            }
-             
-            if ( withContentType ) {
-                // Content-type
-                try ( TypedInputStream stream = HttpOp.execHttpPostStream(url+"/ds", contentType, "", "text/plain") ) {
-                    String x = IOUtils.toString(stream, StandardCharsets.UTF_8);
-                    assertNotNull(x);
-                } catch (IOException ex) {
-                    IO.exception(ex);
-                }
-            } else {
-                // No Content-Type
-                try ( TypedInputStream stream = HttpOp.execHttpPostStream(url+"/ds", contentType, "", "text/plain") ) {
-                    fail("Expected to fail HTTP POST using Content-Type");
-                } catch (HttpException ex) {} 
-
-                // Service endpoint name. DELETE -> fails 405
-                try { 
-                    HttpOp.execHttpDelete(url+"/ds/"+endpointName);
-                    throw new IllegalStateException("DELETE succeeded");
-                } catch (HttpException ex) {
-                    assertEquals(405, ex.getResponseCode());   
-                }
-            }
-        } finally {
-            server.stop();
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/7e6d03af/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/TestFusekiTestAuth.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/TestFusekiTestAuth.java b/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/TestFusekiTestAuth.java
deleted file mode 100644
index 4c575b4..0000000
--- a/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/TestFusekiTestAuth.java
+++ /dev/null
@@ -1,98 +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.embedded;
-
-import static org.apache.jena.fuseki.embedded.FusekiTestAuth.assertAuthHttpException;
-
-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.atlas.logging.LogCtl;
-import org.apache.jena.atlas.web.HttpException;
-import org.apache.jena.atlas.web.TypedInputStream;
-import org.apache.jena.fuseki.Fuseki;
-import org.apache.jena.riot.web.HttpOp;
-import org.eclipse.jetty.security.SecurityHandler;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
-import org.junit.Test;
-
-public class TestFusekiTestAuth {
-    
-    static {
-        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");
-    }
-    
-    private static String USER      = "user1234";
-    private static String PASSWORD  = "password1234";
-    
-    @BeforeClass
-    public static void ctlBeforeClass() {
-        SecurityHandler sh = FusekiTestAuth.makeSimpleSecurityHandler("/*", USER, PASSWORD);
-        FusekiTestAuth.setupServer(false, sh);
-    }
-
-    @AfterClass
-    public static void ctlAfterClass() {
-        FusekiTestAuth.teardownServer();
-        HttpOp.setDefaultHttpClient(HttpOp.createPoolingHttpClient());
-    }
-
-    @Test(expected=HttpException.class) 
-    public void testServer_auth_no_auth() {
-      // No Auth
-      try ( TypedInputStream in = HttpOp.execHttpGet(FusekiTestAuth.urlDataset(), "*/*") ) {}
-      catch (HttpException ex) { throw assertAuthHttpException(ex); }
-    }
-    
-    @Test public void testServer_auth() {
-        BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
-        Credentials credentials = new UsernamePasswordCredentials(USER, PASSWORD);
-        credsProvider.setCredentials(AuthScope.ANY, credentials);
-        HttpClient client = HttpClients.custom().setDefaultCredentialsProvider(credsProvider).build();
-        try ( TypedInputStream in = HttpOp.execHttpGet(FusekiTestAuth.urlDataset(), "*/*", client, null) ) {}
-    }
-    
-    @Test(expected=HttpException.class)
-    public void testServer_auth_bad_user() {
-        BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
-        Credentials credentials = new UsernamePasswordCredentials("USERUSER", PASSWORD);
-        credsProvider.setCredentials(AuthScope.ANY, credentials);
-        HttpClient client = HttpClients.custom().setDefaultCredentialsProvider(credsProvider).build();
-        try ( TypedInputStream in = HttpOp.execHttpGet(FusekiTestAuth.urlDataset(), "*/*", client, null) ) {}
-        catch (HttpException ex) { throw assertAuthHttpException(ex); }
-    }
-        
-    @Test(expected=HttpException.class)
-    public void testServer_auth_bad_password() {
-        BasicCredentialsProvider credsProv = new BasicCredentialsProvider();
-        credsProv.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(USER, "WRONG"));
-        HttpClient client = HttpClients.custom().setDefaultCredentialsProvider(credsProv).build();
-        
-        try ( TypedInputStream in = HttpOp.execHttpGet(FusekiTestAuth.urlDataset(), "*/*", client, null) ) {}
-        catch (HttpException ex) { throw assertAuthHttpException(ex); }
-    }
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/7e6d03af/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/TestFusekiTestServer.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/TestFusekiTestServer.java b/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/TestFusekiTestServer.java
deleted file mode 100644
index 9bbe2a8..0000000
--- a/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/TestFusekiTestServer.java
+++ /dev/null
@@ -1,60 +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.embedded;
-
-import org.apache.http.auth.AuthScope;
-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.atlas.web.HttpException;
-import org.apache.jena.atlas.web.TypedInputStream;
-import org.apache.jena.riot.web.HttpOp;
-import org.apache.jena.web.HttpSC;
-import org.junit.*;
-
-public class TestFusekiTestServer {
-    
-//    @BeforeClass static public void beforeSuiteClass() { ServerCtl.ctlBeforeTestSuite(); } 
-//    @AfterClass  static public void afterSuiteClass()  { ServerCtl.ctlAfterTestSuite(); }
-
-    // This is file is the "suite".
-    
-    @BeforeClass public static void ctlBeforeClass() { FusekiTestServer.ctlBeforeTestSuite(); FusekiTestServer.ctlBeforeClass(); }
-    @AfterClass  public static void ctlAfterClass()  { FusekiTestServer.ctlAfterClass();      FusekiTestServer.ctlAfterTestSuite(); }
-    @Before      public void ctlBeforeTest()         { FusekiTestServer.ctlBeforeTest(); }
-    @After       public void ctlAfterTest()          { FusekiTestServer.ctlAfterTest(); }
-    
-    @Test public void testServer_1() {
-        HttpOp.execHttpGetString(FusekiTestServer.urlDataset());
-    }
-    
-    @Test public void testServer_2() {
-        BasicCredentialsProvider credsProv = new BasicCredentialsProvider();
-        credsProv.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("USER", "PASSWORD"));
-        HttpClient client = HttpClients.custom().setDefaultCredentialsProvider(credsProv).build();
-        
-        // No auth set - should work.
-        try ( TypedInputStream in = HttpOp.execHttpGet(FusekiTestServer.urlDataset(), "*/*") ) {}
-        catch (HttpException ex) {
-            Assert.assertTrue(ex.getResponseCode() == HttpSC.FORBIDDEN_403 || ex.getResponseCode() == HttpSC.UNAUTHORIZED_401 );
-            throw ex;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/7e6d03af/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/TestMultipleEmbedded.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/TestMultipleEmbedded.java b/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/TestMultipleEmbedded.java
deleted file mode 100644
index 7871490..0000000
--- a/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/TestMultipleEmbedded.java
+++ /dev/null
@@ -1,170 +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.embedded;
-
-import static org.apache.jena.fuseki.embedded.TestEmbeddedFuseki.dataset ;
-import static org.apache.jena.fuseki.embedded.TestEmbeddedFuseki.query ;
-import static org.junit.Assert.assertEquals ;
-import static org.junit.Assert.assertTrue ;
-
-import java.io.OutputStream ;
-
-import org.apache.http.HttpEntity ;
-import org.apache.http.entity.ContentProducer ;
-import org.apache.http.entity.EntityTemplate ;
-import org.apache.jena.atlas.web.ContentType ;
-import org.apache.jena.fuseki.FusekiException ;
-import org.apache.jena.fuseki.FusekiLib;
-import org.apache.jena.graph.Graph ;
-import org.apache.jena.query.ResultSet ;
-import org.apache.jena.query.ResultSetFormatter ;
-import org.apache.jena.riot.RDFDataMgr ;
-import org.apache.jena.riot.RDFFormat ;
-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.system.Txn ;
-import org.junit.Test ;
-
-public class TestMultipleEmbedded {
-    
-    static Quad q1 = SSE.parseQuad("(_ :s :p 1)") ;
-    static Quad q2 = SSE.parseQuad("(_ :s :p 2)") ;
-    
-    // Two servers, same port -> bad.
-    @Test(expected=FusekiException.class)
-    public void multiple_01() {
-        DatasetGraph dsg = dataset() ;
-
-        int port = FusekiLib.choosePort() ;
-        FusekiServer server1 = FusekiServer.create().port(port).add("/ds1", dsg).build() ;
-        // Same port - Bad.
-        FusekiServer server2 = FusekiServer.create().port(port).add("/ds2", dsg).build() ;
-    
-        server1.start();
-        
-        try {
-            server2.start();
-        } catch (FusekiException ex) {
-            assertTrue(ex.getCause() instanceof java.net.BindException ) ;
-            throw ex ;
-        } finally {
-            try { server1.stop() ; } catch (Exception ex) {}
-            try { server2.stop() ; } catch (Exception ex) {}
-        }
-    }
-
-    // Two servers, different ports -> good.
-    @Test
-    public void multiple_02() {
-        DatasetGraph dsg = dataset() ;
-        int port1 = FusekiLib.choosePort() ;
-        FusekiServer server1 = FusekiServer.create().port(port1).add("/ds1", dsg).build() ;
-
-        // Different port - good
-        int port2 = FusekiLib.choosePort() ;
-        FusekiServer server2 = FusekiServer.create().port(port2).add("/ds2", dsg).build() ;
-
-        try {
-            server1.start();
-            server2.start();
-        } finally {
-            try { server1.stop() ; } catch (Exception ex) {}
-            try { server2.stop() ; } catch (Exception ex) {}
-        }
-    }
-    
-    // Two servers, two datasets.
-    @Test
-    public void multiple_03() {
-        DatasetGraph dsg1 = dataset() ;
-        DatasetGraph dsg2 = dataset() ;
-        // Same name.
-        int port1 = FusekiLib.choosePort() ;
-        FusekiServer server1 = FusekiServer.create().port(port1).add("/ds", dsg1).build().start() ;
-        Txn.executeWrite(dsg1, ()->dsg1.add(q1));
-        
-        int port2 = FusekiLib.choosePort() ;
-        FusekiServer server2 = FusekiServer.create().port(port2).add("/ds", dsg2).build().start() ;
-        Txn.executeWrite(dsg2, ()->dsg2.add(q2));
-        
-        query("http://localhost:"+port1+"/ds/", "SELECT * {?s ?p 1}", qExec->{
-            ResultSet rs = qExec.execSelect() ; 
-            int x = ResultSetFormatter.consume(rs) ;
-            assertEquals(1, x) ;
-        }) ;
-        query("http://localhost:"+port2+"/ds/", "SELECT * {?s ?p 1}", qExec->{
-            ResultSet rs = qExec.execSelect() ; 
-            int x = ResultSetFormatter.consume(rs) ;
-            assertEquals(0, x) ;
-        }) ;
-        server1.stop();
-        // server2 still running
-        query("http://localhost:"+port2+"/ds/", "SELECT * {?s ?p 2}", qExec->{
-            ResultSet rs = qExec.execSelect() ; 
-            int x = ResultSetFormatter.consume(rs) ;
-            assertEquals(1, x) ;
-        }) ;
-        server2.stop();
-    }
-    
-    // Two servers, one dataset under two names.
-    @Test
-    public void multiple_04() {
-        DatasetGraph dsg = dataset() ;
-        
-        int port1 = FusekiLib.choosePort() ;
-        FusekiServer server1 = FusekiServer.create().port(port1).add("/ds1", dsg).build().start() ;
-        Txn.executeWrite(dsg, ()->dsg.add(q1));
-        
-        int port2 = FusekiLib.choosePort() ;
-        FusekiServer server2 = FusekiServer.create().port(port2).add("/ds2", dsg).build().start() ;
-        Txn.executeWrite(dsg, ()->dsg.add(q2));
-        
-        query("http://localhost:"+port1+"/ds1", "SELECT * {?s ?p ?o}", qExec->{
-            ResultSet rs = qExec.execSelect() ; 
-            int x = ResultSetFormatter.consume(rs) ;
-            assertEquals(2, x) ;
-        }) ;
-        query("http://localhost:"+port2+"/ds2", "SELECT * {?s ?p ?o}", qExec->{
-            ResultSet rs = qExec.execSelect() ; 
-            int x = ResultSetFormatter.consume(rs) ;
-            assertEquals(2, x) ;
-        }) ;
-
-        server1.stop();
-        server2.stop();
-    }
-
-    /** Create an HttpEntity for the graph */  
-    protected static HttpEntity graphToHttpEntity(final Graph graph) {
-        final RDFFormat syntax = RDFFormat.TURTLE_BLOCKS ;
-        ContentProducer producer = new ContentProducer() {
-            @Override
-            public void writeTo(OutputStream out) {
-                RDFDataMgr.write(out, graph, syntax) ;
-            }
-        } ;
-        EntityTemplate entity = new EntityTemplate(producer) ;
-        ContentType ct = syntax.getLang().getContentType() ;
-        entity.setContentType(ct.getContentType()) ;
-        return entity ;
-    }
-}
-

http://git-wip-us.apache.org/repos/asf/jena/blob/7e6d03af/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/examples/ExtendFuseki_AddService_1.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/examples/ExtendFuseki_AddService_1.java b/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/examples/ExtendFuseki_AddService_1.java
deleted file mode 100644
index e4f8e3d..0000000
--- a/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/examples/ExtendFuseki_AddService_1.java
+++ /dev/null
@@ -1,158 +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.embedded.examples;
-
-import java.io.IOException;
-
-import org.apache.jena.atlas.io.IO;
-import org.apache.jena.atlas.logging.LogCtl;
-import org.apache.jena.atlas.web.HttpException;
-import org.apache.jena.atlas.web.TypedInputStream;
-import org.apache.jena.fuseki.FusekiLib;
-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.riot.web.HttpOp;
-import org.apache.jena.sparql.core.DatasetGraphFactory;
-import org.apache.jena.util.FileUtils;
-import org.apache.jena.web.HttpSC;
-
-/**
- * This example show adding a custom operation to Fuseki.
- * <p>
- * There are two ways operations are routed: for a datset {@code /dataset}:
- * <ol>
- * <li>By endpoint name: {@code /dataset/endpoint}</li>
- * <li>By content type, when a POST is made on the {@code /dataset}.</li>
- * </ol>
- * The first is the usual way; the second is not a common pattern.
- * <p>
- * The second way is in addition to the endpoint; an endpoint is always required to
- * enabled routing by {@code Content-Type}.
- * <p>
- * The process for adding an operation is:
- * <ul>
- * <li>Register the operation with the server, with its implmementation.</li>
- * <li>Add the operation to a datasets.</li>
- * </ul>
- * <pre>
- *   // Register operation.
- *   Operation myOperation = Operation.register("Special", "Custom operation");
- *   // An implementation to call
- *   ActionService customHandler = new SpecialService();
- *   // Builder pattern ...
- *   FusekiServer server = 
- *       FusekiServer.create().port (1122)
- *          // Register the operation with the server, together with implementation. 
- *          .registerOperation(myOperation, customHandler)
- *          // Add a dataset
- *          .add("/dataset", DatasetGraphFactory.createTxnMem(), true)
- *          // Add operation by endpoint
- *          .addOperation("/dataset", "endpoint", myOperation)
- *          .build();
- * </pre>
- * @see SpecialService
- */
-
-public class ExtendFuseki_AddService_1 {
-    static { LogCtl.setLog4j(); }
-
-    // Endpoint dispatch only.
-    
-    // Choose free port for the example
-    // Normally, this is fixed and published, and fixed in URLs.
-    // To make the example portable, we ask the OS for a free port.
-    static int PORT             = FusekiLib.choosePort();
-    
-    // The server
-    static String SERVER_URL    = "http://localhost:"+PORT+"/";
-    
-    static String DATASET       = "dataset";
-    
-    public static void main(String ...args) {
-        // Create a new operation: operations are really just names (symbols). The code to
-        // run is found by looking up the operation in a per-server table that gives the server-specific
-        // implementation as an ActionService.
-
-        Operation myOperation = Operation.register("Special", "Custom operation");
-        
-        // Service endpoint name.
-        // This can be different for different datasets even in the same server.
-        // c.f. {@code fuseki:serviceQuery}
-        
-        String endpointName = "special";
-
-        // The handled for the new operation.
-        
-        ActionService customHandler = new SpecialService();
-        
-        FusekiServer server = 
-            FusekiServer.create().port(PORT)
-                .verbose(true)
-
-                // Register the new operation, and it's handler, but no Content-Type
-                .registerOperation(myOperation, customHandler)
-                
-                // Add a dataset with the normal, default naming services 
-                // (/sparql, /query, /update, /upload, /data, /get)  
-                .add(DATASET, DatasetGraphFactory.createTxnMem(), true)
-                
-                // Add the custom service, mapping from endpoint to operation for a specific dataset.
-                .addOperation(DATASET, endpointName, myOperation)
-                
-                // And build the server.
-                .build();
-        
-        // Start the server. This does not block this thread.
-        server.start();
-        
-        // Try some operations on the server using the service URL. 
-        String customOperationURL = SERVER_URL + DATASET + "/" + endpointName;
-        
-        try {
-
-            // Service endpoint name : GET
-            String s1 = HttpOp.execHttpGetString(customOperationURL);
-            System.out.print(s1);
-            if ( s1 == null )
-                System.out.println();
-
-            // Service endpoint name : POST
-            try ( TypedInputStream stream = HttpOp.execHttpPostStream(customOperationURL, null, "text/plain") ) {
-                String s2 = FileUtils.readWholeFileAsUTF8(stream);
-                System.out.print(s2);
-                if ( s2 == null )
-                    System.out.println();
-            } catch (IOException ex) { IO.exception(ex); }
-
-            // Service endpoint name. DELETE -> fails 405
-            try { 
-                HttpOp.execHttpDelete(customOperationURL);
-                throw new IllegalStateException("DELETE succeeded");
-            } catch (HttpException ex) {
-                if ( ex.getResponseCode() != HttpSC.METHOD_NOT_ALLOWED_405 )
-                    System.err.println("Unexpected HTTP Response Code: "+ex.getMessage());
-                else
-                    System.out.println("DELETE rejected correctly: "+ex.getMessage());
-            }
-        } finally {
-            server.stop();
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/7e6d03af/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/examples/ExtendFuseki_AddService_2.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/examples/ExtendFuseki_AddService_2.java b/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/examples/ExtendFuseki_AddService_2.java
deleted file mode 100644
index 7c97132..0000000
--- a/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/examples/ExtendFuseki_AddService_2.java
+++ /dev/null
@@ -1,123 +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.embedded.examples;
-
-import org.apache.jena.atlas.logging.LogCtl;
-import org.apache.jena.fuseki.FusekiLib;
-import org.apache.jena.fuseki.build.FusekiBuilder;
-import org.apache.jena.fuseki.embedded.FusekiServer;
-import org.apache.jena.fuseki.server.DataService;
-import org.apache.jena.fuseki.server.Operation;
-import org.apache.jena.fuseki.servlets.ActionService;
-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.riot.web.HttpOp;
-import org.apache.jena.sparql.core.DatasetGraph;
-import org.apache.jena.sparql.core.DatasetGraphFactory;
-import org.apache.jena.sparql.engine.http.QueryExceptionHTTP;
-import org.apache.jena.web.HttpSC;
-
-/**
- * Create custom endpoint names, dispatch by {@code Content-Type}.
- * See also {@link ExtendFuseki_AddService_1} for more details.
- */
-
-public class ExtendFuseki_AddService_2 {
-    static { LogCtl.setLog4j(); }
-
-    // Endpoint dispatch only.
-    static int PORT             = FusekiLib.choosePort();
-    
-    // The server
-    static String SERVER_URL    = "http://localhost:"+PORT+"/";
-    
-    static String DATASET       = "dataset";
-    
-    public static void main(String ...args) {
-        // Register a new operation
-
-        Operation myOperation = Operation.register("Special", "Custom operation");
-        
-        // Service endpoint names.
-        
-        String queryEndpoint = "q";
-        String customEndpoint = "x";
-        
-        // Make a DataService with custom named for endpoints.
-        // In this example, "q" for SPARQL query and "x" for our custom extension and no others.
-        DatasetGraph dsg = DatasetGraphFactory.createTxnMem();
-        DataService dataService = new DataService(dsg);
-        // This would add the usual defaults.
-        //FusekiBuilder.populateStdServices(dataService, true);
-        FusekiBuilder.addServiceEP(dataService, myOperation, customEndpoint);
-        FusekiBuilder.addServiceEP(dataService, Operation.Query, queryEndpoint);
-
-        // The handled for the new operation.
-        ActionService customHandler = new SpecialService();
-        
-        FusekiServer server = 
-            FusekiServer.create().port(PORT)
-                .verbose(true)
-                // Register the new operation, and it's handler
-                .registerOperation(myOperation, customHandler)
-
-                // The DataService.
-                .add(DATASET, dataService)
-                
-                // And build the server.
-                .build();
-        
-        server.start();
-        
-        // Try some operations on the server using the service URL. 
-        String customOperationURL = SERVER_URL + DATASET + "/" + customEndpoint;
-        String queryOperationURL = SERVER_URL + DATASET + "/" + queryEndpoint;
-        
-        Query query = QueryFactory.create("ASK{}"); 
-        
-        
-        try {
-            
-            // Try custom name - OK
-            try ( QueryExecution qExec = QueryExecutionFactory.sparqlService(queryOperationURL, query) ) {
-                qExec.execAsk();
-            }
-            
-            // Try default name - 404
-            try ( QueryExecution qExec = QueryExecutionFactory.sparqlService(SERVER_URL + DATASET + "/sparql", query) ) {
-                qExec.execAsk();
-                throw new RuntimeException("Didn't fail");
-            } catch (QueryExceptionHTTP ex) {
-                if ( ex.getResponseCode() != HttpSC.NOT_FOUND_404 ) {
-                    throw new RuntimeException("Not a 404", ex);
-                }
-            }
-            
-            // Service endpoint name : GET
-            String s1 = HttpOp.execHttpGetString(customOperationURL);
-            if ( s1 == null )
-                throw new RuntimeException("Failed: "+customOperationURL);
-            
-        } finally {
-            server.stop();
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/7e6d03af/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/examples/ExtendFuseki_AddService_3.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/examples/ExtendFuseki_AddService_3.java b/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/examples/ExtendFuseki_AddService_3.java
deleted file mode 100644
index ce663a8..0000000
--- a/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/examples/ExtendFuseki_AddService_3.java
+++ /dev/null
@@ -1,107 +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.embedded.examples;
-
-import java.io.IOException;
-
-import org.apache.jena.atlas.io.IO;
-import org.apache.jena.atlas.logging.LogCtl;
-import org.apache.jena.atlas.web.TypedInputStream;
-import org.apache.jena.fuseki.FusekiLib;
-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.riot.web.HttpOp;
-import org.apache.jena.sparql.core.DatasetGraphFactory;
-import org.apache.jena.util.FileUtils;
-
-/**
- * This example show adding a custom operation to Fuseki, with dispatch by {@code Content-Type}.
- * <p>
- * See {@link ExtendFuseki_AddService_1} fior a geenral description of the routing process.
- * @see SpecialService
- */
-
-public class ExtendFuseki_AddService_3 {
-    static { LogCtl.setLog4j(); }
-
-    static int PORT             = FusekiLib.choosePort();
-    
-    // The server
-    static String SERVER_URL    = "http://localhost:"+PORT+"/";
-    
-    static String DATASET       = "dataset";
-    
-    public static void main(String ...args) {
-        // Create a new operation: operations are really just names (symbols). The code to
-        // run is found by looking up the operation in a per-server table that gives the server-specific
-        // implementation as an ActionService.
-
-        Operation myOperation = Operation.register("Special", "Custom operation");
-        
-        // Service endpoint name.
-        // This can be different for different datasets even in the same server.
-        // c.f. {@code fuseki:serviceQuery}
-        
-        String endpointName = "special";
-        String contentType = "application/special";
-
-        // The handled for the new operation.
-        
-        ActionService customHandler = new SpecialService();
-        
-        FusekiServer server = 
-            FusekiServer.create().port(PORT)
-                .verbose(true)
-
-                // Register the new operation, with content type and handler
-                .registerOperation(myOperation, contentType, customHandler)
-                
-                // Add a dataset with the normal, default naming services 
-                // (/sparql, /query, /update, /upload, /data, /get)  
-                .add(DATASET, DatasetGraphFactory.createTxnMem(), true)
-                
-                // Add the custom service, mapping from endpoint to operation for a specific dataset.
-                // Required when when routing via Content-Type. 
-                .addOperation(DATASET, endpointName, myOperation)
-                
-                // And build the server.
-                .build();
-        
-        // Start the server. This does not block this thread.
-        server.start();
-        
-        // Try some operations on the server using the service URL.
-        String datasetURL = SERVER_URL + DATASET;
-        //String customOperationURL = SERVER_URL + DATASET + "/" + endpointName;
-        
-        try {
-
-            // Dataset endpoint name : POST, with Content-type.
-            try ( TypedInputStream stream = HttpOp.execHttpPostStream(datasetURL, contentType, "", "text/plain") ) {
-                String s2 = FileUtils.readWholeFileAsUTF8(stream);
-                System.out.print(s2);
-                if ( s2 == null )
-                    System.out.println();
-            } catch (IOException ex) { IO.exception(ex); }
-        } finally {
-            server.stop();
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/7e6d03af/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/examples/SpecialService.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/examples/SpecialService.java b/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/examples/SpecialService.java
deleted file mode 100644
index 239e6d7..0000000
--- a/jena-fuseki2/jena-fuseki-embedded/src/test/java/org/apache/jena/fuseki/embedded/examples/SpecialService.java
+++ /dev/null
@@ -1,81 +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.embedded.examples;
-
-import java.io.IOException;
-
-import org.apache.jena.atlas.logging.LogCtl;
-import org.apache.jena.fuseki.servlets.ActionREST;
-import org.apache.jena.fuseki.servlets.HttpAction;
-import org.apache.jena.fuseki.servlets.ServletOps;
-import org.apache.jena.riot.WebContent;
-import org.apache.jena.web.HttpSC;
-
-public class SpecialService extends ActionREST {
-    static { LogCtl.setLog4j(); }
-    
-    @Override
-    protected void doGet(HttpAction action) {
-        action.response.setStatus(HttpSC.OK_200);
-        try {
-            action.response.setContentType(WebContent.contentTypeTextPlain);
-            action.response.getOutputStream().println("    ** Hello world (GET) **");
-        }
-        catch (IOException e) {
-            e.printStackTrace();
-        }
-    }
-
-    @Override
-    protected void doHead(HttpAction action) {
-        action.response.setStatus(HttpSC.OK_200);
-        action.response.setContentType(WebContent.contentTypeTextPlain);
-    }
-
-    @Override
-    protected void doPost(HttpAction action) {
-        action.response.setStatus(HttpSC.OK_200);
-        try {
-            action.response.setContentType(WebContent.contentTypeTextPlain);
-            action.response.getOutputStream().println("    ** Hello world (POST) **");
-        }
-        catch (IOException e) {
-            e.printStackTrace();
-        }
-    }
-
-    @Override
-    protected void doPatch(HttpAction action) { notSupported(action); }
-
-    @Override
-    protected void doDelete(HttpAction action) { notSupported(action); }
-
-    @Override
-    protected void doPut(HttpAction action) { notSupported(action); }
-
-    @Override
-    protected void doOptions(HttpAction action) { notSupported(action); }
-
-    @Override
-    protected void validate(HttpAction action) { }
-    
-    private void notSupported(HttpAction action) {
-        ServletOps.errorMethodNotAllowed(action.getMethod()+" "+action.getActionURI());
-    }
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/7e6d03af/jena-fuseki2/jena-fuseki-embedded/src/test/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-embedded/src/test/resources/log4j.properties b/jena-fuseki2/jena-fuseki-embedded/src/test/resources/log4j.properties
deleted file mode 100644
index e84e60e..0000000
--- a/jena-fuseki2/jena-fuseki-embedded/src/test/resources/log4j.properties
+++ /dev/null
@@ -1,40 +0,0 @@
-# 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/7e6d03af/jena-fuseki2/jena-fuseki-embedded/testing/FusekiEmbedded/config.ttl
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-embedded/testing/FusekiEmbedded/config.ttl b/jena-fuseki2/jena-fuseki-embedded/testing/FusekiEmbedded/config.ttl
deleted file mode 100644
index 5a7f84a..0000000
--- a/jena-fuseki2/jena-fuseki-embedded/testing/FusekiEmbedded/config.ttl
+++ /dev/null
@@ -1,18 +0,0 @@
-@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 ja:      <http://jena.hpl.hp.com/2005/11/Assembler#> .
-@prefix tdb:     <http://jena.hpl.hp.com/2008/tdb#> .
-
-<#serviceInMemory> rdf:type fuseki:Service;
-    rdfs:label "test";
-    fuseki:name "FuTest";
-    fuseki:serviceQuery "query";
-    fuseki:serviceUpdate "update";
-    fuseki:serviceUpload "upload" ;
-    fuseki:dataset <#dataset> ;
-.
-
-<#dataset> rdf:type ja:RDFDataset;
-.

http://git-wip-us.apache.org/repos/asf/jena/blob/7e6d03af/jena-fuseki2/jena-fuseki-embedded/testing/FusekiEmbedded/test.txt
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-embedded/testing/FusekiEmbedded/test.txt b/jena-fuseki2/jena-fuseki-embedded/testing/FusekiEmbedded/test.txt
deleted file mode 100644
index 1a98ff9..0000000
--- a/jena-fuseki2/jena-fuseki-embedded/testing/FusekiEmbedded/test.txt
+++ /dev/null
@@ -1 +0,0 @@
-Test data.

http://git-wip-us.apache.org/repos/asf/jena/blob/7e6d03af/jena-fuseki2/jena-fuseki-main/pom.xml
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-main/pom.xml b/jena-fuseki2/jena-fuseki-main/pom.xml
new file mode 100644
index 0000000..25f3d5e
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-main/pom.xml
@@ -0,0 +1,159 @@
+<?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 Server</name>
+  <artifactId>jena-fuseki-main</artifactId>
+
+  <parent>
+    <groupId>org.apache.jena</groupId>
+    <artifactId>jena-fuseki</artifactId>
+    <version>3.9.0-SNAPSHOT</version>
+  </parent> 
+
+  <packaging>jar</packaging>
+
+  <properties>
+    <automatic.module.name>org.apache.jena.jena-fuseki-main</automatic.module.name>
+  </properties>
+  
+  <dependencies> 
+
+    <dependency>
+      <groupId>org.apache.jena</groupId>
+      <artifactId>jena-fuseki-core</artifactId>
+      <version>3.9.0-SNAPSHOT</version>
+
+      <exclusions>
+        
+        <!-- Parts of Jena not needed but picked up via jena-fuseki-core.
+             The application can put back any of these if it wants to use
+             them, e.g. a text-indexed dataset, providing the version is compatible.
+	      -->
+
+        <exclusion>
+          <groupId>org.apache.jena</groupId>
+          <artifactId>jena-text</artifactId>
+        </exclusion>
+
+        <!-- Jena uses SLF4J as its API. Exclude any logging bindings picked up via Jena.
+          For example, jena-fuseki-core has logging output and so some logging provider.
+          For embedded use, it is the enclosing application that chooses the kind of
+          logging system.    
+        --> 
+        <!--
+        <exclusion>
+          <groupId>org.slf4j</groupId>
+          <artifactId>slf4j-log4j12</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>log4j</groupId>
+          <artifactId>log4j</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.slf4j</groupId>
+          <artifactId>slf4j-jdk14</artifactId>
+        </exclusion>
+        -->
+      </exclusions>
+      
+    </dependency>
+
+    <dependency>
+      <groupId>org.apache.jena</groupId>
+      <artifactId>jena-cmds</artifactId>
+      <version>3.9.0-SNAPSHOT</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-log4j12</artifactId>
+      <optional>true</optional>
+    </dependency>
+    <dependency>
+      <groupId>log4j</groupId>
+      <artifactId>log4j</artifactId> 
+      <optional>true</optional>
+    </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>
+        <configuration>
+          <archive>
+            <manifestEntries>
+              <Automatic-Module-Name>${automatic.module.name}</Automatic-Module-Name>
+            </manifestEntries>
+          </archive>
+        </configuration>
+        <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>
+
+    </plugins>
+
+  </build>
+
+</project>

http://git-wip-us.apache.org/repos/asf/jena/blob/7e6d03af/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiServer.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiServer.java b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiServer.java
new file mode 100644
index 0000000..cd9b9e0
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiServer.java
@@ -0,0 +1,597 @@
+/*
+ * 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.main;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import javax.servlet.Filter;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServlet;
+
+import org.apache.jena.atlas.lib.Pair;
+import org.apache.jena.fuseki.Fuseki;
+import org.apache.jena.fuseki.FusekiConfigException;
+import org.apache.jena.fuseki.FusekiException;
+import org.apache.jena.fuseki.build.FusekiBuilder;
+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;
+import org.apache.jena.fuseki.server.FusekiVocab;
+import org.apache.jena.fuseki.server.Operation;
+import org.apache.jena.fuseki.servlets.ActionService;
+import org.apache.jena.fuseki.servlets.FusekiFilter;
+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.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.core.assembler.AssemblerUtils;
+import org.apache.jena.sparql.util.graph.GraphUtils;
+import org.eclipse.jetty.security.SecurityHandler;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.servlet.DefaultServlet;
+import org.eclipse.jetty.servlet.FilterHolder;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+
+/**
+ * Embedded Fuseki server. This is a Fuseki server running with a pre-configured set of
+ * datasets and services. There is no admin UI and no security.
+ * <p>
+ * To create a embedded sever, use {@link FusekiServer} ({@link #make} is a
+ * packaging of a call to {@link FusekiServer} for the case of one dataset,
+ * responding to localhost only).
+ * <p>
+ * The application should call {@link #start()} to actually start the server
+ * (it will run in the background : see {@link #join}).
+ * <p>Example:
+ * <pre>
+ *      DatasetGraph dsg = ...;
+ *      FusekiServer server = FusekiServer.create()
+ *          .port(1234)
+ *          .add("/ds", dsg)
+ *          .build();
+ *       server.start();
+ * </pre>
+ * Compact form (use the builder pattern above to get more flexibility):
+ * <pre>
+ *    FusekiServer.make(1234, "/ds", dsg).start();
+ * </pre>
+ *
+ */
+public class FusekiServer {
+    /** Construct a Fuseki server for one dataset.
+     * It only responds to localhost.
+     * The returned server has not been started.
+     */
+    static public FusekiServer make(int port, String name, DatasetGraph dsg) {
+        return create()
+            .port(port)
+            .loopback(true)
+            .add(name, dsg)
+            .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;
+
+    private FusekiServer(int port, Server server) {
+        this.server = server;
+        // This should be the same.
+        //this.port = ((ServerConnector)server.getConnectors()[0]).getPort();
+        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();
+    }
+
+    /** Get the {@link DataAccessPointRegistry}.
+     * This method is intended for inspecting the registry.
+     */
+    public DataAccessPointRegistry getDataAccessPointRegistry() {
+        return DataAccessPointRegistry.get(getServletContext());
+    }
+
+    /** Get the {@link DataAccessPointRegistry}.
+     * This method is intended for inspecting the registry.
+     */
+    public ServiceDispatchRegistry getServiceDispatchRegistry() {
+        return ServiceDispatchRegistry.get(getServletContext());
+    }
+
+    /** Start the server - the server continues to run after this call returns.
+     *  To synchronise with the server stopping, call {@link #join}.
+     */
+    public FusekiServer start() {
+        try { server.start(); }
+        catch (Exception e) { throw new FusekiException(e); }
+        if ( port == 0 )
+            port = ((ServerConnector)server.getConnectors()[0]).getLocalPort();
+        Fuseki.serverLog.info("Start Fuseki (port="+port+")");
+        return this;
+    }
+
+    /** Stop the server. */
+    public void stop() {
+        Fuseki.serverLog.info("Stop Fuseki (port="+port+")");
+        try { server.stop(); }
+        catch (Exception e) { throw new FusekiException(e); }
+    }
+
+    /** Wait for the server to exit. This call is blocking. */
+    public void join() {
+        try { server.join(); }
+        catch (Exception e) { throw new FusekiException(e); }
+    }
+
+    /** FusekiServer.Builder */
+    public static class Builder {
+        private DataAccessPointRegistry  dataAccessPoints   = new DataAccessPointRegistry();
+        private final ServiceDispatchRegistry  serviceDispatch;
+        // Default values.
+        private int                      serverPort         = 3330;
+        private boolean                  networkLoopback    = false;
+        private boolean                  verbose            = false;
+        private boolean                  withStats          = false;
+        private boolean                  withPing           = 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                   staticContentDir   = null;
+        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}.
+         */
+        @Deprecated
+        public Builder setPort(int port) {
+            return port(port);
+        }
+
+        /** 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.serverPort = port;
+            return this;
+        }
+
+        /** Context path to Fuseki.  If it's "/" then Fuseki URL look like
+         * "http://host:port/dataset/query" else "http://host:port/path/dataset/query"
+         * @deprecated Use {@link #contextPath}.
+         */
+        @Deprecated
+        public Builder setContextPath(String path) {
+            return contextPath(path);
+        }
+
+        /** Context path to Fuseki.  If it's "/" then Fuseki URL look like
+         * "http://host:port/dataset/query" else "http://host:port/path/dataset/query"
+         */
+        public Builder contextPath(String path) {
+            requireNonNull(path, "path");
+            this.contextPath = path;
+            return this;
+        }
+
+        /** Restrict the server to only responding to the localhost interface.
+         *  @deprecated Use {@link #networkLoopback}.
+         */
+        @Deprecated
+        public Builder setLoopback(boolean loopback) {
+            return loopback(loopback);
+        }
+
+        /** Restrict the server to only responding to the localhost interface. */
+        public Builder loopback(boolean loopback) {
+            this.networkLoopback = loopback;
+            return this;
+        }
+
+        /** Set the location (filing system directory) to serve static file from.
+         *  @deprecated Use {@link #staticFileBase}.
+         */
+        @Deprecated
+        public Builder setStaticFileBase(String directory) {
+            return staticFileBase(directory);
+        }
+
+        /** 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.
+         *  @deprecated Use {@link #staticFileBase}.
+         */
+        @Deprecated
+        public Builder setSecurityHandler(SecurityHandler securityHandler) {
+            return securityHandler(securityHandler);
+        }
+
+        /** 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 variety of proven security options.
+         */
+        public Builder securityHandler(SecurityHandler securityHandler) {
+            requireNonNull(securityHandler, "securityHandler");
+            this.securityHandler = securityHandler;
+            return this;
+        }
+
+        /** Set verbose logging
+         *  @deprecated Use {@link #verbose(boolean)}.
+         */
+        @Deprecated
+        public Builder setVerbose(boolean verbose) {
+            return verbose(verbose);
+        }
+
+        /** Set verbose logging */
+        public Builder verbose(boolean verbose) {
+            this.verbose = verbose;
+            return this;
+        }
+
+        /** Add the "/$/stats" servlet that responds with stats about the server,
+         * including counts of all calls made.
+         */
+        public Builder enableStats(boolean withStats) {
+            this.withStats = withStats;
+            return this;
+        }
+
+        /** Add the "/$/ping" servlet that responds to HTTP very efficiently.
+         * This is useful for testing whether a server is alive, for example, from a load balancer.
+         */
+        public Builder enablePing(boolean withPing) {
+            this.withPing = withPing;
+            return this;
+        }
+
+        /**
+         * 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");
+            requireNonNull(dataset, "dataset");
+            return add(name, dataset, true);
+        }
+
+        /**
+         * 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 and enabling
+         * update if allowUpdate=true.
+         */
+        public Builder add(String name, DatasetGraph dataset, boolean allowUpdate) {
+            requireNonNull(name, "name");
+            requireNonNull(dataset, "dataset");
+            DataService dSrv = FusekiBuilder.buildDataServiceStd(dataset, allowUpdate);
+            return add(name, dSrv);
+        }
+
+        /** Add a data service that includes dataset and service names.
+         * A {@link DataService} allows for choices of the various endpoint names.
+         */
+        public Builder add(String name, DataService dataService) {
+            requireNonNull(name, "name");
+            requireNonNull(dataService, "dataService");
+            return add$(name, dataService);
+        }
+
+        private Builder add$(String name, DataService dataService) {
+            name = DataAccessPoint.canonical(name);
+            if ( dataAccessPoints.isRegistered(name) )
+                throw new FusekiConfigException("Data service name already registered: "+name);
+            DataAccessPoint dap = new DataAccessPoint(name, dataService);
+            dataAccessPoints.register(dap);
+            return this;
+        }
+
+        /**
+         * 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");
+            Model model = AssemblerUtils.readAssemblerFile(filename);
+
+            // Process server context
+            Resource server = GraphUtils.getResourceByType(model, FusekiVocab.tServer);
+            if ( server != null )
+                AssemblerUtils.setContext(server, Fuseki.getContext()) ;
+
+            // Process services, whether via server ja:services or, if absent, by finding by type.
+            List<DataAccessPoint> x = FusekiConfig.servicesAndDatasets(model);
+            // Unbundle so that they accumulate.
+            x.forEach(dap->add(dap.getName(), dap.getDataService()));
+            return this;
+        }
+
+        /**
+         * 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");
+            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 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");
+            requireNonNull(filter, "filter");
+            filters.add(Pair.create(pathSpec, filter));
+            return this;
+        }
+
+        /**
+         * Add an operation and handler to the server. This does not enable it for any dataset.
+         * <p>
+         * To associate an operation with a dataset, call {@link #addOperation} after adding the dataset.
+         *
+         * @see #addOperation
+         */
+        public Builder registerOperation(Operation operation, ActionService handler) {
+            registerOperation(operation, null, handler);
+            return this;
+        }
+
+        /**
+         * Add an operation to the server, together with its triggering Content-Type (may be null) and servlet handler.
+         * <p>
+         * To associate an operation with a dataset, call {@link #addOperation} after adding the dataset.
+         *
+         * @see #addOperation
+         */
+        public Builder registerOperation(Operation operation, String contentType, ActionService handler) {
+            Objects.requireNonNull(operation, "operation");
+            if ( handler == null )
+                serviceDispatch.unregister(operation);
+            else    
+                serviceDispatch.register(operation, contentType, handler);
+            return this;
+        }
+
+        /**
+         * Create an endpoint on the dataset.
+         * The operation must already be registered with the builder.
+         * @see #registerOperation(Operation, ActionService)
+         */
+        public Builder addOperation(String datasetName, String endpointName, Operation operation) {
+            Objects.requireNonNull(datasetName, "datasetName");
+            Objects.requireNonNull(endpointName, "endpointName");
+
+            String name = DataAccessPoint.canonical(datasetName);
+
+            if ( ! serviceDispatch.isRegistered(operation) )
+                throw new FusekiConfigException("Operation not registered: "+operation.getName());
+
+            if ( ! dataAccessPoints.isRegistered(name) )
+                throw new FusekiConfigException("Dataset not registered: "+datasetName);
+            DataAccessPoint dap = dataAccessPoints.get(name);
+            FusekiBuilder.addServiceEP(dap.getDataService(), operation, endpointName);
+            return this;
+        }
+
+        /**
+         * Build a server according to the current description.
+         */
+        public FusekiServer build() {
+            ServletContextHandler handler = buildFusekiContext();
+            // Use HandlerCollection for several ServletContextHandlers and thus several ServletContext.
+            Server server = jettyServer(serverPort, networkLoopback);
+            server.setHandler(handler);
+            return new FusekiServer(serverPort, server);
+        }
+
+        /** Build one configured Fuseki in one unit - same ServletContext, same dispatch ContextPath */  
+        private ServletContextHandler buildFusekiContext() {
+            ServletContextHandler handler = buildServletContext(contextPath);
+            ServletContext cxt = handler.getServletContext();
+            Fuseki.setVerbose(cxt, verbose);
+            servletAttr.forEach((n,v)->cxt.setAttribute(n, v));
+            // Clone to isolate from any future changes.
+            ServiceDispatchRegistry.set(cxt, new ServiceDispatchRegistry(serviceDispatch));
+            DataAccessPointRegistry.set(cxt, new DataAccessPointRegistry(dataAccessPoints));
+            JettyLib.setMimeTypes(handler);
+            servletsAndFilters(handler);
+            // Start services.
+            DataAccessPointRegistry.get(cxt).forEach((name, dap)->dap.getDataService().goActive());
+            return handler;
+        }
+        
+        /** 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(Fuseki.servletRequestLogName);
+            context.setErrorHandler(new FusekiErrorHandler1());
+            context.setContextPath(contextPath);
+            if ( securityHandler != null )
+                context.setSecurityHandler(securityHandler);
+            return context;
+        }
+
+        /** Add servlets and servlet filters, including the {@link FusekiFilter} */ 
+        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();
+            addFilter(context, "/*", ff);
+
+            if ( withStats )
+                addServlet(context, "/$/stats", new ActionStats());
+            if ( withPing )
+                addServlet(context, "/$/ping", new ActionPing());
+
+            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, "/");
+            }
+        }
+
+        private static void addServlet(ServletContextHandler context, String pathspec, HttpServlet httpServlet) {
+            ServletHolder sh = new ServletHolder(httpServlet);
+            context.addServlet(sh, pathspec);
+        }
+
+        private void addFilter(ServletContextHandler context, String pathspec, Filter filter) {
+            FilterHolder h = new FilterHolder(filter);
+            context.addFilter(h, pathspec, null);
+        }
+
+        /** Jetty server with one connector/port. */
+        private static Server jettyServer(int port, boolean loopback) {
+            Server 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(1024 * 1024);
+            // Do not add "Server: Jetty(....) when not a development system.
+            if ( ! Fuseki.outputJettyServerHeader )
+                f1.getHttpConfiguration().setSendServerVersion(false);
+            ServerConnector connector = new ServerConnector(server, f1);
+            connector.setPort(port);
+            server.addConnector(connector);
+            if ( loopback )
+                connector.setHost("localhost");
+            return server;
+        }
+    }
+}