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/07 12:02:17 UTC

[05/16] jena git commit: JENA-1585: Fuseki core reorg

JENA-1585: Fuseki core reorg


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

Branch: refs/heads/master
Commit: 1d41d2ce4975ebc34e8a4dd4ff67cf1100421856
Parents: 720a198
Author: Andy Seaborne <an...@apache.org>
Authored: Sat Aug 4 21:56:23 2018 +0100
Committer: Andy Seaborne <an...@apache.org>
Committed: Sun Aug 5 12:47:58 2018 +0100

----------------------------------------------------------------------
 .../apache/jena/fuseki/cmds/FusekiBasicCmd.java |   2 +-
 .../java/org/apache/jena/fuseki/FusekiLib.java  |  99 ----
 .../org/apache/jena/fuseki/FusekiLogging.java   | 190 --------
 .../jena/fuseki/FusekiNotFoundException.java    |  26 --
 .../jena/fuseki/FusekiRequestException.java     |  57 ---
 .../jena/fuseki/build/FusekiBuildLib.java       | 125 +++++
 .../apache/jena/fuseki/build/FusekiBuilder.java |  17 +-
 .../apache/jena/fuseki/build/FusekiConfig.java  |   8 +-
 .../org/apache/jena/fuseki/build/Template.java  |   2 +-
 .../org/apache/jena/fuseki/cmd/FusekiCmd.java   |   8 +-
 .../apache/jena/fuseki/ctl/ActionAsyncTask.java |  57 +++
 .../apache/jena/fuseki/ctl/ActionBackup.java    |  65 +++
 .../jena/fuseki/ctl/ActionBackupList.java       |  94 ++++
 .../jena/fuseki/ctl/ActionContainerItem.java    | 118 +++++
 .../org/apache/jena/fuseki/ctl/ActionCtl.java   |  85 ++++
 .../org/apache/jena/fuseki/ctl/ActionPing.java  |  77 ++++
 .../org/apache/jena/fuseki/ctl/ActionSleep.java |  94 ++++
 .../org/apache/jena/fuseki/ctl/ActionStats.java | 203 +++++++++
 .../org/apache/jena/fuseki/ctl/ActionTasks.java | 125 +++++
 .../java/org/apache/jena/fuseki/ctl/Async.java  |  61 +++
 .../java/org/apache/jena/fuseki/ctl/Backup.java | 131 ++++++
 .../apache/jena/fuseki/ctl/JsonConstCtl.java    |  29 ++
 .../org/apache/jena/fuseki/ctl/TaskBase.java    |  43 ++
 .../apache/jena/fuseki/jetty/JettyFuseki.java   |   2 +-
 .../apache/jena/fuseki/mgt/ActionAsyncTask.java |  56 ---
 .../apache/jena/fuseki/mgt/ActionBackup.java    |  64 ---
 .../jena/fuseki/mgt/ActionBackupList.java       |  94 ----
 .../jena/fuseki/mgt/ActionContainerItem.java    | 118 -----
 .../org/apache/jena/fuseki/mgt/ActionCtl.java   |  85 ----
 .../apache/jena/fuseki/mgt/ActionDatasets.java  |   7 +-
 .../org/apache/jena/fuseki/mgt/ActionItem.java  |   1 +
 .../org/apache/jena/fuseki/mgt/ActionLogs.java  |   1 +
 .../org/apache/jena/fuseki/mgt/ActionPing.java  |  77 ----
 .../jena/fuseki/mgt/ActionServerStatus.java     |  11 +-
 .../org/apache/jena/fuseki/mgt/ActionSleep.java |  94 ----
 .../org/apache/jena/fuseki/mgt/ActionStats.java | 202 ---------
 .../org/apache/jena/fuseki/mgt/ActionTasks.java | 125 -----
 .../java/org/apache/jena/fuseki/mgt/Async.java  |  61 ---
 .../java/org/apache/jena/fuseki/mgt/Backup.java | 131 ------
 .../org/apache/jena/fuseki/mgt/DumpServlet.java |  14 +-
 .../org/apache/jena/fuseki/mgt/JsonConst.java   |  53 ---
 .../apache/jena/fuseki/mgt/JsonDescription.java |  12 +-
 .../org/apache/jena/fuseki/mgt/MgtConst.java    |  21 +
 .../apache/jena/fuseki/mgt/ServiceMXBean.java   |  32 ++
 .../org/apache/jena/fuseki/mgt/TaskBase.java    |  43 --
 .../jena/fuseki/migrate/GraphLoadUtils.java     |  71 ---
 .../jena/fuseki/migrate/StreamRDFLimited.java   |  62 ---
 .../apache/jena/fuseki/server/FusekiEnv.java    | 168 -------
 .../server/FusekiServerEnvironmentInit.java     |  53 ---
 .../fuseki/server/FusekiServerListener.java     | 105 -----
 .../apache/jena/fuseki/server/FusekiSystem.java | 453 ------------------
 .../jena/fuseki/server/ServiceMXBean.java       |  32 --
 .../fuseki/server/ShiroEnvironmentLoader.java   | 163 -------
 .../apache/jena/fuseki/server/SystemState.java  | 108 -----
 .../jena/fuseki/servlets/NullOutputStream.java  |  53 ---
 .../jena/fuseki/servlets/REST_Quads_RW.java     |   2 +
 .../jena/fuseki/servlets/SPARQL_GSP_RW.java     |   4 +-
 .../fuseki/servlets/SPARQL_QueryGeneral.java    |   2 +-
 .../apache/jena/fuseki/servlets/ServletOps.java |   3 +-
 .../org/apache/jena/fuseki/servlets/Upload.java | 164 -------
 .../jena/fuseki/servlets/UploadDetails.java     |  86 ----
 .../jena/fuseki/system/FusekiLogging.java       | 191 ++++++++
 .../jena/fuseki/system/GraphLoadUtils.java      |  71 +++
 .../jena/fuseki/system/StreamRDFLimited.java    |  62 +++
 .../org/apache/jena/fuseki/system/Upload.java   | 168 +++++++
 .../jena/fuseki/system/UploadDetails.java       |  86 ++++
 .../apache/jena/fuseki/webapp/FusekiEnv.java    | 168 +++++++
 .../webapp/FusekiServerEnvironmentInit.java     |  53 +++
 .../fuseki/webapp/FusekiServerListener.java     | 108 +++++
 .../apache/jena/fuseki/webapp/FusekiSystem.java | 454 +++++++++++++++++++
 .../fuseki/webapp/ShiroEnvironmentLoader.java   | 163 +++++++
 .../apache/jena/fuseki/webapp/SystemState.java  | 112 +++++
 .../org/apache/jena/fuseki/core/config.ttl      |  30 ++
 .../org/apache/jena/fuseki/core/shiro.ini       |  37 ++
 .../jena/fuseki/core/templates/config-mem       |  27 ++
 .../jena/fuseki/core/templates/config-service   |  23 +
 .../jena/fuseki/core/templates/config-tdb       |  31 ++
 .../jena/fuseki/core/templates/config-tdb-dir   |  30 ++
 .../jena/fuseki/core/templates/config-tdb-mem   |  30 ++
 .../jena/fuseki/core/templates/config-tdb2      |  31 ++
 .../jena/fuseki/core/templates/config-tdb2-dir  |  30 ++
 .../jena/fuseki/core/templates/config-tdb2-mem  |  30 ++
 .../org/apache/jena/fuseki/server/config.ttl    |  30 --
 .../org/apache/jena/fuseki/server/shiro.ini     |  37 --
 .../jena/fuseki/server/templates/config-mem     |  27 --
 .../jena/fuseki/server/templates/config-service |  23 -
 .../jena/fuseki/server/templates/config-tdb     |  31 --
 .../jena/fuseki/server/templates/config-tdb-dir |  30 --
 .../jena/fuseki/server/templates/config-tdb-mem |  30 --
 .../jena/fuseki/server/templates/config-tdb2    |  31 --
 .../fuseki/server/templates/config-tdb2-dir     |  30 --
 .../fuseki/server/templates/config-tdb2-mem     |  30 --
 .../src/main/webapp/WEB-INF/web.xml             | 124 ++---
 .../apache/jena/fuseki/AbstractFusekiTest.java  |   8 +-
 .../java/org/apache/jena/fuseki/ServerCtl.java  |   6 +-
 .../java/org/apache/jena/fuseki/ServerTest.java |   2 +-
 .../java/org/apache/jena/fuseki/TS_Fuseki.java  |  11 +-
 .../java/org/apache/jena/fuseki/TestAdmin.java  |   8 +-
 .../java/org/apache/jena/fuseki/TestAuth.java   |   6 +-
 .../org/apache/jena/fuseki/TestBuilder.java     |  68 +++
 .../jena/fuseki/TestDatasetAccessorHTTP.java    | 318 +++++++++++++
 .../fuseki/TestDatasetGraphAccessorHTTP.java    |  40 ++
 .../org/apache/jena/fuseki/TestDatasetOps.java  |   2 +-
 .../org/apache/jena/fuseki/TestFileUpload.java  |   2 -
 .../java/org/apache/jena/fuseki/TestHttpOp.java | 198 ++++++++
 .../apache/jena/fuseki/TestHttpOperations.java  |   2 -
 .../apache/jena/fuseki/build/TestBuilder.java   |  66 ---
 .../fuseki/http/TestDatasetAccessorHTTP.java    | 320 -------------
 .../http/TestDatasetGraphAccessorHTTP.java      |  41 --
 .../org/apache/jena/fuseki/http/TestHttpOp.java | 199 --------
 .../org/apache/jena/fuseki/package-info.java    |  32 ++
 .../jena/fuseki/embedded/FusekiServer.java      |  15 +-
 .../jena/fuseki/embedded/TS_EmbeddedFuseki.java |   2 -
 .../fuseki/embedded/TestFusekiTestAuth.java     |   2 -
 114 files changed, 4098 insertions(+), 4157 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jena/blob/1d41d2ce/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 f1680b6..010b391 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
@@ -38,12 +38,12 @@ 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.FusekiLogging;
 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 ;

http://git-wip-us.apache.org/repos/asf/jena/blob/1d41d2ce/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/FusekiLib.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/FusekiLib.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/FusekiLib.java
index 2da827b..37162e8 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/FusekiLib.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/FusekiLib.java
@@ -35,16 +35,11 @@ import org.apache.jena.atlas.web.ContentType ;
 import org.apache.jena.atlas.web.HttpException;
 import org.apache.jena.ext.com.google.common.collect.ArrayListMultimap;
 import org.apache.jena.ext.com.google.common.collect.Multimap;
-import org.apache.jena.fuseki.server.SystemState ;
 import org.apache.jena.fuseki.servlets.HttpAction ;
 import org.apache.jena.graph.Graph ;
 import org.apache.jena.graph.Node ;
 import org.apache.jena.graph.Triple ;
 import org.apache.jena.query.* ;
-import org.apache.jena.rdf.model.Literal ;
-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.RDFConnectionRemote;
 import org.apache.jena.riot.Lang ;
 import org.apache.jena.riot.RDFLanguages ;
@@ -53,7 +48,6 @@ import org.apache.jena.shared.PrefixMapping ;
 import org.apache.jena.sparql.core.DatasetGraph ;
 import org.apache.jena.sparql.core.Quad ;
 import org.apache.jena.sparql.util.Convert ;
-import org.apache.jena.vocabulary.RDFS ;
 
 public class FusekiLib {
     // ==> ActionLib
@@ -174,99 +168,6 @@ public class FusekiLib {
         pmapDest.withDefaultMappings(pmapSrc) ;
     }
 
-    // ---- Helper code
-    public static ResultSet query(String string, Model m) {
-        return query(string, m, null, null) ;
-    }
-
-    public static ResultSet query(String string, Dataset ds) {
-        return query(string, ds, null, null) ;
-    }
-
-    public static ResultSet query(String string, Model m, String varName, RDFNode value) {
-        Query query = QueryFactory.create(SystemState.PREFIXES + string) ;
-        QuerySolutionMap initValues = null ;
-        if ( varName != null )
-            initValues = querySolution(varName, value) ;
-        try ( QueryExecution qExec = QueryExecutionFactory.create(query, m, initValues) ) {
-            return ResultSetFactory.copyResults(qExec.execSelect()) ;
-        }
-    }
-
-    public static ResultSet query(String string, Dataset ds, String varName, RDFNode value) {
-        Query query = QueryFactory.create(SystemState.PREFIXES + string) ;
-        QuerySolutionMap initValues = null ;
-        if ( varName != null )
-            initValues = querySolution(varName, value) ;
-        try ( QueryExecution qExec = QueryExecutionFactory.create(query, ds, initValues) ) {
-            return ResultSetFactory.copyResults(qExec.execSelect()) ;
-        }
-    }
-
-    private static QuerySolutionMap querySolution(String varName, RDFNode value) {
-        QuerySolutionMap qsm = new QuerySolutionMap() ;
-        querySolution(qsm, varName, value) ;
-        return qsm ;
-    }
-
-    public static QuerySolutionMap querySolution(QuerySolutionMap qsm, String varName, RDFNode value) {
-        qsm.add(varName, value) ;
-        return qsm ;
-    }
-    
-    public static RDFNode getOne(Resource svc, String property) {
-        ResultSet rs = FusekiLib.query("SELECT * { ?svc " + property + " ?x}", svc.getModel(), "svc", svc) ;
-        if ( !rs.hasNext() )
-            throw new FusekiConfigException("No property '" + property + "' for service " + FusekiLib.nodeLabel(svc)) ;
-        RDFNode x = rs.next().get("x") ;
-        if ( rs.hasNext() )
-            throw new FusekiConfigException("Multiple properties '" + property + "' for service " + FusekiLib.nodeLabel(svc)) ;
-        return x ;
-    }
-    
-    // Node presentation
-    public static String nodeLabel(RDFNode n) {
-        if ( n == null )
-            return "<null>" ;
-        if ( n instanceof Resource )
-            return strForResource((Resource)n) ;
-    
-        Literal lit = (Literal)n ;
-        return lit.getLexicalForm() ;
-    }
-
-    public static String strForResource(Resource r) {
-        return strForResource(r, r.getModel()) ;
-    }
-
-    public static String strForResource(Resource r, PrefixMapping pm) {
-        if ( r == null )
-            return "NULL " ;
-        if ( r.hasProperty(RDFS.label) ) {
-            RDFNode n = r.getProperty(RDFS.label).getObject() ;
-            if ( n instanceof Literal )
-                return ((Literal)n).getString() ;
-        }
-    
-        if ( r.isAnon() )
-            return "<<blank node>>" ;
-    
-        if ( pm == null )
-            pm = r.getModel() ;
-    
-        return strForURI(r.getURI(), pm) ;
-    }
-
-    public static String strForURI(String uri, PrefixMapping pm) {
-        if ( pm != null ) {
-            String x = pm.shortForm(uri) ;
-    
-            if ( !x.equals(uri) )
-                return x ;
-        }
-        return "<" + uri + ">" ;
-    }
-
     /** Choose an unused port for a server to listen on */
     public static int choosePort() {
         try (ServerSocket s = new ServerSocket(0)) {

http://git-wip-us.apache.org/repos/asf/jena/blob/1d41d2ce/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/FusekiLogging.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/FusekiLogging.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/FusekiLogging.java
deleted file mode 100644
index 6eb669b..0000000
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/FusekiLogging.java
+++ /dev/null
@@ -1,190 +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;
-
-import java.io.File ;
-import java.net.URL ;
-
-import org.apache.jena.atlas.lib.StrUtils ;
-import org.apache.jena.atlas.logging.LogCtl ;
-import org.apache.jena.fuseki.server.FusekiEnv ;
-import org.apache.jena.riot.SysRIOT ;
-import org.apache.log4j.PropertyConfigurator ;
-import org.apache.log4j.helpers.Loader ;
-
-public class FusekiLogging
-{
-    // This class must not have static constants, or otherwise not "Fuseki.*"
-    // or any class else where that might kick off logging.  Otherwise, the 
-    // setLogging is pointless (it's already set).
-    // PlanB - reinitialize logging regardless on first call. 
-    
-    // Set logging.
-    // 1/ Use log4j.configuration if defined.
-    // 2/ Use file:log4j.properties if exists
-    // 3/ Use log4j.properties on the classpath.
-    // 4/ Use built-in org/apache/jena/fuseki/log4j.properties on the classpath.
-    // 5/ Use Built in string
-
-    /** Places for the log4j properties file at (3) */ 
-    private static final String[] resourcesForLog4jProperties = {
-        // Hmm - 
-        "log4j.properties",
-        "org/apache/jena/fuseki/log4j.properties"
-    } ;
-    
-    private static final boolean LogLogging     = false ;
-    private static boolean loggingInitialized   = false ;
-    private static boolean allowLoggingReset    = true ;
-    
-    /**
-     * Switch off logging setting. 
-     * Used by the embedded server so that the application's
-     * logging setup is not overwritten.
-     */
-    public static synchronized void allowLoggingReset(boolean value) {
-        allowLoggingReset = value ;
-    }
-    
-    /** Set up logging - standalone and war packaging */
-    public static synchronized void setLogging() {
-        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.
-        String x = System.getProperty("log4j.configuration", null) ;
-        logLogging("log4j.configuration = %s", x) ;
-
-        if ( x != null ) { 
-            // log4j will initialize in the usual way. This includes a value of
-            // "set", which indicates that logging was set before by some other Jena code.
-            if ( x.equals("set") )
-                Fuseki.serverLog.warn("Fuseki logging: Unexpected: Log4j was setup by some other part of Jena") ;
-            return ;
-        }
-        logLogging("Fuseki logging - setup") ;
-        // Look for a log4j.properties in the current working directory
-        // and an existing FUSEKI_BASE for easy customization.
-        String fn1 = "log4j.properties" ;
-        String fn2 = null ;
-        
-        if ( FusekiEnv.FUSEKI_BASE != null ) 
-            fn2 = FusekiEnv.FUSEKI_BASE.toString()+"/log4j.properties" ;
-        if ( attempt(fn1) ) return ; 
-        if ( attempt(fn2) ) return ;
-        
-        // Try classpath
-        for ( String resourceName : resourcesForLog4jProperties ) {
-            // The log4j general initialization is done in a class static
-            // in LogManager so it can't be called again in any sensible manner.
-            // Instead, we include the same basic mechanism ...
-            logLogging("Fuseki logging - classpath %s", resourceName) ;
-            URL url = Loader.getResource(resourceName) ;
-            if ( url != null ) {
-                // Problem - test classes can be on the classpath (development mainly).
-                if ( url.toString().contains("-tests.jar") || url.toString().contains("test-classes") )
-                    url = null ;
-            }
-            
-            if ( url != null ) {
-                PropertyConfigurator.configure(url) ;
-                logLogging("Fuseki logging - found via classpath %s", url) ;
-                System.setProperty("log4j.configuration", url.toString()) ;
-                return ;
-            }
-        }
-        // Use builtin.
-        logLogging("Fuseki logging - Fallback log4j.properties string") ;
-        String dftLog4j = log4JsetupFallback() ;
-        LogCtl.resetLogging(dftLog4j);
-        // Stop anything attempting to do it again.
-        System.setProperty("log4j.configuration", "set") ;
-    }
-
-    private static boolean attempt(String fn) {
-        try {
-            File f = new File(fn) ;
-            if ( f.exists() ) {
-                logLogging("Fuseki logging - found file:log4j.properties") ;
-                PropertyConfigurator.configure(fn) ;
-                System.setProperty("log4j.configuration", "file:" + fn) ;
-                return true ;
-            }
-        }
-        catch (Throwable th) {}
-        return false ;
-    }
-
-    private static void logLogging(String fmt, Object ... args) {
-        if ( LogLogging ) {
-            System.out.printf(fmt, args) ; 
-            System.out.println() ;
-        }
-    }
-
-    private static String log4JsetupFallback() {
-        return StrUtils.strjoinNL
-            // Preferred: classes/log4j.properties, from src/main/resources/log4j.properties
-            // Keep these in-step.  Different usages cause different logging initalizations;
-            // if the jar is rebundled, it may loose the associated log4.properties file.
-            ("## 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",
-             //"log4j.appender.jena.plainstdout.layout.ConversionPattern=%d{HH:mm:ss} %-10c{1} %-5p %m%n",
-
-             "# Unadorned, for the 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",
-             
-             "## Most things", 
-             "log4j.rootLogger=INFO, jena.plainstdout",
-             "log4j.logger.com.hp.hpl.jena=WARN",
-             "log4j.logger.org.apache.jena=WARN",
-
-             "# Fuseki System logs.",
-             "log4j.logger." + Fuseki.serverLogName     + "=INFO",
-             "log4j.logger." + Fuseki.actionLogName     + "=INFO",
-             "log4j.logger." + Fuseki.adminLogName      + "=INFO",
-             "log4j.logger." + Fuseki.validationLogName + "=INFO",
-             "log4j.logger." + Fuseki.configLogName     + "=INFO",
-
-             "log4j.logger.org.apache.jena.tdb.loader=INFO",
-             "log4j.logger.org.eclipse.jetty=WARN" ,
-             "log4j.logger.org.apache.shiro=WARN",
-
-             "# NCSA Request Access log",
-             "log4j.additivity."+Fuseki.requestLogName   + "=false",
-             "log4j.logger."+Fuseki.requestLogName       + "=OFF, fuseki.plain",
-
-             "## Parser output", 
-             "log4j.additivity" + SysRIOT.riotLoggerName + "=false",
-             "log4j.logger." + SysRIOT.riotLoggerName + "=INFO, plainstdout"
-                ) ;
-    }
-}
-

http://git-wip-us.apache.org/repos/asf/jena/blob/1d41d2ce/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/FusekiNotFoundException.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/FusekiNotFoundException.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/FusekiNotFoundException.java
deleted file mode 100644
index be9be90..0000000
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/FusekiNotFoundException.java
+++ /dev/null
@@ -1,26 +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;
-
-import org.apache.jena.web.HttpSC ;
-
-public class FusekiNotFoundException extends FusekiRequestException
-{
-    public FusekiNotFoundException(String msg)    { super(HttpSC.NOT_FOUND_404, msg) ; }
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/1d41d2ce/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/FusekiRequestException.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/FusekiRequestException.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/FusekiRequestException.java
deleted file mode 100644
index e197be2..0000000
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/FusekiRequestException.java
+++ /dev/null
@@ -1,57 +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;
-
-import org.apache.jena.web.HttpSC ;
-
-
-public class FusekiRequestException extends FusekiException
-{
-    public static FusekiRequestException create(int code, String msg)
-    {
-        if ( code == HttpSC.NOT_FOUND_404 )
-            return new FusekiNotFoundException(msg) ;
-        return new FusekiRequestException(code, msg) ;
-    }
-    
-    private final int statusCode ;
-    private final String responseMessage ;
-    protected FusekiRequestException(int code, String msg)
-    {
-        super(msg) ;
-        this.statusCode = code ;
-        responseMessage = msg ;
-    }
-    
-    public int getStatusCode()
-    {
-        return statusCode ;
-    }
-
-    public String getResponseMessage()
-    {
-        return responseMessage ;
-    }
-
-    @Override
-    public String toString()
-    {
-        return "HTTP: "+statusCode+" "+getMessage() ;
-    }
-}

http://git-wip-us.apache.org/repos/asf/jena/blob/1d41d2ce/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
new file mode 100644
index 0000000..b0e96cf
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiBuildLib.java
@@ -0,0 +1,125 @@
+/*
+ * 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.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 ;
+import org.apache.jena.rdf.model.RDFNode ;
+import org.apache.jena.rdf.model.Resource ;
+import org.apache.jena.shared.PrefixMapping ;
+import org.apache.jena.vocabulary.RDFS ;
+
+public class FusekiBuildLib {
+
+    // ---- Helper code
+    public static ResultSet query(String string, Model m) {
+        return query(string, m, null, null) ;
+    }
+
+    public static ResultSet query(String string, Dataset ds) {
+        return query(string, ds, null, null) ;
+    }
+
+    public static ResultSet query(String string, Model m, String varName, RDFNode value) {
+        Query query = QueryFactory.create(SystemState.PREFIXES + string) ;
+        QuerySolutionMap initValues = null ;
+        if ( varName != null )
+            initValues = querySolution(varName, value) ;
+        try ( QueryExecution qExec = QueryExecutionFactory.create(query, m, initValues) ) {
+            return ResultSetFactory.copyResults(qExec.execSelect()) ;
+        }
+    }
+
+    public static ResultSet query(String string, Dataset ds, String varName, RDFNode value) {
+        Query query = QueryFactory.create(SystemState.PREFIXES + string) ;
+        QuerySolutionMap initValues = null ;
+        if ( varName != null )
+            initValues = querySolution(varName, value) ;
+        try ( QueryExecution qExec = QueryExecutionFactory.create(query, ds, initValues) ) {
+            return ResultSetFactory.copyResults(qExec.execSelect()) ;
+        }
+    }
+
+    private static QuerySolutionMap querySolution(String varName, RDFNode value) {
+        QuerySolutionMap qsm = new QuerySolutionMap() ;
+        querySolution(qsm, varName, value) ;
+        return qsm ;
+    }
+
+    public static QuerySolutionMap querySolution(QuerySolutionMap qsm, String varName, RDFNode value) {
+        qsm.add(varName, value) ;
+        return qsm ;
+    }
+
+    public static RDFNode getOne(Resource svc, String property) {
+        ResultSet rs = FusekiBuildLib.query("SELECT * { ?svc " + property + " ?x}", svc.getModel(), "svc", svc) ;
+        if ( !rs.hasNext() )
+            throw new FusekiConfigException("No property '" + property + "' for service " + FusekiBuildLib.nodeLabel(svc)) ;
+        RDFNode x = rs.next().get("x") ;
+        if ( rs.hasNext() )
+            throw new FusekiConfigException("Multiple properties '" + property + "' for service " + FusekiBuildLib.nodeLabel(svc)) ;
+        return x ;
+    }
+
+    // Node presentation
+    public static String nodeLabel(RDFNode n) {
+        if ( n == null )
+            return "<null>" ;
+        if ( n instanceof Resource )
+            return strForResource((Resource)n) ;
+    
+        Literal lit = (Literal)n ;
+        return lit.getLexicalForm() ;
+    }
+
+    public static String strForResource(Resource r) {
+        return strForResource(r, r.getModel()) ;
+    }
+
+    public static String strForResource(Resource r, PrefixMapping pm) {
+        if ( r == null )
+            return "NULL " ;
+        if ( r.hasProperty(RDFS.label) ) {
+            RDFNode n = r.getProperty(RDFS.label).getObject() ;
+            if ( n instanceof Literal )
+                return ((Literal)n).getString() ;
+        }
+    
+        if ( r.isAnon() )
+            return "<<blank node>>" ;
+    
+        if ( pm == null )
+            pm = r.getModel() ;
+    
+        return strForURI(r.getURI(), pm) ;
+    }
+
+    public static String strForURI(String uri, PrefixMapping pm) {
+        if ( pm != null ) {
+            String x = pm.shortForm(uri) ;
+    
+            if ( !x.equals(uri) )
+                return x ;
+        }
+        return "<" + uri + ">" ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/1d41d2ce/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 19f2745..166e14d 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
@@ -19,8 +19,6 @@
 package org.apache.jena.fuseki.build;
 
 import static java.lang.String.format ;
-import static org.apache.jena.fuseki.FusekiLib.nodeLabel ;
-import static org.apache.jena.fuseki.FusekiLib.query ;
 import static org.apache.jena.fuseki.server.FusekiVocab.pServiceQueryEP;
 import static org.apache.jena.fuseki.server.FusekiVocab.pServiceReadGraphStoreEP;
 import static org.apache.jena.fuseki.server.FusekiVocab.pServiceReadQuadsEP;
@@ -33,7 +31,6 @@ import org.apache.jena.assembler.Assembler ;
 import org.apache.jena.datatypes.xsd.XSDDatatype ;
 import org.apache.jena.fuseki.Fuseki ;
 import org.apache.jena.fuseki.FusekiConfigException ;
-import org.apache.jena.fuseki.FusekiLib ;
 import org.apache.jena.fuseki.server.DataAccessPoint ;
 import org.apache.jena.fuseki.server.DataService ;
 import org.apache.jena.fuseki.server.Operation ;
@@ -52,7 +49,7 @@ public class FusekiBuilder
 {
     /** Build a DataAccessPoint, including DataService, from the description at Resource svc */ 
     public static DataAccessPoint buildDataAccessPoint(Resource svc, DatasetDescriptionRegistry dsDescMap) {
-        RDFNode n = FusekiLib.getOne(svc, "fu:name") ;
+        RDFNode n = FusekiBuildLib.getOne(svc, "fu:name") ;
         if ( ! n.isLiteral() )
             throw new FusekiConfigException("Not a literal for access point name: "+FmtUtils.stringForRDFNode(n));
         Literal object = n.asLiteral() ;
@@ -110,13 +107,13 @@ public class FusekiBuilder
         return dataService ;
     }
     
-    static Dataset getDataset(Resource datasetDesc, DatasetDescriptionRegistry dsDescMap) {
+    public static Dataset getDataset(Resource datasetDesc, DatasetDescriptionRegistry dsDescMap) {
     	// check if this one already built
     	Dataset ds = dsDescMap.get(datasetDesc);
     	if (ds == null) {
     	    // Check if the description is in the model.
             if ( !datasetDesc.hasProperty(RDF.type) )
-                throw new FusekiConfigException("No rdf:type for dataset " + nodeLabel(datasetDesc)) ;
+                throw new FusekiConfigException("No rdf:type for dataset " + FusekiBuildLib.nodeLabel(datasetDesc)) ;
             ds = (Dataset)Assembler.general.open(datasetDesc) ;
     	}
     	// Some kind of check that it is "the same" dataset.  
@@ -155,18 +152,18 @@ public class FusekiBuilder
 
     public static RDFNode getOne(Resource svc, String property) {
         String ln = property.substring(property.indexOf(':') + 1) ;
-        ResultSet rs = FusekiLib.query("SELECT * { ?svc " + property + " ?x}", svc.getModel(), "svc", svc) ;
+        ResultSet rs = FusekiBuildLib.query("SELECT * { ?svc " + property + " ?x}", svc.getModel(), "svc", svc) ;
         if ( !rs.hasNext() )
-            throw new FusekiConfigException("No " + ln + " for service " + FusekiLib.nodeLabel(svc)) ;
+            throw new FusekiConfigException("No " + ln + " for service " + FusekiBuildLib.nodeLabel(svc)) ;
         RDFNode x = rs.next().get("x") ;
         if ( rs.hasNext() )
-            throw new FusekiConfigException("Multiple " + ln + " for service " + FusekiLib.nodeLabel(svc)) ;
+            throw new FusekiConfigException("Multiple " + ln + " for service " + FusekiBuildLib.nodeLabel(svc)) ;
         return x ;
     }
 
     private static void addServiceEP(DataService dataService, Operation operation, Resource svc, Property property) {
         String p = "<"+property.getURI()+">" ;
-        ResultSet rs = query("SELECT * { ?svc " + p + " ?ep}", svc.getModel(), "svc", svc) ;
+        ResultSet rs = FusekiBuildLib.query("SELECT * { ?svc " + p + " ?ep}", svc.getModel(), "svc", svc) ;
         for ( ; rs.hasNext() ; ) {
             QuerySolution soln = rs.next() ;
             String epName = soln.getLiteral("ep").getLexicalForm() ;

http://git-wip-us.apache.org/repos/asf/jena/blob/1d41d2ce/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 614b721..1f32e8c 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
@@ -37,8 +37,8 @@ import org.apache.jena.atlas.lib.IRILib ;
 import org.apache.jena.atlas.lib.StrUtils ;
 import org.apache.jena.fuseki.Fuseki ;
 import org.apache.jena.fuseki.FusekiConfigException ;
-import org.apache.jena.fuseki.FusekiLib ;
 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 ;
@@ -107,13 +107,13 @@ public class FusekiConfig {
         // Old style configuration file : server to services.
         DatasetDescriptionRegistry dsDescMap = new DatasetDescriptionRegistry();
         // ---- Services
-        ResultSet rs = FusekiLib.query("SELECT * { ?s fu:services [ list:member ?service ] }", model) ;
+        ResultSet rs = FusekiBuildLib.query("SELECT * { ?s fu:services [ list:member ?service ] }", model) ;
         List<DataAccessPoint> accessPoints = new ArrayList<>() ;
 
         if ( ! rs.hasNext() )
             // No "fu:services ( .... )" so try looking for services directly.
             // This means Fuseki2, service configuration files (no server section) work for --conf. 
-            rs = FusekiLib.query("SELECT ?service { ?service a fu:Service }", model) ;
+            rs = FusekiBuildLib.query("SELECT ?service { ?service a fu:Service }", model) ;
 
         for ( ; rs.hasNext() ; ) {
             QuerySolution soln = rs.next() ;
@@ -215,7 +215,7 @@ public class FusekiConfig {
         
         ds.begin(ReadWrite.WRITE) ;
         try {
-            ResultSet rs = FusekiLib.query(qs, ds) ;
+            ResultSet rs = FusekiBuildLib.query(qs, ds) ;
 
     //        ResultSetFormatter.out(rs); 
     //        ((ResultSetRewindable)rs).reset();

http://git-wip-us.apache.org/repos/asf/jena/blob/1d41d2ce/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/Template.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/Template.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/Template.java
index 6c8ca47..d5057e1 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/Template.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/Template.java
@@ -20,7 +20,7 @@ package org.apache.jena.fuseki.build;
 
 import java.nio.file.Path ;
 
-import org.apache.jena.fuseki.server.FusekiEnv ;
+import org.apache.jena.fuseki.webapp.FusekiEnv;
 
 public class Template
 {

http://git-wip-us.apache.org/repos/asf/jena/blob/1d41d2ce/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 57947e9..9fd5075 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
@@ -29,14 +29,14 @@ import jena.cmd.TerminationException;
 import org.apache.jena.atlas.lib.FileOps ;
 import org.apache.jena.fuseki.Fuseki ;
 import org.apache.jena.fuseki.FusekiException;
-import org.apache.jena.fuseki.FusekiLogging ;
 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.FusekiEnv ;
-import org.apache.jena.fuseki.server.FusekiSystem ;
-import org.apache.jena.fuseki.server.FusekiServerListener ;
 import org.apache.jena.fuseki.server.FusekiInitialConfig ;
+import org.apache.jena.fuseki.system.FusekiLogging;
+import org.apache.jena.fuseki.webapp.FusekiEnv;
+import org.apache.jena.fuseki.webapp.FusekiServerListener;
+import org.apache.jena.fuseki.webapp.FusekiSystem;
 import org.apache.jena.query.ARQ ;
 import org.apache.jena.query.Dataset ;
 import org.apache.jena.riot.Lang ;

http://git-wip-us.apache.org/repos/asf/jena/blob/1d41d2ce/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
new file mode 100644
index 0000000..5c3ec01
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionAsyncTask.java
@@ -0,0 +1,57 @@
+/**
+ * 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.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 ;
+
+/** Base helper class for creating async tasks on "items", based on POST  */ 
+public abstract class ActionAsyncTask extends ActionItem
+{
+    public ActionAsyncTask() { super() ; }
+    
+    @Override
+    final
+    protected void execGet(HttpAction action) {
+        ServletOps.errorMethodNotAllowed(METHOD_GET);
+    }
+
+    @Override
+    final
+    protected JsonValue execGetItem(HttpAction action) { 
+        throw new InternalErrorException("GET for AsyncTask -- Should not be here!") ;
+    }
+
+    @Override
+    final
+    protected JsonValue execPostItem(HttpAction action) {
+        Runnable task = createRunnable(action) ;
+        AsyncTask aTask = Async.execASyncTask(action, AsyncPool.get(), "backup", task) ;
+        Async.setLocationHeader(action, aTask);
+        return Async.asJson(aTask) ;
+    }
+    
+    protected abstract Runnable createRunnable(HttpAction action) ;
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/1d41d2ce/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
new file mode 100644
index 0000000..b83c736
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionBackup.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.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/1d41d2ce/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
new file mode 100644
index 0000000..509408e
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionBackupList.java
@@ -0,0 +1,94 @@
+/**
+ * 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/1d41d2ce/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionContainerItem.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionContainerItem.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionContainerItem.java
new file mode 100644
index 0000000..138b44e
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionContainerItem.java
@@ -0,0 +1,118 @@
+/**
+ * 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 javax.servlet.http.HttpServletRequest ;
+import javax.servlet.http.HttpServletResponse ;
+
+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.web.HttpSC ;
+
+/** Base for actions that are container and also have action on items */ 
+public abstract class ActionContainerItem extends ActionCtl {
+    
+    public ActionContainerItem() { super() ; }
+
+    // Redirect operations so they dispatch to perform(HttpAction)
+    @Override
+    final protected void doGet(HttpServletRequest request, HttpServletResponse response) {
+        doCommon(request, response);
+    }
+
+    @Override
+    final protected void doPost(HttpServletRequest request, HttpServletResponse response) {
+        doCommon(request, response);
+    }
+    
+    @Override
+    final protected void doHead(HttpServletRequest request, HttpServletResponse response) {
+        doCommon(request, response);
+    }
+    
+    @Override
+    final protected void doDelete(HttpServletRequest request, HttpServletResponse response) {
+        doCommon(request, response);
+    }
+    
+    @Override
+    final
+    protected void perform(HttpAction action) {
+        String method = action.request.getMethod() ;
+        if ( method.equals(METHOD_GET) )
+            execGet(action) ;
+        else if ( method.equals(METHOD_POST) )
+            execPost(action) ;
+        else if ( method.equals(METHOD_DELETE) )
+            execDelete(action) ;
+        else
+            ServletOps.error(HttpSC.METHOD_NOT_ALLOWED_405) ;
+    }
+
+    protected void execGet(HttpAction action) {
+        JsonValue v ;
+        if ( isContainerAction(action)  )
+            v = execGetContainer(action) ;
+        else
+            v = execGetItem(action) ;
+        
+        ServletOps.sendJsonReponse(action, v);
+    }
+    
+    /** GET request on the container - respond with JSON, or null for plain 200 */  
+    protected abstract JsonValue execGetContainer(HttpAction action) ;
+    /** GET request on an item in the container - respond with JSON, or null for plain 200 */  
+    protected abstract JsonValue execGetItem(HttpAction action) ;
+
+    protected void execPost(HttpAction action) {
+        JsonValue v ;
+        if ( isContainerAction(action) )
+            v = execPostContainer(action) ;
+        else
+            v = execPostItem(action) ;
+        
+        ServletOps.sendJsonReponse(action, v);
+    }
+    
+    /** POST request on the container - respond with JSON, or null for plain 200 */  
+    protected abstract JsonValue execPostContainer(HttpAction action) ;
+    /** POST request on an item in the container - respond with JSON, or null for plain 200 */  
+    protected abstract JsonValue execPostItem(HttpAction action) ;
+
+    
+    /** DELETE request */
+    protected void execDelete(HttpAction action) {
+        if ( isContainerAction(action)  )
+            execDeleteContainer(action) ;
+        else 
+            execDeleteItem(action) ;
+        ServletOps.success(action) ;
+    }
+    
+    /** DELETE request on an item in the container */
+    protected void execDeleteContainer(HttpAction action) {
+        ServletOps.errorMethodNotAllowed(METHOD_DELETE, "DELETE applied to a container") ;
+    }
+
+    /** DELETE request on an item in the container */
+    protected void execDeleteItem(HttpAction action) {
+        ServletOps.errorMethodNotAllowed(METHOD_DELETE) ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/1d41d2ce/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionCtl.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionCtl.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionCtl.java
new file mode 100644
index 0000000..756d948
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionCtl.java
@@ -0,0 +1,85 @@
+/*
+ * 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.fuseki.Fuseki ;
+import org.apache.jena.fuseki.server.DataAccessPoint ;
+import org.apache.jena.fuseki.servlets.ActionBase ;
+import org.apache.jena.fuseki.servlets.HttpAction ;
+import org.apache.jena.fuseki.servlets.ServletOps ;
+
+/** Control/admin request lifecycle */
+public abstract class ActionCtl extends ActionBase {
+
+    protected ActionCtl() { super(Fuseki.adminLog) ; }
+    
+    @Override
+    final
+    protected void execCommonWorker(HttpAction action) {
+        DataAccessPoint dataAccessPoint ;
+        
+        String datasetUri = mapRequestToDatasetName(action) ;
+        if ( datasetUri != null ) {
+            dataAccessPoint = action.getDataAccessPointRegistry().get(datasetUri) ;
+            if ( dataAccessPoint == null ) {
+                ServletOps.errorNotFound("Not found: "+datasetUri) ;
+                return ;
+            }
+        }
+        else {
+            // This is a placeholder when creating new DatasetRefs
+            // and also if addressing a container, not a dataset
+            dataAccessPoint = null ;
+        }
+        
+        action.setControlRequest(dataAccessPoint, datasetUri) ;
+        action.setEndpoint(null, null) ;   // No operation or service name.
+        executeAction(action) ;
+    }
+
+    protected String mapRequestToDatasetName(HttpAction action) {
+        return extractItemName(action) ;
+    }
+
+    // Possible intercept point 
+    protected void executeAction(HttpAction action) {
+        executeLifecycle(action) ;
+    }
+    
+    // This is the service request lifecycle.
+    final
+    protected void executeLifecycle(HttpAction action) {
+        perform(action) ;
+    }
+    
+    final
+    protected boolean isContainerAction(HttpAction action) {
+        return (action.getDataAccessPoint() == null ) ;
+    }
+    
+    protected abstract void perform(HttpAction action) ;
+
+//    /** Map request to uri in the registry.
+//     *  null means no mapping done (passthrough). 
+//     */
+//    protected String mapRequestToDataset(HttpAction action) 
+//    {
+//        return ActionLib.mapRequestToDataset(action.request.getRequestURI()) ;
+//    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/1d41d2ce/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionPing.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionPing.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionPing.java
new file mode 100644
index 0000000..2e43b22
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionPing.java
@@ -0,0 +1,77 @@
+/**
+ * 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 org.apache.jena.riot.WebContent.charsetUTF8 ;
+import static org.apache.jena.riot.WebContent.contentTypeTextPlain ;
+
+import java.io.IOException ;
+
+import javax.servlet.ServletOutputStream ;
+import javax.servlet.http.HttpServlet ;
+import javax.servlet.http.HttpServletRequest ;
+import javax.servlet.http.HttpServletResponse ;
+
+import org.apache.jena.atlas.lib.DateTimeUtils ;
+import org.apache.jena.fuseki.Fuseki ;
+import org.apache.jena.fuseki.servlets.ServletOps ;
+import org.apache.jena.web.HttpSC ;
+
+/** The ping servlet provides a low cost, uncached endpoint that can be used
+ * to determine if this component is running and responding.  For example,
+ * a nagios check should use this endpoint.    
+ */
+public class ActionPing extends HttpServlet
+{
+    // Ping is special.
+    // To avoid excessive logging and id allocation for a "noise" operation,
+    // this is a raw servlet.
+    public ActionPing() { super() ; } 
+    
+    @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 doHead(HttpServletRequest req, HttpServletResponse resp) {
+        doCommon(req, resp); 
+    }
+
+    protected void doCommon(HttpServletRequest request, HttpServletResponse response) {
+        try {
+            ServletOps.setNoCache(response) ; 
+            response.setContentType(contentTypeTextPlain);
+            response.setCharacterEncoding(charsetUTF8) ;
+            response.setStatus(HttpSC.OK_200);
+            ServletOutputStream out = response.getOutputStream() ;
+            out.println(DateTimeUtils.nowAsXSDDateTimeString());
+        } catch (IOException ex) {
+            Fuseki.serverLog.warn("ping :: IOException :: "+ex.getMessage());
+        }
+    }
+}
+
+

http://git-wip-us.apache.org/repos/asf/jena/blob/1d41d2ce/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionSleep.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionSleep.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionSleep.java
new file mode 100644
index 0000000..1857a05
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionSleep.java
@@ -0,0 +1,94 @@
+/**
+ * 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 javax.servlet.http.HttpServletRequest ;
+import javax.servlet.http.HttpServletResponse ;
+
+import org.apache.jena.atlas.json.JsonValue ;
+import org.apache.jena.atlas.lib.Lib ;
+import org.apache.jena.fuseki.async.AsyncPool ;
+import org.apache.jena.fuseki.async.AsyncTask ;
+import org.apache.jena.fuseki.servlets.HttpAction ;
+import org.apache.jena.fuseki.servlets.ServletOps ;
+import org.slf4j.Logger ;
+
+/** A task that kicks off a asynchronous operation that simply waits and exits.  For testing. */
+public class ActionSleep extends ActionCtl /* Not ActionAsyncTask - that is a container-item based. */
+{
+    public ActionSleep() { super() ; }
+    
+    // And only POST
+    @Override
+    protected void doPost(HttpServletRequest request, HttpServletResponse response) {
+        doCommon(request, response);
+    }
+
+    @Override
+    protected void perform(HttpAction action) {
+        Runnable task = createRunnable(action) ;
+        AsyncTask aTask = Async.execASyncTask(action, AsyncPool.get(), "sleep", task) ;
+        JsonValue v = Async.asJson(aTask) ;
+        Async.setLocationHeader(action, aTask);
+        ServletOps.sendJsonReponse(action, v);
+    }
+
+    protected Runnable createRunnable(HttpAction action) {
+        String name = action.getDatasetName() ;
+        if ( name == null )
+            name = "''" ;
+        
+        String interval = action.request.getParameter("interval") ;
+        int sleepMilli = 5000 ;
+        if ( interval != null )
+            try {
+                sleepMilli = Integer.parseInt(interval) ;
+            } catch (NumberFormatException ex) {
+                action.log.error(format("[%d] NumberFormatException: %s", action.id, interval)) ; 
+            }
+        action.log.info(format("[%d] Sleep %s %d ms", action.id, name, sleepMilli)) ;
+        return new SleepTask(action, sleepMilli) ;
+    }
+
+    static class SleepTask implements Runnable {
+        private final Logger log ;
+        private final long actionId ;
+        private final int sleepMilli ;
+        
+        public SleepTask(HttpAction action, int sleepMilli ) {
+            this.log = action.log ;
+            this.actionId = action.id ;
+            this.sleepMilli = sleepMilli ;
+        }
+
+        @Override
+        public void run() {
+            try {
+                log.info(format("[%d] >> Sleep start", actionId)) ;
+                Lib.sleep(sleepMilli) ;
+                log.info(format("[%d] << Sleep finish", actionId)) ;
+            } catch (Exception ex) {
+                log.info(format("[%d] **** Exception", actionId), ex) ;
+            }
+        }
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/1d41d2ce/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
new file mode 100644
index 0000000..dacd151
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionStats.java
@@ -0,0 +1,203 @@
+/**
+ * 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 static org.apache.jena.riot.WebContent.charsetUTF8 ;
+import static org.apache.jena.riot.WebContent.contentTypeTextPlain ;
+
+import java.io.IOException ;
+import java.util.Iterator ;
+import java.util.List ;
+
+import javax.servlet.ServletOutputStream ;
+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 ;
+
+public class ActionStats extends ActionContainerItem
+{
+    public ActionStats() { super() ; } 
+    
+    // This does not consult the system database for dormant etc.
+    @Override
+    protected JsonValue execGetContainer(HttpAction action) { 
+        action.log.info(format("[%d] GET stats all", action.id)) ;
+        return generateStats(action.getDataAccessPointRegistry()) ;
+    }
+
+    public static JsonObject generateStats(DataAccessPointRegistry registry) {
+        JsonBuilder builder = new JsonBuilder() ;
+        builder.startObject("top") ;
+        builder.key(MgtConst.datasets) ;
+        builder.startObject("datasets") ;
+        registry.forEach((name, access)->statsDataset(builder, access));
+        builder.finishObject("datasets") ;
+        builder.finishObject("top") ;
+        return builder.build().getAsObject() ;
+    }
+    
+    @Override
+    protected JsonValue execGetItem(HttpAction action) {
+        action.log.info(format("[%d] GET stats dataset %s", action.id, action.getDatasetName())) ;
+        
+        JsonBuilder builder = new JsonBuilder() ;
+        String datasetPath = DataAccessPoint.canonical(action.getDatasetName()) ;
+        builder.startObject("TOP") ;
+        
+        builder.key(MgtConst.datasets) ;
+        builder.startObject("datasets") ;
+        statsDataset(builder, datasetPath, action.getDataAccessPointRegistry()) ;
+        builder.finishObject("datasets") ;
+        
+        builder.finishObject("TOP") ;
+        return builder.build() ;
+    }
+    
+    public static JsonObject generateStats(DataAccessPoint access) {
+        JsonBuilder builder = new JsonBuilder() ;
+        statsDataset(builder, access) ;
+        return builder.build().getAsObject() ;
+    }
+    
+    private void statsDataset(JsonBuilder builder, String name, DataAccessPointRegistry registry) {
+        DataAccessPoint access = registry.get(name) ;
+        statsDataset(builder, access);
+    }
+    
+    private static void statsDataset(JsonBuilder builder, DataAccessPoint access) {
+        // Object started
+        builder.key(access.getName()) ;
+        DataService dSrv = access.getDataService() ;
+        builder.startObject("counters") ;
+        
+        builder.key(CounterName.Requests.getName()).value(dSrv.getCounters().value(CounterName.Requests)) ;
+        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") ;
+        
+        for ( Operation operName : dSrv.getOperations() ) {
+            List<Endpoint> endpoints = access.getDataService().getEndpoints(operName) ;
+            
+            for ( Endpoint endpoint : endpoints ) {
+                // Endpoint names are unique for a given service.
+                builder.key(endpoint.getEndpoint()) ;
+                builder.startObject() ;
+                
+                operationCounters(builder, endpoint);
+                builder.key(MgtConst.operation).value(operName.getName()) ;
+                builder.key(MgtConst.description).value(operName.getDescription());
+                
+                builder.finishObject() ;
+            }
+        }
+        builder.finishObject("endpoints") ;
+        builder.finishObject("counters") ;
+    }
+
+    private static void operationCounters(JsonBuilder builder, Endpoint operation) {
+        for (CounterName cn : operation.getCounters().counters()) {
+            Counter c = operation.getCounters().get(cn) ;
+            builder.key(cn.getName()).value(c.value()) ;
+        }
+    }
+
+    private void statsTxt(HttpServletResponse resp, DataAccessPointRegistry registry) throws IOException
+    {
+        ServletOutputStream out = resp.getOutputStream() ;
+        resp.setContentType(contentTypeTextPlain);
+        resp.setCharacterEncoding(charsetUTF8) ;
+
+        Iterator<String> iter = registry.keys().iterator() ;
+        while(iter.hasNext())
+        {
+            String ds = iter.next() ;
+            DataAccessPoint desc = registry.get(ds) ;
+            statsTxt(out, desc) ;
+            if ( iter.hasNext() )
+                out.println() ;
+        }
+        out.flush() ;
+    }
+    
+    private void statsTxt(ServletOutputStream out, DataAccessPoint desc) throws IOException
+    {
+        DataService dSrv = desc.getDataService() ;
+        out.println("Dataset: "+desc.getName()) ;
+        out.println("    Requests      = "+dSrv.getCounters().value(CounterName.Requests)) ;
+        out.println("    Good          = "+dSrv.getCounters().value(CounterName.RequestsGood)) ;
+        out.println("    Bad           = "+dSrv.getCounters().value(CounterName.RequestsBad)) ;
+
+        out.println("  SPARQL Query:") ;
+        out.println("    Request       = "+counter(dSrv, Operation.Query, CounterName.Requests)) ;
+        out.println("    Good          = "+counter(dSrv, Operation.Query, CounterName.RequestsGood)) ;
+        out.println("    Bad requests  = "+counter(dSrv, Operation.Query, CounterName.RequestsBad)) ;
+        out.println("    Timeouts      = "+counter(dSrv, Operation.Query, CounterName.QueryTimeouts)) ;
+        out.println("    Bad exec      = "+counter(dSrv, Operation.Query, CounterName.QueryExecErrors)) ;
+        out.println("    IO Errors     = "+counter(dSrv, Operation.Query, CounterName.QueryIOErrors)) ;
+
+        out.println("  SPARQL Update:") ;
+        out.println("    Request       = "+counter(dSrv, Operation.Update, CounterName.Requests)) ;
+        out.println("    Good          = "+counter(dSrv, Operation.Update, CounterName.RequestsGood)) ;
+        out.println("    Bad requests  = "+counter(dSrv, Operation.Update, CounterName.RequestsBad)) ;
+        out.println("    Bad exec      = "+counter(dSrv, Operation.Update, CounterName.UpdateExecErrors)) ;
+        
+        out.println("  Upload:") ;
+        out.println("    Requests      = "+counter(dSrv, Operation.Upload, CounterName.Requests)) ;
+        out.println("    Good          = "+counter(dSrv, Operation.Upload, CounterName.RequestsGood)) ;
+        out.println("    Bad           = "+counter(dSrv, Operation.Upload, CounterName.RequestsBad)) ;
+        
+        out.println("  SPARQL Graph Store Protocol:") ;
+        out.println("    GETs          = "+gspValue(dSrv, CounterName.HTTPget)      + " (good="+gspValue(dSrv, CounterName.HTTPgetGood)+"/bad="+gspValue(dSrv, CounterName.HTTPgetBad)+")") ;
+        out.println("    PUTs          = "+gspValue(dSrv, CounterName.HTTPput)      + " (good="+gspValue(dSrv, CounterName.HTTPputGood)+"/bad="+gspValue(dSrv, CounterName.HTTPputBad)+")") ;
+        out.println("    POSTs         = "+gspValue(dSrv, CounterName.HTTPpost)     + " (good="+gspValue(dSrv, CounterName.HTTPpostGood)+"/bad="+gspValue(dSrv, CounterName.HTTPpostBad)+")") ;
+        out.println("    PATCHs        = "+gspValue(dSrv, CounterName.HTTPpatch)    + " (good="+gspValue(dSrv, CounterName.HTTPpatchGood)+"/bad="+gspValue(dSrv, CounterName.HTTPpatchBad)+")") ;
+        out.println("    DELETEs       = "+gspValue(dSrv, CounterName.HTTPdelete)   + " (good="+gspValue(dSrv, CounterName.HTTPdeleteGood)+"/bad="+gspValue(dSrv, CounterName.HTTPdeleteBad)+")") ;
+        out.println("    HEADs         = "+gspValue(dSrv, CounterName.HTTPhead)     + " (good="+gspValue(dSrv, CounterName.HTTPheadGood)+"/bad="+gspValue(dSrv, CounterName.HTTPheadBad)+")") ;
+    }
+    
+    private long counter(DataService dSrv, Operation operation, CounterName cName) {
+        return 0 ;
+    }
+    
+    private long gspValue(DataService dSrv, CounterName cn) {
+        return  counter(dSrv, Operation.GSP_RW, cn) +
+                counter(dSrv, Operation.GSP_R, cn) ;
+    }
+
+    // We shouldn't get here - no doPost above.
+    
+    @Override
+    protected JsonValue execPostContainer(HttpAction action) {
+        throw new InternalError(METHOD_POST+" container") ;
+    }
+
+    @Override
+    protected JsonValue execPostItem(HttpAction action) {
+        throw new InternalError(METHOD_POST+" item") ;
+    }
+}
+
+

http://git-wip-us.apache.org/repos/asf/jena/blob/1d41d2ce/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionTasks.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionTasks.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionTasks.java
new file mode 100644
index 0000000..23f786a
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/ActionTasks.java
@@ -0,0 +1,125 @@
+/**
+ * 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 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.Fuseki ;
+import org.apache.jena.fuseki.async.AsyncPool ;
+import org.apache.jena.fuseki.async.AsyncTask ;
+import org.apache.jena.fuseki.servlets.ActionBase ;
+import org.apache.jena.fuseki.servlets.HttpAction ;
+import org.apache.jena.fuseki.servlets.ServletOps ;
+import org.apache.jena.web.HttpSC ;
+
+public class ActionTasks extends ActionBase //ActionContainerItem
+{
+    private static AsyncPool[] pools = { AsyncPool.get() } ; 
+    
+    public ActionTasks() { super(Fuseki.serverLog) ; }
+    
+    @Override
+    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
+        doCommon(request, response);
+    }
+
+    @Override
+    protected void doPost(HttpServletRequest request, HttpServletResponse response) {
+        doCommon(request, response);
+    }
+
+    private static String prefix = "/" ;
+    
+    @Override
+    protected void execCommonWorker(HttpAction action) {
+        String name = extractItemName(action) ;
+        if ( name != null ) {
+            if ( name.startsWith(prefix))
+                name = name.substring(prefix.length()) ; 
+            else
+                log.warn("Unexpected task name : "+name) ;
+        }
+        
+        String method = action.request.getMethod() ;
+        if ( method.equals(METHOD_GET) )
+            execGet(action, name) ;
+        else if ( method.equals(METHOD_POST) )
+            execPost(action, name) ;
+        else
+            ServletOps.error(HttpSC.METHOD_NOT_ALLOWED_405) ;
+    }
+
+    private void execGet(HttpAction action, String name) {
+        if ( name == null )
+            log.info(format("[%d] Tasks", action.id));
+        else
+            log.info(format("[%d] Task %s", action.id, name));
+
+        JsonValue responseBody = null ;
+        
+        if ( name == null ) {
+            JsonBuilder builder = new JsonBuilder() ;
+            builder.startArray() ;
+            
+            for ( AsyncPool pool : pools ) {
+                for ( AsyncTask aTask : pool.tasks() ) {
+                    //builder.value(aTask.getTaskId()) ;
+                    descOneTask(builder, aTask) ;
+                }
+            }
+            builder.finishArray() ;
+            responseBody = builder.build(); 
+        } else {
+            for ( AsyncPool pool : pools ) {
+                // Assumes first is only.
+                AsyncTask aTask = pool.getTask(name) ;
+                if ( aTask != null ) {
+                    JsonBuilder builder = new JsonBuilder() ;
+                    descOneTask(builder, aTask);
+                    responseBody = builder.build() ;
+                }
+            }
+        }
+        
+        if ( responseBody == null )
+            ServletOps.errorNotFound("Task '"+name+"' not found") ;
+        ServletOps.setNoCache(action) ; 
+        ServletOps.sendJsonReponse(action, responseBody); 
+    }
+
+    private void execPost(HttpAction action, String name) {
+        
+    }
+    
+    private static void descOneTask(JsonBuilder builder, AsyncTask aTask) {
+        builder.startObject("SingleTask") ;
+        builder.key(JsonConstCtl.task).value(aTask.displayName()) ;
+        builder.key(JsonConstCtl.taskId).value(aTask.getTaskId()) ;
+        if ( aTask.getStartPoint() != null )
+            builder.key(JsonConstCtl.started).value(aTask.getStartPoint()) ;
+        if ( aTask.getFinishPoint() != null )
+            builder.key(JsonConstCtl.finished).value(aTask.getFinishPoint()) ;
+        builder.finishObject("SingleTask") ;
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/1d41d2ce/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/Async.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/Async.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/Async.java
new file mode 100644
index 0000000..b3696c3
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/ctl/Async.java
@@ -0,0 +1,61 @@
+/**
+ * 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.http.HttpHeaders ;
+import org.apache.jena.atlas.json.JsonBuilder ;
+import org.apache.jena.atlas.json.JsonValue ;
+import org.apache.jena.fuseki.async.AsyncPool ;
+import org.apache.jena.fuseki.async.AsyncTask ;
+import org.apache.jena.fuseki.server.DataService ;
+import org.apache.jena.fuseki.servlets.HttpAction ;
+
+public class Async
+{
+    public static AsyncTask asyncTask(AsyncPool asyncPool, String displayName, DataService dataService, Runnable task, long requestId) {
+        AsyncTask asyncTask = asyncPool.submit(task, displayName, dataService, requestId) ;
+        return asyncTask ;
+    }
+    
+    public static JsonValue asJson(AsyncTask asyncTask) {
+        JsonBuilder builder = new JsonBuilder() ;
+        builder.startObject("outer") ;
+        builder.key(JsonConstCtl.taskId).value(asyncTask.getTaskId()) ;
+        if ( asyncTask.getOriginatingRequestId() > 0 )
+            builder.key(JsonConstCtl.taskRequestId).value(asyncTask.getOriginatingRequestId()) ;
+        builder.finishObject("outer") ;
+        return builder.build() ;
+    }
+    
+    public static void setLocationHeader(HttpAction action, AsyncTask asyncTask) {
+        String x = action.getRequest().getRequestURI() ;
+        if ( ! x.endsWith("/") )
+            x += "/" ;
+        x += asyncTask.getTaskId() ;
+        //String x = "/$/tasks/"+asyncTask.getTaskId() ;
+        action.getResponse().setHeader(HttpHeaders.LOCATION, x) ;
+    }
+
+    public static AsyncTask execASyncTask(HttpAction action, AsyncPool asyncPool, String displayName, Runnable runnable) {
+        AsyncTask atask = Async.asyncTask(asyncPool, displayName, action.getDataService(), runnable, action.id) ;
+        Async.setLocationHeader(action, atask); 
+        return atask ;
+    }
+}
+