http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/conneg/ConNeg.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/conneg/ConNeg.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/conneg/ConNeg.java
new file mode 100644
index 0000000..25e6a4f
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/conneg/ConNeg.java
@@ -0,0 +1,123 @@
+/*
+ * 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.conneg;
+
+import static org.apache.jena.fuseki.HttpNames.hAcceptCharset ;
+
+import javax.servlet.http.HttpServletRequest ;
+
+import org.apache.jena.atlas.web.AcceptList ;
+import org.apache.jena.atlas.web.MediaRange ;
+import org.apache.jena.atlas.web.MediaType ;
+import org.slf4j.Logger ;
+import org.slf4j.LoggerFactory ;
+
+public class ConNeg
+{
+    private static Logger log = LoggerFactory.getLogger(ConNeg.class) ;
+    // See riot.ContentNeg (client side).
+    // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1
+
+    static public MediaType parse(String contentType)
+    {
+        try {
+            return MediaType.create(contentType) ;
+        } catch (RuntimeException ex) { return null ; }
+    }
+    
+    static public MediaType match(String headerString, AcceptList offerList)
+    {
+        AcceptList l = new AcceptList(headerString) ;
+        return AcceptList.match(l, offerList) ;
+    }
+
+    /** Match a single media type against a header string */
+    public static String match(String headerString, String mediaRangeStr)
+    {
+        AcceptList l = new AcceptList(headerString) ;
+        MediaRange aItem = new MediaRange(mediaRangeStr) ;  // MediaType
+        MediaType m = l.match(aItem) ;
+        if ( m == null )
+            return null ;
+        return m.toHeaderString() ;
+    }
+    
+    /*package*/ static String[] split(String s, String splitStr)
+    {
+        String[] x = s.split(splitStr,2) ;
+        for ( int i = 0 ; i < x.length ; i++ )
+        {
+            x[i] = x[i].trim() ;
+        }
+        return x ;
+    }
+
+    public static MediaType chooseCharset(HttpServletRequest httpRequest,
+                                          AcceptList myPrefs,
+                                          MediaType defaultMediaType)
+    {
+        String a = httpRequest.getHeader(hAcceptCharset) ;
+        if ( log.isDebugEnabled() )
+            log.debug("Accept-Charset request: "+a) ;
+        
+        MediaType item = choose(a, myPrefs, defaultMediaType) ;
+        
+        if ( log.isDebugEnabled() )
+            log.debug("Charset chosen: "+item) ;
+    
+        return item ;
+    }
+
+    public static MediaType chooseContentType(HttpServletRequest httpRequest,
+                                              AcceptList myPrefs,
+                                              MediaType defaultMediaType)
+    {
+        String a = WebLib.getAccept(httpRequest) ;
+        if ( log.isDebugEnabled() )
+            log.debug("Accept request: "+a) ;
+        
+        MediaType item = choose(a, myPrefs, defaultMediaType) ;
+    
+        if ( log.isDebugEnabled() )
+            log.debug("Content type chosen: "+item) ;
+    
+        return item ;
+    }
+
+    private static MediaType choose(String headerString, AcceptList myPrefs,
+                                    MediaType defaultMediaType)
+    {
+        if ( headerString == null )
+            return defaultMediaType ;
+        
+        AcceptList headerList = new AcceptList(headerString) ;
+        
+        if ( myPrefs == null )
+        {
+            MediaType i = headerList.first() ;
+            if ( i == null ) return defaultMediaType ;
+            return i ;
+        }
+    
+        MediaType i = AcceptList.match(headerList, myPrefs) ;
+        if ( i == null )
+            return defaultMediaType ;
+        return i ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/conneg/WebLib.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/conneg/WebLib.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/conneg/WebLib.java
new file mode 100644
index 0000000..fdeb139
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/conneg/WebLib.java
@@ -0,0 +1,60 @@
+/*
+ * 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.conneg;
+
+import java.util.Enumeration ;
+
+import javax.servlet.http.HttpServletRequest ;
+
+import org.apache.jena.fuseki.HttpNames ;
+
+public class WebLib
+{
+    /** Split a string, removing whitespace around the split string.
+     * e.g. Use in splitting HTTP accept/content-type headers.  
+     */
+    public static String[] split(String s, String splitStr)
+    {
+        String[] x = s.split(splitStr,2) ;
+        for ( int i = 0 ; i < x.length ; i++ )
+        {
+            x[i] = x[i].trim() ;
+        }
+        return x ;
+    }
+
+    /** Migrate to WebLib */
+    public static String getAccept(HttpServletRequest httpRequest)
+    {
+        // There can be multiple accept headers -- note many tools don't allow 
these to be this way (e.g. wget, curl)
+        Enumeration<String> en = httpRequest.getHeaders(HttpNames.hAccept) ;
+        if ( ! en.hasMoreElements() )
+            return null ;
+        StringBuilder sb = new StringBuilder() ;
+        String sep = "" ;
+        for ( ; en.hasMoreElements() ; )
+        {
+            String x = en.nextElement() ;
+            sb.append(sep) ;
+            sep = ", " ;
+            sb.append(x) ;
+        }
+        return sb.toString() ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/mgt/ActionBackup.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/mgt/ActionBackup.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/mgt/ActionBackup.java
new file mode 100644
index 0000000..c36e8be
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/mgt/ActionBackup.java
@@ -0,0 +1,196 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.mgt ;
+
+import static java.lang.String.format ;
+
+import java.io.* ;
+import java.util.concurrent.Callable ;
+import java.util.concurrent.ExecutorService ;
+import java.util.concurrent.Executors ;
+import java.util.zip.GZIPOutputStream ;
+
+import javax.servlet.http.HttpServletRequest ;
+import javax.servlet.http.HttpServletResponse ;
+
+import org.apache.jena.atlas.io.IO ;
+import org.apache.jena.atlas.lib.FileOps ;
+import org.apache.jena.atlas.logging.Log ;
+import org.apache.jena.fuseki.FusekiException ;
+import org.apache.jena.fuseki.FusekiLib ;
+import org.apache.jena.fuseki.server.DatasetRef ;
+import org.apache.jena.fuseki.server.DatasetRegistry ;
+import org.apache.jena.fuseki.servlets.HttpAction ;
+import org.apache.jena.fuseki.servlets.ServletBase ;
+import org.apache.jena.riot.Lang ;
+import org.apache.jena.riot.RDFDataMgr ;
+import org.apache.jena.web.HttpSC ;
+
+import com.hp.hpl.jena.sparql.core.DatasetGraph ;
+import com.hp.hpl.jena.sparql.util.Utils ;
+
+public class ActionBackup extends ServletBase
+{
+    public ActionBackup() { super() ; }
+
+    // Limit to one backup at a time.
+    public static final ExecutorService backupService = 
Executors.newFixedThreadPool(1) ;
+    
+    @Override
+    protected void doPost(HttpServletRequest request, HttpServletResponse 
response) throws IOException
+    {
+        String dataset = FusekiLib.safeParameter(request, "dataset") ;
+        if ( dataset == null )
+        {
+            response.sendError(HttpSC.BAD_REQUEST_400, "Required parameter 
missing: ?dataset=") ;
+            return ;
+        }
+        
+        if ( ! dataset.startsWith("/") )
+            dataset="/"+dataset ;
+        
+        // HttpSession session = request.getSession(true) ;
+        // session.setAttribute("dataset", dataset) ;
+        // session.setMaxInactiveInterval(15*60) ; // 10 mins
+
+        boolean known = DatasetRegistry.get().isRegistered(dataset) ;
+        if (!known)
+        {
+            response.sendError(HttpSC.BAD_REQUEST_400, "No such dataset: " + 
dataset) ;
+            return ;
+        }
+        
+        long id = allocRequestId(request, response);
+        HttpAction action = new HttpAction(id, request, response, false) ;
+        DatasetRef ref = DatasetRegistry.get().get(dataset) ;
+        action.setDataset(ref);
+        scheduleBackup(action) ;
+    }
+
+    static final String BackupArea = "backups" ;  
+    
+    private void scheduleBackup(final HttpAction action)
+    {
+        String dsName = action.dsRef.name ;
+        final String ds = dsName.startsWith("/")? dsName : "/"+dsName ;
+        
+        String timestamp = Utils.nowAsString("yyyy-MM-dd_HH-mm-ss") ;
+        final String filename = BackupArea + ds + "_" + timestamp ;
+        FileOps.ensureDir(BackupArea) ;
+        
+        try {
+            final Callable<Boolean> task = new Callable<Boolean>() {
+                @Override
+                public Boolean call() throws Exception
+                {
+                    log.info(format("[%d] Start backup %s to '%s'", action.id, 
ds, filename)) ;
+                    action.beginRead() ;
+                    try {
+                        backup(action.getActiveDSG(), filename) ;
+                        log.info(format("[%d] Finish backup %s to '%s'", 
action.id, ds, filename)) ;
+                    }
+                    catch ( RuntimeException ex )
+                    {
+                        log.info(format("[%d] Exception during backup: ", 
action.id, ex.getMessage()), ex) ;
+                        return Boolean.FALSE ;
+                    }
+                    finally {
+                        action.endRead() ;
+                    }
+                    return Boolean.TRUE ;
+                }} ;
+            
+            log.info(format("[%d] Schedule backup %s to '%s'", action.id, ds, 
filename)) ;                
+            backupService.submit(task) ;
+        } 
+        //catch (FusekiException ex)
+        catch (RuntimeException ex)
+        {
+            log.warn("Unanticipated exception", ex) ;
+            try { action.response.sendError(HttpSC.INTERNAL_SERVER_ERROR_500, 
ex.getMessage()) ; }
+            catch (IOException e) { IO.exception(e) ; }
+            return ;            
+        }
+        
+        successPage(action, "Backup scheduled - see server log for details") ;
+    }
+    
+    // Share with new ServletBase.
+    protected static void successPage(HttpAction action, String message)
+    {
+        try {
+            action.response.setContentType("text/html");
+            action.response.setStatus(HttpSC.OK_200);
+            PrintWriter out = action.response.getWriter() ;
+            out.println("<html>") ;
+            out.println("<head>") ;
+            out.println("</head>") ;
+            out.println("<body>") ;
+            out.println("<h1>Success</h1>");
+            if ( message != null )
+            {
+                out.println("<p>") ;
+                out.println(message) ;
+                out.println("</p>") ;
+            }
+            out.println("</body>") ;
+            out.println("</html>") ;
+            out.flush() ;
+        } catch (IOException ex) { IO.exception(ex) ; }
+    }
+    
+    public static void backup(DatasetGraph dsg, String backupfile)
+    {
+        if ( ! backupfile.endsWith(".nq") )
+            backupfile = backupfile+".nq" ;
+        
+        OutputStream out = null ;
+        try
+        {
+            if ( true )
+            {
+                // This seems to achive about the same as "gzip -6"
+                // It's not too expensive in elapsed time but it's not zero 
cost.
+                // GZip, large buffer.
+                out = new FileOutputStream(backupfile+".gz") ;
+                out = new GZIPOutputStream(out, 8*1024) ;
+                out = new BufferedOutputStream(out) ;
+            }
+            else
+            {
+                out = new FileOutputStream(backupfile) ;
+                out = new BufferedOutputStream(out) ;
+            }
+            
+            RDFDataMgr.write(out, dsg,Lang.NQUADS) ;
+            out.close() ;
+            out = null ;
+        } 
+        catch (FileNotFoundException e)
+        {
+            Log.warn(ActionBackup.class, "File not found: "+backupfile) ;
+            throw new FusekiException("File not found: "+backupfile) ;
+        } 
+        catch (IOException e) { IO.exception(e) ; }
+        finally {
+            try { if (out != null) out.close() ; }
+            catch (IOException e) { /* ignore */ }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/mgt/ActionDataset.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/mgt/ActionDataset.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/mgt/ActionDataset.java
new file mode 100644
index 0000000..fc3d395
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/mgt/ActionDataset.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.mgt;
+
+import java.io.IOException ;
+import java.io.UnsupportedEncodingException ;
+
+import javax.servlet.ServletOutputStream ;
+import javax.servlet.http.HttpServlet ;
+import javax.servlet.http.HttpServletRequest ;
+import javax.servlet.http.HttpServletResponse ;
+import javax.servlet.http.HttpSession ;
+
+import org.apache.commons.codec.binary.Base64 ;
+import org.apache.jena.fuseki.FusekiLib ;
+import org.apache.jena.fuseki.HttpNames ;
+import org.apache.jena.fuseki.server.DatasetRegistry ;
+import org.apache.jena.web.HttpSC ;
+
+/** Log-in and choose dataset */
+public class ActionDataset extends HttpServlet
+{
+    @Override
+    protected void doPost(HttpServletRequest request, HttpServletResponse 
response) throws IOException
+    {
+//        request.getRemoteUser() ;
+//        request.getUserPrincipal() ;
+        
+        String dataset = FusekiLib.safeParameter(request, "dataset") ;
+        HttpSession session = request.getSession(true) ;
+        session.setAttribute("dataset", dataset) ;
+        session.setMaxInactiveInterval(15*60) ; // 10 mins
+        
+        boolean known = DatasetRegistry.get().isRegistered(dataset) ;
+        if ( !known )
+        {
+            response.sendError(HttpSC.BAD_REQUEST_400, "No such dataset: 
"+dataset) ;
+            return ;
+        }
+        
+        if ( true )
+        {
+            // Redirect to GET page.
+            response.setHeader(HttpNames.hLocation, PageNames.pageAfterLogin) ;
+            response.setStatus(HttpSC.SEE_OTHER_303) ;
+        }
+        else
+        {
+            // Welcome style - but HTML inline :-(
+            response.setContentType("text/html");
+            response.setStatus(HttpSC.OK_200) ;
+            ServletOutputStream out = response.getOutputStream() ;
+            out.print("<p>"+dataset+"("+known+")</p>") ;
+
+            for ( String name : DatasetRegistry.get().keys() ) {
+                out.print("<li>") ;
+                out.print(name) ;
+                out.println("</li>") ;
+            }
+            out.println("</ul>") ;
+            out.println("<p><a href=\"info\">Next</a></p>") ;
+        }
+        
+//        Cookie cookie = new Cookie("org.apache.jena.fuseki.session", 
dataset) ;
+//        // 24 hours.
+//        cookie.setMaxAge(24*60*60) ;
+        
+    }
+    
+    /**
+     * This method returns true if the HttpServletRequest contains a valid
+     * authorisation header
+     * @param req The HttpServletRequest to test
+     * @return true if the Authorisation header is valid
+     */
+
+    private boolean authenticate(HttpServletRequest req)
+    {
+        String authhead=req.getHeader("Authorization");
+
+        if(authhead!=null)
+        {
+            byte[] up = Base64.decodeBase64(authhead.substring(6)) ;
+            // Decode the authorisation String
+            String usernpass ;
+            try
+            {
+                usernpass = new String(up, "ascii") ;
+            } catch (UnsupportedEncodingException e)
+            {
+                e.printStackTrace();
+                usernpass = null ;
+            }
+            // Split the username from the password
+            String user=usernpass.substring(0,usernpass.indexOf(":"));
+            String password=usernpass.substring(usernpass.indexOf(":")+1);
+
+            if (user.equals("user") && password.equals("pass"))
+                return true;
+        }
+
+        return false;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/mgt/ManagementServer.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/mgt/ManagementServer.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/mgt/ManagementServer.java
new file mode 100644
index 0000000..c884bde
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/mgt/ManagementServer.java
@@ -0,0 +1,98 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.mgt;
+
+import static org.apache.jena.fuseki.Fuseki.serverLog ;
+
+import java.util.List ;
+
+import javax.servlet.http.HttpServlet ;
+
+import org.apache.jena.fuseki.Fuseki ;
+import org.apache.jena.fuseki.server.FusekiErrorHandler ;
+import org.apache.jena.fuseki.servlets.DumpServlet ;
+import org.eclipse.jetty.server.Connector ;
+import org.eclipse.jetty.server.Server ;
+import org.eclipse.jetty.server.nio.SelectChannelConnector ;
+import org.eclipse.jetty.servlet.ServletContextHandler ;
+import org.eclipse.jetty.servlet.ServletHolder ;
+
+public class ManagementServer
+{
+    public static Server createManagementServer(int mgtPort)
+    {
+        Fuseki.serverLog.info("Adding management functions") ;
+        
+        // Separate Jetty server
+        Server server = new Server() ;
+        
+//        BlockingChannelConnector bcConnector = new 
BlockingChannelConnector() ;
+//        bcConnector.setUseDirectBuffers(false) ;
+//        Connector connector = bcConnector ;
+        
+        Connector connector = new SelectChannelConnector() ;
+        // Ignore idle time. 
+        // If set, then if this goes off, it keeps going off and you get a lot 
of log messages.
+        connector.setMaxIdleTime(0) ; // Jetty outputs a lot of messages if 
this goes off.
+        connector.setPort(mgtPort);
+        server.addConnector(connector) ;
+        
+        ServletContextHandler context = new 
ServletContextHandler(ServletContextHandler.SESSIONS);
+        context.setErrorHandler(new FusekiErrorHandler()) ;
+        server.setHandler(context);
+        
+        // Add the server control servlet
+        addServlet(context, new MgtCmdServlet(),    "/mgt") ;
+        addServlet(context, new DumpServlet(),      "/dump") ;
+        addServlet(context, new StatsServlet(),     "/stats") ;
+        
+        return server ; 
+        // Old plan
+//      // Development : server control panel.
+//      addServlet(context, new ServerServlet(), "/server") ;
+//      addServlet(context, new ActionBackup(), "/backup") ;
+    }
+
+    // SHARE
+    private static void addServlet(ServletContextHandler context, String 
datasetPath, HttpServlet servlet, List<String> pathSpecs)
+    {
+        for ( String pathSpec : pathSpecs )
+        {
+            if ( pathSpec.endsWith("/") )
+                pathSpec = pathSpec.substring(0, pathSpec.length()-1) ;
+            if ( pathSpec.startsWith("/") )
+                pathSpec = pathSpec.substring(1, pathSpec.length()) ;
+            addServlet(context, servlet, datasetPath+"/"+pathSpec) ;
+        }
+    }
+
+    private static void addServlet(ServletContextHandler context, HttpServlet 
servlet, String pathSpec)
+    {
+        ServletHolder holder = new ServletHolder(servlet) ;
+        addServlet(context, holder, pathSpec) ;
+    }
+    
+    private static void addServlet(ServletContextHandler context, 
ServletHolder holder, String pathSpec)
+    {
+        serverLog.debug("Add servlet @ "+pathSpec) ;
+        context.addServlet(holder, pathSpec) ;
+    }
+
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/mgt/MgtCmdServlet.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/mgt/MgtCmdServlet.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/mgt/MgtCmdServlet.java
new file mode 100644
index 0000000..5385e75
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/mgt/MgtCmdServlet.java
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** A servlet that dumps its request
+ */
+
+// Could be neater - much, much neater!
+
+package org.apache.jena.fuseki.mgt ;
+
+import java.io.IOException ;
+import java.io.PrintWriter ;
+
+import javax.servlet.http.HttpServlet ;
+import javax.servlet.http.HttpServletRequest ;
+import javax.servlet.http.HttpServletResponse ;
+
+import org.apache.jena.fuseki.Fuseki ;
+import org.apache.jena.fuseki.server.DatasetRef ;
+import org.apache.jena.fuseki.server.SPARQLServer ;
+import org.apache.jena.fuseki.server.ServiceRef ;
+import org.apache.jena.web.HttpSC ;
+import org.slf4j.Logger ;
+
+import com.hp.hpl.jena.Jena ;
+import com.hp.hpl.jena.query.ARQ ;
+import com.hp.hpl.jena.tdb.TDB ;
+
+/** Control functions for a Fuskei server */
+
+public class MgtCmdServlet extends HttpServlet
+{
+    // Experimental - likely to change. 
+    private static Logger log = Fuseki.serverLog ;
+
+    public MgtCmdServlet()
+    {
+
+    }
+
+    @Override
+    public void init()
+    {
+        return ;
+    }
+
+    public static String paramCmd     = "cmd" ;
+    public static String cmdBackup    = "backup" ;          // 
&dataset=/datasetname
+    public static String cmdRestart   = "restart" ;         // Not implemented.
+    public static String cmdShutdown  = "shutdown" ;        // Server stops, 
no questions asked. (Not implemented)
+
+    ActionBackup         actionBackup = new ActionBackup() ;
+
+    @Override
+    public void doPost(HttpServletRequest req, HttpServletResponse resp) 
throws IOException
+    {
+        // Commands format:
+        // ?cmd=backup&<other args per command>
+
+        String[] args = req.getParameterValues(paramCmd) ;
+        if ( args == null ) {
+            resp.setContentType("text/plain") ;
+            resp.setStatus(HttpSC.BAD_REQUEST_400) ;
+
+            return ;
+        }
+        for ( String cmd : args ) {
+            if ( log.isInfoEnabled() )
+                log.info("Management command: " + cmd) ;
+
+            if ( cmd.equalsIgnoreCase(cmdBackup) ) {
+                actionBackup.doPost(req, resp) ;
+                continue ;
+            }
+            if ( cmd.equalsIgnoreCase(cmdRestart) ) {
+
+                continue ;
+            }
+            if ( cmd.equalsIgnoreCase(cmdShutdown) ) {
+                Fuseki.getServer().stop() ;
+                continue ;
+            }
+            log.warn("Unrecognized command : " + cmd) ;
+
+        }
+    }
+
+    @Override
+    public void doGet(HttpServletRequest req, HttpServletResponse resp)
+    {
+        try {
+            // serverLog.info("Fuseki Server Config servlet") ;
+
+            PrintWriter out = resp.getWriter() ;
+            resp.setContentType("text/plain") ;
+            SPARQLServer server = Fuseki.getServer() ;
+
+            out.println("Software:") ;
+            String fusekiVersion = Fuseki.VERSION ;
+            if ( fusekiVersion.equals("${project.version}") )
+                fusekiVersion = "(development)" ;
+
+            out.printf("  %s %s\n", Fuseki.NAME, fusekiVersion) ;
+            out.printf("  %s %s\n", TDB.NAME, TDB.VERSION) ;
+            out.printf("  %s %s\n", ARQ.NAME, ARQ.VERSION) ;
+            out.printf("  %s %s\n", Jena.NAME, Jena.VERSION) ;
+
+            // out.printf("Port: %s\n",
+            // server.getServer().getConnectors()[0].getPort()) ;
+            out.println() ;
+
+            for ( DatasetRef dsRef : server.getDatasets() ) {
+                datasetRefDetails(out, dsRef) ;
+                out.println() ;
+            }
+        }
+        catch (IOException ex) {}
+    }
+
+    private static void datasetRefDetails(PrintWriter out, DatasetRef dsRef)
+    {
+        if ( dsRef.name != null )
+            out.println("Name = " + dsRef.name) ;
+        else
+            out.println("Name = <unset>") ;
+
+        endpointDetail(out, "Query", dsRef, dsRef.query) ;
+        endpointDetail(out, "Update", dsRef, dsRef.update) ;
+        endpointDetail(out, "Upload", dsRef, dsRef.upload) ;
+        endpointDetail(out, "Graphs(Read)", dsRef, dsRef.readGraphStore) ;
+        endpointDetail(out, "Graphs(RW)", dsRef, dsRef.readWriteGraphStore) ;
+    }
+
+    private static void endpointDetail(PrintWriter out, String label, 
DatasetRef dsRef, ServiceRef service)
+    {
+        boolean first = true ;
+        out.printf("   %-15s :: ", label) ;
+
+        for ( String s : service.endpoints ) {
+            if ( !first )
+                out.print(" , ") ;
+            first = false ;
+            s = "/" + dsRef.name + "/" + s ;
+            out.print(s) ;
+        }
+        out.println() ;
+    }
+
+    @Override
+    public String getServletInfo()
+    {
+        return "Fuseki Control Servlet" ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/mgt/MgtFunctions.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/mgt/MgtFunctions.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/mgt/MgtFunctions.java
new file mode 100644
index 0000000..e43b1e2
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/mgt/MgtFunctions.java
@@ -0,0 +1,180 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.mgt;
+
+import java.util.List ;
+
+import javax.servlet.http.HttpServletRequest ;
+import javax.servlet.http.HttpSession ;
+
+import org.apache.jena.atlas.io.IndentedLineBuffer ;
+import org.apache.jena.atlas.iterator.Iter ;
+import org.apache.jena.fuseki.Fuseki ;
+import org.apache.jena.fuseki.server.DatasetRef ;
+import org.apache.jena.fuseki.server.DatasetRegistry ;
+import org.apache.jena.fuseki.server.ServiceRef ;
+
+import com.hp.hpl.jena.shared.PrefixMapping ;
+import com.hp.hpl.jena.sparql.core.DatasetGraph ;
+import com.hp.hpl.jena.sparql.core.Prologue ;
+import com.hp.hpl.jena.sparql.serializer.PrologueSerializer ;
+import com.hp.hpl.jena.tdb.store.DatasetGraphTDB ;
+
+/** Avoid code in JSPs */
+public class MgtFunctions
+{
+    /** Return the name of the current dataset */ 
+    public static String dataset(HttpServletRequest request, String dftValue)
+    {
+        String ds = dataset(request) ;
+        if ( ds == null )
+            return dftValue ;
+        return ds ;
+    }
+    
+    /** Return the name of the current dataset */ 
+    public static String dataset(HttpServletRequest request)
+    {
+        HttpSession session = request.getSession(false) ;
+        if ( session == null )
+            return "No session";
+        String ds = (String)session.getAttribute("dataset") ;
+        return ds ;
+    }
+
+    /** Return the dataset description reference for currnet dataset */  
+    public static DatasetRef datasetDesc(HttpServletRequest request)
+    {
+        HttpSession session = request.getSession(false) ;
+        if ( session == null )
+            return null ;
+        String ds = (String)session.getAttribute("dataset") ;
+        return DatasetRegistry.get().get(ds) ;
+    }
+
+    /** Return lists of datasets */ 
+    public static List<String> datasets(HttpServletRequest request)
+    {
+        return Iter.toList(DatasetRegistry.get().keys()) ;
+    }
+
+    /** Return name of */  
+    public static String actionDataset(HttpServletRequest request)
+    {
+        return PageNames.actionDatasetNames ;
+    }
+
+    // Service name getters ...
+    
+    /** Return a SPARQL query service name for the dataset */
+    public static String serviceQuery(String dataset)
+    {
+        String dft = "sparql" ; 
+        DatasetRef ref = getFromRegistry(dataset) ;
+        if ( ref == null )
+            return dft ;
+        return serviceNameOrDefault(ref.query, dft) ;
+    }
+    
+    /** Return a SPARQL update service name for the dataset */
+    public static String serviceUpdate(String dataset)
+    {
+        String dft = "update" ; 
+        DatasetRef ref = getFromRegistry(dataset) ;
+        if ( ref == null )
+            return dft ;
+        return serviceNameOrDefault(ref.update, dft) ;
+    }
+    
+    /** Return a SPARQL upload service name for the dataset */
+    public static String serviceUpload(String dataset)
+    {
+        String dft = "upload" ;
+        DatasetRef ref = getFromRegistry(dataset) ;
+        if ( ref == null )
+            return dft ;
+        return serviceNameOrDefault(ref.upload, dft) ;
+    }
+
+    /** Return a SPARQL Graph Store Protocol (Read) service name for the 
dataset */
+    public static String serviceGraphRead(String dataset)
+    {
+        String dft = "get" ;
+        DatasetRef ref = getFromRegistry(dataset) ;
+        if ( ref == null )
+            return dft ;
+        return serviceNameOrDefault(ref.readGraphStore, dft) ;
+    }
+
+    /** Return a SPARQL Graph Store Protocol (Read-Write) service name for the 
dataset */
+    public static String serviceGraphReadWrite(String dataset)
+    {
+        String dft = "data" ;
+        DatasetRef ref = getFromRegistry(dataset) ;
+        if ( ref == null )
+            return dft ;
+        return serviceNameOrDefault(ref.readWriteGraphStore, dft) ;
+    }
+
+    private static DatasetRef getFromRegistry(String dataset)
+    {
+        DatasetRegistry registry = DatasetRegistry.get() ;
+        if ( registry == null )
+        {
+            Fuseki.serverLog.warn("No dataset registry") ;
+            return null ;
+        }
+        
+        DatasetRef ref = registry.get(dataset) ;
+        if ( ref == null )
+            Fuseki.serverLog.warn("Dataset not found: "+dataset) ;
+        return ref ;
+    }
+
+    private static String serviceNameOrDefault(ServiceRef service, String 
defaultValue)
+    {
+        if ( service.endpoints.isEmpty() )
+            return defaultValue ;
+        String x = service.endpoints.get(0) ;
+        if ( x.startsWith("/") )
+            x = x.substring(1) ;
+        return x ;
+    }
+    
+    /** Return prefixes for the datasets, SPARQL syntax. */ 
+    public static String prefixes(HttpServletRequest request)
+    {
+        String dsName = dataset(request) ;
+        DatasetRef desc = getFromRegistry(dsName) ;
+        if ( desc == null )
+            return "<not found>" ;
+        DatasetGraph dsg = desc.dataset ; 
+        
+        if ( dsg instanceof DatasetGraphTDB )
+        {
+            PrefixMapping pmap = 
((DatasetGraphTDB)dsg).getPrefixes().getPrefixMapping() ;
+            Prologue prologue = new Prologue(pmap) ;
+            IndentedLineBuffer buff = new IndentedLineBuffer() ;
+            PrologueSerializer.output(buff, prologue) ;
+            buff.append("\n") ;
+            return buff.asString() ;
+        }
+        return "" ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/mgt/PageNames.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/mgt/PageNames.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/mgt/PageNames.java
new file mode 100644
index 0000000..4dc315b
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/mgt/PageNames.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.mgt;
+
+public class PageNames
+{
+    //public static final String pageControlPanel     = "/control-panel.tpl" ;
+    
+    /** The dispatch URL for the place to go after login (0.2.1 - login = 
choose dataset) */
+    public static final String pageAfterLogin      = "/sparql.tpl" ;
+    
+    /** This is the full web dispatch URL: control-panel.tpl knowns 
+     * the name indirectly because it gets it via velocity
+     * calling mgt.actionDataset.
+     */
+    public static final String actionDatasetNames   = "/$/datasets" ;
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/mgt/StatsServlet.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/mgt/StatsServlet.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/mgt/StatsServlet.java
new file mode 100644
index 0000000..e165355
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/mgt/StatsServlet.java
@@ -0,0 +1,171 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.mgt;
+
+import java.io.IOException ;
+import java.util.Iterator ;
+
+import javax.servlet.ServletOutputStream ;
+import javax.servlet.http.HttpServlet ;
+import javax.servlet.http.HttpServletRequest ;
+import javax.servlet.http.HttpServletResponse ;
+
+import org.apache.jena.atlas.json.JSON ;
+import org.apache.jena.atlas.json.JsonArray ;
+import org.apache.jena.atlas.json.JsonObject ;
+import org.apache.jena.fuseki.server.* ;
+import org.apache.jena.riot.WebContent ;
+
+public class StatsServlet extends HttpServlet
+{
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+        //throws ServletException, IOException
+    {
+        try {
+            // Conneg etc.
+            statsJSON(req, resp) ;
+        } catch (IOException e)
+        { }
+    }
+    
+    private void statsJSON(HttpServletRequest req, HttpServletResponse resp) 
throws IOException
+    {
+        ServletOutputStream out = resp.getOutputStream() ;
+        resp.setContentType(WebContent.contentTypeJSON);
+        resp.setCharacterEncoding(WebContent.charsetUTF8) ;
+
+        /*
+         * { "server" : ....   
+         *    "datasets" : {
+         *       "ds1": { counters... }
+         *       GSP stucture?
+         *         
+         */
+        
+        JsonObject obj = new JsonObject() ;
+        JsonObject datasets = new JsonObject() ;
+        JsonObject server = new JsonObject() ;
+        server.put("host", req.getLocalName()+":"+req.getLocalPort()) ;
+        
+        for ( String ds : DatasetRegistry.get().keys() )
+            statsJSON(datasets, ds) ; 
+        
+        obj.put("server", server) ;
+        obj.put("datasets", datasets) ;
+        
+        JSON.write(out, obj) ;
+        out.flush() ;
+    }
+    
+    private void statsJSON(JsonObject datasets, String ds) {
+        DatasetRef desc = DatasetRegistry.get().get(ds) ;
+        JsonObject stats = new JsonObject() ;
+        datasets.put(ds, stats) ;
+        stats.put(CounterName.Requests.name(),      
desc.getCounters().value(CounterName.Requests)) ;
+        stats.put(CounterName.RequestsGood.name(),  
desc.getCounters().value(CounterName.RequestsGood)) ;
+        stats.put(CounterName.RequestsBad.name(),   
desc.getCounters().value(CounterName.RequestsBad)) ;
+        JsonObject services = new JsonObject() ;
+
+//        JsonArray endpoints = new JsonArray() ;
+//        services.put("endpoints", endpoints) ;
+//        JsonArray srvNames = new JsonArray() ;
+//        services.put("names", srvNames) ;
+        
+        // There can be several endpoints for one service.
+        for ( ServiceRef srvRef : desc.getServiceRefs() ) {
+            JsonObject epStats = new JsonObject() ;
+            statsJSON(epStats, srvRef) ;
+            services.put(srvRef.name, epStats) ;
+            JsonArray endpoints = new JsonArray() ;
+            epStats.put("endpoints", endpoints) ;
+            for ( String ep : srvRef.endpoints) {
+                endpoints.add(ep) ;
+            }
+        }
+        stats.put("services", services) ;
+    }
+
+    private void statsJSON(JsonObject epStats, ServiceRef srvRef) {
+        for (CounterName cn : srvRef.getCounters().counters()) {
+            Counter c = srvRef.getCounters().get(cn) ;
+            epStats.put(cn.name(), c.value()) ;
+        }
+    }
+
+    private void statsTxt(HttpServletResponse resp) throws IOException
+    {
+        ServletOutputStream out = resp.getOutputStream() ;
+        resp.setContentType(WebContent.contentTypeTextPlain);
+        resp.setCharacterEncoding(WebContent.charsetUTF8) ;
+
+        Iterator<String> iter = DatasetRegistry.get().keys().iterator() ;
+        while(iter.hasNext())
+        {
+            String ds = iter.next() ;
+            DatasetRef desc = DatasetRegistry.get().get(ds) ;
+            statsTxt(out, desc) ;
+            if ( iter.hasNext() )
+                out.println() ;
+        }
+        out.flush() ;
+    }
+    private void statsTxt(ServletOutputStream out, DatasetRef desc) throws 
IOException
+    {
+        out.println("Dataset: "+desc.name) ;
+        out.println("    Requests      = 
"+desc.getCounters().value(CounterName.Requests)) ;
+        out.println("    Good          = 
"+desc.getCounters().value(CounterName.RequestsGood)) ;
+        out.println("    Bad           = 
"+desc.getCounters().value(CounterName.RequestsBad)) ;
+
+        out.println("  SPARQL Query:") ;
+        out.println("    Request       = 
"+desc.query.getCounters().value(CounterName.Requests)) ;
+        out.println("    Good          = 
"+desc.query.getCounters().value(CounterName.RequestsGood)) ;
+        out.println("    Bad requests  = 
"+desc.query.getCounters().value(CounterName.RequestsBad)) ;
+        out.println("    Timeouts      = 
"+desc.query.getCounters().value(CounterName.QueryTimeouts)) ;
+        out.println("    Bad exec      = 
"+desc.query.getCounters().value(CounterName.QueryExecErrors)) ;
+
+        out.println("  SPARQL Update:") ;
+        out.println("    Request       = 
"+desc.update.getCounters().value(CounterName.Requests)) ;
+        out.println("    Good          = 
"+desc.update.getCounters().value(CounterName.RequestsGood)) ;
+        out.println("    Bad requests  = 
"+desc.update.getCounters().value(CounterName.RequestsBad)) ;
+        out.println("    Bad exec      = 
"+desc.update.getCounters().value(CounterName.UpdateExecErrors)) ;
+        
+        out.println("  Upload:") ;
+        out.println("    Requests      = 
"+desc.upload.getCounters().value(CounterName.Requests)) ;
+        out.println("    Good          = 
"+desc.upload.getCounters().value(CounterName.RequestsGood)) ;
+        out.println("    Bad           = 
"+desc.upload.getCounters().value(CounterName.RequestsBad)) ;
+        
+        out.println("  SPARQL Graph Store Protocol:") ;
+        out.println("    GETs          = "+gspValue(desc, CounterName.GSPget)+ 
" (good="+gspValue(desc, CounterName.GSPgetGood)+"/bad="+gspValue(desc, 
CounterName.GSPgetBad)+")") ;
+        out.println("    PUTs          = "+gspValue(desc, CounterName.GSPput)+ 
" (good="+gspValue(desc, CounterName.GSPputGood)+"/bad="+gspValue(desc, 
CounterName.GSPputBad)+")") ;
+        out.println("    POSTs         = "+gspValue(desc, 
CounterName.GSPpost)+ " (good="+gspValue(desc, 
CounterName.GSPpostGood)+"/bad="+gspValue(desc, CounterName.GSPpostBad)+")") ;
+        out.println("    DELETEs       = "+gspValue(desc, 
CounterName.GSPdelete)+ " (good="+gspValue(desc, 
CounterName.GSPdeleteGood)+"/bad="+gspValue(desc, 
CounterName.GSPdeleteBad)+")") ;
+        out.println("    HEADs         = "+gspValue(desc, 
CounterName.GSPhead)+ " (good="+gspValue(desc, 
CounterName.GSPheadGood)+"/bad="+gspValue(desc, CounterName.GSPheadBad)+")") ;
+    }
+    
+    private long gspValue(DatasetRef desc, CounterName cn) {
+        long x1 = desc.readGraphStore.getCounters().value(cn) ;
+        long x2 = desc.readWriteGraphStore.getCounters().value(cn) ;
+        return x1+x2 ;
+    }
+    
+    
+}
+
+

http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/migrate/GraphLoadUtils.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/migrate/GraphLoadUtils.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/migrate/GraphLoadUtils.java
new file mode 100644
index 0000000..da16863
--- /dev/null
+++ 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/migrate/GraphLoadUtils.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.jena.fuseki.migrate;
+
+import org.apache.jena.atlas.web.TypedInputStream ;
+import org.apache.jena.fuseki.Fuseki ;
+import org.apache.jena.riot.RDFDataMgr ;
+import org.apache.jena.riot.system.StreamRDF ;
+import org.apache.jena.riot.system.StreamRDFLib ;
+
+import com.hp.hpl.jena.graph.Factory ;
+import com.hp.hpl.jena.graph.Graph ;
+import com.hp.hpl.jena.rdf.model.Model ;
+import com.hp.hpl.jena.rdf.model.ModelFactory ;
+
+/** A packaging of code to do a controlled read of a graph or model */
+
+public class GraphLoadUtils
+{
+    // ---- Model level
+    
+    public static Model readModel(String uri, int limit)
+    {
+        Graph g = Factory.createGraphMem() ;
+        readUtil(g, uri, limit) ;
+        return ModelFactory.createModelForGraph(g) ;
+    }
+    
+    public static void loadModel(Model model, String uri, int limit) 
+    {
+        Graph g = model.getGraph() ;
+        readUtil(g, uri, limit) ;
+    }
+
+    // ---- Graph level
+    
+    public static Graph readGraph(String uri, int limit)
+    {
+        Graph g = Factory.createGraphMem() ;
+        readUtil(g, uri, limit) ;
+        return g ;
+    }
+    
+    public static void loadGraph(Graph g, String uri, int limit) 
+    {
+        readUtil(g, uri, limit) ;
+    }
+    
+    // ** Worker.
+    private static void readUtil(Graph graph, String uri, int limit)
+    {
+        // We need to do this ourselves, not via riot, to use the 
webStreamManager
+        StreamRDF sink = StreamRDFLib.graph(graph) ;
+        sink = new SinkRDFLimited(sink, limit) ;
+
+        TypedInputStream input = Fuseki.webStreamManager.open(uri) ;
+        RDFDataMgr.parse(sink, input, uri) ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/migrate/Registry.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/migrate/Registry.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/migrate/Registry.java
new file mode 100644
index 0000000..ca30be1
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/migrate/Registry.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.migrate;
+
+import java.util.Collection ;
+import java.util.HashMap ;
+import java.util.Map ;
+
+public class Registry<T>
+{
+    protected Map<String, T> registry = new HashMap<String, T>() ;
+    
+    public Registry() {}
+    
+    public void put(String key, T value) { registry.put(key, value) ; }
+    
+    public T get(String key) { return registry.get(key) ; }
+    
+    public boolean isRegistered(String key) { return registry.containsKey(key) 
; }
+    public void remove(String key) { registry.remove(key) ; } 
+    public Collection<String> keys() { return registry.keySet() ; }
+    //public Iterator<String> keys() { return registry.keySet().iterator() ; }
+    
+    public int size() { return registry.size() ; }
+    public boolean isEmpty() { return registry.isEmpty() ; }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/migrate/SinkRDFLimited.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/migrate/SinkRDFLimited.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/migrate/SinkRDFLimited.java
new file mode 100644
index 0000000..514d756
--- /dev/null
+++ 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/migrate/SinkRDFLimited.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.migrate;
+
+import org.apache.jena.riot.system.StreamRDF ;
+import org.apache.jena.riot.system.StreamRDFWrapper ;
+
+import com.hp.hpl.jena.graph.Triple ;
+import com.hp.hpl.jena.sparql.core.Quad ;
+
+public class SinkRDFLimited extends StreamRDFWrapper
+{
+   private long count = 0 ;
+   private final long limit ;
+    
+    public SinkRDFLimited(StreamRDF output, long limit)
+    {
+        super(output) ;
+        this.limit = limit ;
+    }
+
+    @Override
+    public void triple(Triple triple)
+    { 
+        count++ ;
+        super.triple(triple) ;
+    }
+
+    @Override
+    public void quad(Quad quad)
+    { 
+        count++ ;
+        super.quad(quad) ;
+    }
+
+    public long getCount() { return count ; } 
+    public long getLimit() { return limit ; }
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/Counter.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/Counter.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/Counter.java
new file mode 100644
index 0000000..88d4d37
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/Counter.java
@@ -0,0 +1,34 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.server;
+
+import java.util.concurrent.atomic.AtomicLong ;
+
+/** A statistics counter */
+public class Counter
+{
+    private AtomicLong counter = new AtomicLong(0) ;
+    
+    public Counter()    {}
+    
+    public void inc()   { counter.incrementAndGet() ; } 
+    public void dec()   { counter.decrementAndGet() ; } 
+    public long value() { return counter.get() ; } 
+}
+

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

http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/CounterName.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/CounterName.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/CounterName.java
new file mode 100644
index 0000000..2952aa8
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/CounterName.java
@@ -0,0 +1,83 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.server;
+
+/** Names for all counters */ 
+public enum CounterName {
+    // There are generic names - apply to all services and datasets
+    // and specific ones.
+
+    
+    // Total request received
+    Requests("requests"),
+    // .. of which some and "good" and some are "bad".
+    // #"good" + #"bad" roughly equals #"requests"
+    // except that the total is incremented at the start, and the outcome at 
the end.
+    // There may also be short term consistency issues.
+    RequestsGood("requests.good"),
+    RequestsBad("requests.bad") ,
+    
+    // SPARQL Protocol - query and update - together with upload.  
+    
+    // Query - standard and ... 
+    QueryTimeouts("query.timeouts") ,
+    QueryExecErrors("query.execerrors") ,
+    
+    // Update - standard and ...
+    UpdateExecErrors("update.execerrors"),
+    
+    // Upload ... standard counters
+    
+    // Graph Store Protocol.
+
+    // For each HTTP method
+    GSPget("gsp.get.requests") ,
+    GSPgetGood("gsp.get.requests.good") ,
+    GSPgetBad("gsp.get.requests.bad") ,
+
+    GSPpost("gsp.post.requests") ,
+    GSPpostGood("gsp.post.requests.good") ,
+    GSPpostBad("gsp.post.requests.bad") ,
+
+    GSPdelete("gsp.delete.requests") ,
+    GSPdeleteGood("gsp.delete.requests.good") ,
+    GSPdeleteBad("gsp.delete.requests.bad") ,
+
+    GSPput("gsp.put.requests") ,
+    GSPputGood("gsp.put.requests.good") ,
+    GSPputBad("gsp.put.requests.bad") ,
+
+    GSPhead("gsp.head.requests") ,
+    GSPheadGood("gsp.head.requests.good") ,
+    GSPheadBad("gsp.head.requests.bad") ,
+
+    GSPpatch("gsp.patch.requests") ,
+    GSPpatchGood("gsp.patch.requests.good") ,
+    GSPpatchBad("gsp.patch.requests.bad") ,
+
+    GSPoptions("gsp.options.requests") ,
+    GSPoptionsGood("gsp.options.requests.good") ,
+    GSPoptionsBad("gsp.options.requests.bad") ,
+    
+    ;
+    
+    private String name ;
+    private CounterName(String name) { this.name = name ; }
+    
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/CounterSet.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/CounterSet.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/CounterSet.java
new file mode 100644
index 0000000..7d0d622
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/CounterSet.java
@@ -0,0 +1,70 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.server ;
+
+import java.util.Collection ;
+import java.util.HashMap ;
+import java.util.Map ;
+
+import org.slf4j.Logger ;
+import org.slf4j.LoggerFactory ;
+
+/** A collection of counters */
+public class CounterSet {
+    private static Logger             log      = 
LoggerFactory.getLogger(CounterSet.class) ;
+
+    private Map<CounterName, Counter> counters = new HashMap<CounterName, 
Counter>() ;
+
+    public CounterSet() {}
+
+    public Collection<CounterName> counters() {
+        return counters.keySet() ;
+    }
+
+    public void inc(CounterName c) {
+        get(c).inc() ;
+    }
+
+    public void dec(CounterName c) {
+        get(c).dec() ;
+    }
+
+    public long value(CounterName c) {
+        return get(c).value() ;
+    }
+
+    public void add(CounterName counterName) {
+        if ( counters.containsKey(counterName) ) {
+            log.warn("Duplicate counter in counter set: " + counterName) ;
+            return ;
+        }
+        counters.put(counterName, new Counter()) ;
+    }
+
+    public boolean contains(CounterName cn) {
+        return counters.containsKey(cn) ;
+    }
+
+    public Counter get(CounterName cn) {
+        Counter c = counters.get(cn) ;
+        if ( c == null )
+            log.warn("No counter in counter set: " + cn) ;
+        return c ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/Counters.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/Counters.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/Counters.java
new file mode 100644
index 0000000..4e5ca4b
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/Counters.java
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.server;
+
+/** Objects that have a counter set */ 
+public interface Counters {
+    public  CounterSet getCounters() ;
+}
+

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

http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/DatasetRef.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/DatasetRef.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/DatasetRef.java
new file mode 100644
index 0000000..040c759
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/DatasetRef.java
@@ -0,0 +1,241 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.server;
+
+import java.util.* ;
+import java.util.concurrent.atomic.AtomicLong ;
+
+import org.apache.jena.fuseki.Fuseki ;
+
+import com.hp.hpl.jena.query.ReadWrite ;
+import com.hp.hpl.jena.sparql.core.DatasetGraph ;
+
+public class DatasetRef implements DatasetMXBean, Counters
+{
+    public String name                          = null ;
+    public DatasetGraph dataset                 = null ;
+
+    public ServiceRef query                     = new ServiceRef("query") ;
+    public ServiceRef update                    = new ServiceRef("update") ;
+    public ServiceRef upload                    = new ServiceRef("upload") ;
+    public ServiceRef readGraphStore            = new ServiceRef("gspRead") ;
+    public ServiceRef readWriteGraphStore       = new 
ServiceRef("gspReadWrite") ; 
+    
+    // Dataset-level counters.
+    private final CounterSet counters           = new CounterSet() ;
+    @Override
+    public  CounterSet getCounters() { return counters ; }
+    
+    private Map<String, ServiceRef> endpoints   = new HashMap<String, 
ServiceRef>() ;
+    private List<ServiceRef> serviceRefs        = new ArrayList<ServiceRef>() ;
+    private boolean initialized = false ;
+    
+    // Two step initiation (c.f. Builder pattern)
+    // Create object - incrementally set state - call init to calculate 
internal datastructures.
+    public DatasetRef() {}
+    public void init() {
+        if ( initialized )
+            Fuseki.serverLog.warn("Already initialized: dataset = "+name) ;
+        initialized = true ;
+        initServices() ;
+    }
+    
+    @Override public String toString() { return "DatasetRef:'"+name+"'" ; }  
+    
+    private void initServices() {
+        add(query) ;
+        add(update) ;
+        add(upload) ;
+        add(readGraphStore) ;
+        add(readWriteGraphStore) ;
+        addCounters() ;
+    }
+    
+    private void add(ServiceRef srvRef) {
+        serviceRefs.add(srvRef) ;
+        for ( String ep : srvRef.endpoints )
+            endpoints.put(ep, srvRef) ;
+    }
+
+    public ServiceRef getServiceRef(String service) {
+        if ( ! initialized )
+            Fuseki.serverLog.error("Not initialized: dataset = "+name) ;
+        if ( service.startsWith("/") )
+            service = service.substring(1, service.length()) ; 
+        return endpoints.get(service) ;
+    }
+
+    public Collection<ServiceRef> getServiceRefs() {
+        return serviceRefs ;
+    }
+
+    /** Counter of active read transactions */
+    public AtomicLong   activeReadTxn           = new AtomicLong(0) ;
+    
+    /** Counter of active write transactions */
+    public AtomicLong   activeWriteTxn          = new AtomicLong(0) ;
+
+    /** Cumulative counter of read transactions */
+    public AtomicLong   totalReadTxn            = new AtomicLong(0) ;
+
+    /** Cumulative counter of writer transactions */
+    public AtomicLong   totalWriteTxn           = new AtomicLong(0) ;
+    
+//    /** Count of requests received - anyzservice */
+//    public AtomicLong   countServiceRequests    = new AtomicLong(0) ;
+//    /** Count of requests received that fail in some way */
+//    public AtomicLong   countServiceRequestsBad = new AtomicLong(0) ;
+//    /** Count of requests received that fail in some way */
+//    public AtomicLong   countServiceRequestsOK  = new AtomicLong(0) ;
+//
+//    // SPARQL Query
+//    
+//    /** Count of SPARQL Queries successfully executed */
+//    public AtomicLong   countQueryOK            = new AtomicLong(0) ;
+//    /** Count of SPARQL Queries with syntax errors */
+//    public AtomicLong   countQueryBadSyntax     = new AtomicLong(0) ;
+//    /** Count of SPARQL Queries with timeout on execution */
+//    public AtomicLong   countQueryTimeout       = new AtomicLong(0) ;
+//    /** Count of SPARQL Queries with execution errors (not timeouts) */
+//    public AtomicLong   countQueryBadExecution  = new AtomicLong(0) ;
+
+    public void startTxn(ReadWrite mode)
+    {
+        switch(mode)
+        {
+            case READ:  
+                activeReadTxn.getAndIncrement() ;
+                totalReadTxn.getAndIncrement() ;
+                break ;
+            case WRITE:
+                activeWriteTxn.getAndIncrement() ;
+                totalWriteTxn.getAndIncrement() ;
+                break ;
+        }
+    }
+    
+    public void finishTxn(ReadWrite mode)
+    {
+        switch(mode)
+        {
+            case READ:  
+                activeReadTxn.decrementAndGet() ;
+                break ;
+            case WRITE:
+                activeWriteTxn.decrementAndGet() ;
+                break ;
+        }
+    }
+
+    //TODO Need to be able to set this from the config file.  
+    public boolean allowDatasetUpdate           = false;
+    
+    public boolean allowTimeoutOverride         = false;
+    public long maximumTimeoutOverride          = Long.MAX_VALUE;
+    
+    public boolean isReadOnly()
+    {
+        return ! allowDatasetUpdate &&
+               ! update.isActive() && 
+               ! upload.isActive() &&
+               ! readWriteGraphStore.isActive()
+               ;
+    }
+    
+    // MBean
+    
+    @Override
+    public String getName()     { return name ; }
+
+    @Override public long getRequests() { 
+        return counters.value(CounterName.Requests) ;
+    }
+
+    @Override
+    public long getRequestsGood() {
+        return counters.value(CounterName.RequestsGood) ;
+    }
+    @Override
+    public long getRequestsBad() {
+        return counters.value(CounterName.RequestsBad) ;
+    }
+    
+    private void addCounters() {
+        getCounters().add(CounterName.Requests) ;
+        getCounters().add(CounterName.RequestsGood) ;
+        getCounters().add(CounterName.RequestsBad) ;
+
+        query.getCounters().add(CounterName.Requests) ;
+        query.getCounters().add(CounterName.RequestsGood) ;
+        query.getCounters().add(CounterName.RequestsBad) ;
+        query.getCounters().add(CounterName.QueryTimeouts) ;
+        query.getCounters().add(CounterName.QueryExecErrors) ;
+
+        update.getCounters().add(CounterName.Requests) ;
+        update.getCounters().add(CounterName.RequestsGood) ;
+        update.getCounters().add(CounterName.RequestsBad) ;
+        update.getCounters().add(CounterName.UpdateExecErrors) ;
+
+        upload.getCounters().add(CounterName.Requests) ;
+        upload.getCounters().add(CounterName.RequestsGood) ;
+        upload.getCounters().add(CounterName.RequestsBad) ;
+
+        addCountersForGSP(readWriteGraphStore.getCounters(), false) ;
+        if ( readGraphStore != readWriteGraphStore )
+            addCountersForGSP(readGraphStore.getCounters(), true) ;
+    }
+
+    private void addCountersForGSP(CounterSet cs, boolean readWrite) {
+        cs.add(CounterName.Requests) ;
+        cs.add(CounterName.RequestsGood) ;
+        cs.add(CounterName.RequestsBad) ;
+
+        cs.add(CounterName.GSPget) ;
+        cs.add(CounterName.GSPgetGood) ;
+        cs.add(CounterName.GSPgetBad) ;
+
+        cs.add(CounterName.GSPhead) ;
+        cs.add(CounterName.GSPheadGood) ;
+        cs.add(CounterName.GSPheadBad) ;
+
+        // Add anyway.
+        // if ( ! readWrite )
+        // return ;
+
+        cs.add(CounterName.GSPput) ;
+        cs.add(CounterName.GSPputGood) ;
+        cs.add(CounterName.GSPputBad) ;
+
+        cs.add(CounterName.GSPpost) ;
+        cs.add(CounterName.GSPpostGood) ;
+        cs.add(CounterName.GSPpostBad) ;
+
+        cs.add(CounterName.GSPdelete) ;
+        cs.add(CounterName.GSPdeleteGood) ;
+        cs.add(CounterName.GSPdeleteBad) ;
+
+        cs.add(CounterName.GSPpatch) ;
+        cs.add(CounterName.GSPpatchGood) ;
+        cs.add(CounterName.GSPpatchBad) ;
+
+        cs.add(CounterName.GSPoptions) ;
+        cs.add(CounterName.GSPoptionsGood) ;
+        cs.add(CounterName.GSPoptionsBad) ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/DatasetRegistry.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/DatasetRegistry.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/DatasetRegistry.java
new file mode 100644
index 0000000..152e8cd
--- /dev/null
+++ 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/DatasetRegistry.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.server;
+
+import org.apache.jena.fuseki.migrate.Registry ;
+
+public class DatasetRegistry extends Registry<DatasetRef>
+{
+    private static DatasetRegistry singleton = new DatasetRegistry() ;
+
+    public static DatasetRegistry get() { return singleton ; }
+    
+    private DatasetRegistry() {}
+}

Reply via email to