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 2015/03/05 16:36:44 UTC

[05/23] jena git commit: Rename folder jena-fuseki to jena-fuseki1

http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/StatsServlet.java
----------------------------------------------------------------------
diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/StatsServlet.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/mgt/StatsServlet.java
new file mode 100644
index 0000000..e165355
--- /dev/null
+++ b/jena-fuseki1/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/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/migrate/GraphLoadUtils.java
----------------------------------------------------------------------
diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/migrate/GraphLoadUtils.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/migrate/GraphLoadUtils.java
new file mode 100644
index 0000000..da16863
--- /dev/null
+++ b/jena-fuseki1/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/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/migrate/Registry.java
----------------------------------------------------------------------
diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/migrate/Registry.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/migrate/Registry.java
new file mode 100644
index 0000000..ca30be1
--- /dev/null
+++ b/jena-fuseki1/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/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/migrate/SinkRDFLimited.java
----------------------------------------------------------------------
diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/migrate/SinkRDFLimited.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/migrate/SinkRDFLimited.java
new file mode 100644
index 0000000..514d756
--- /dev/null
+++ b/jena-fuseki1/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/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/Counter.java
----------------------------------------------------------------------
diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/Counter.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/Counter.java
new file mode 100644
index 0000000..88d4d37
--- /dev/null
+++ b/jena-fuseki1/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/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/CounterMXBean.java
----------------------------------------------------------------------
diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/CounterMXBean.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/CounterMXBean.java
new file mode 100644
index 0000000..2de7658
--- /dev/null
+++ b/jena-fuseki1/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/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/CounterName.java
----------------------------------------------------------------------
diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/CounterName.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/CounterName.java
new file mode 100644
index 0000000..2952aa8
--- /dev/null
+++ b/jena-fuseki1/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/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/CounterSet.java
----------------------------------------------------------------------
diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/CounterSet.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/CounterSet.java
new file mode 100644
index 0000000..7d0d622
--- /dev/null
+++ b/jena-fuseki1/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/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/Counters.java
----------------------------------------------------------------------
diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/Counters.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/Counters.java
new file mode 100644
index 0000000..4e5ca4b
--- /dev/null
+++ b/jena-fuseki1/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/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/DatasetMXBean.java
----------------------------------------------------------------------
diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/DatasetMXBean.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/DatasetMXBean.java
new file mode 100644
index 0000000..bf38229
--- /dev/null
+++ b/jena-fuseki1/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/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/DatasetRef.java
----------------------------------------------------------------------
diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/DatasetRef.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/DatasetRef.java
new file mode 100644
index 0000000..040c759
--- /dev/null
+++ b/jena-fuseki1/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/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/DatasetRegistry.java
----------------------------------------------------------------------
diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/DatasetRegistry.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/DatasetRegistry.java
new file mode 100644
index 0000000..152e8cd
--- /dev/null
+++ b/jena-fuseki1/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() {}
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/FusekiConfig.java
----------------------------------------------------------------------
diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/FusekiConfig.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/FusekiConfig.java
new file mode 100644
index 0000000..9c36a7c
--- /dev/null
+++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/FusekiConfig.java
@@ -0,0 +1,374 @@
+/*
+ * 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.lang.reflect.Method ;
+import java.util.ArrayList ;
+import java.util.Arrays ;
+import java.util.List ;
+
+import org.apache.jena.atlas.iterator.Iter ;
+import org.apache.jena.atlas.lib.StrUtils ;
+import org.apache.jena.fuseki.Fuseki ;
+import org.apache.jena.fuseki.FusekiConfigException ;
+import org.apache.jena.fuseki.HttpNames ;
+import org.slf4j.Logger ;
+
+import com.hp.hpl.jena.assembler.Assembler ;
+import com.hp.hpl.jena.assembler.JA ;
+import com.hp.hpl.jena.query.ARQ ;
+import com.hp.hpl.jena.query.Dataset ;
+import com.hp.hpl.jena.query.Query ;
+import com.hp.hpl.jena.query.QueryExecution ;
+import com.hp.hpl.jena.query.QueryExecutionFactory ;
+import com.hp.hpl.jena.query.QueryFactory ;
+import com.hp.hpl.jena.query.QuerySolution ;
+import com.hp.hpl.jena.query.QuerySolutionMap ;
+import com.hp.hpl.jena.query.ResultSet ;
+import com.hp.hpl.jena.query.ResultSetFactory ;
+import com.hp.hpl.jena.rdf.model.Literal ;
+import com.hp.hpl.jena.rdf.model.Model ;
+import com.hp.hpl.jena.rdf.model.RDFNode ;
+import com.hp.hpl.jena.rdf.model.ResIterator ;
+import com.hp.hpl.jena.rdf.model.Resource ;
+import com.hp.hpl.jena.rdf.model.Statement ;
+import com.hp.hpl.jena.rdf.model.StmtIterator ;
+import com.hp.hpl.jena.shared.PrefixMapping ;
+import com.hp.hpl.jena.sparql.core.DatasetGraph ;
+import com.hp.hpl.jena.sparql.core.DatasetGraphFactory ;
+import com.hp.hpl.jena.sparql.core.DatasetGraphReadOnly ;
+import com.hp.hpl.jena.sparql.core.assembler.AssemblerUtils ;
+import com.hp.hpl.jena.tdb.TDB ;
+import com.hp.hpl.jena.util.FileManager ;
+import com.hp.hpl.jena.vocabulary.RDF ;
+import com.hp.hpl.jena.vocabulary.RDFS ;
+
+public class FusekiConfig
+{
+    static { Fuseki.init(); }
+
+    // The datastructure that captures a servers configuration.
+    
+    // Server port
+    int port ;
+    // Management command port - -1 for none.
+    int mgtPort ;           
+    List<DatasetRef> datasets = null ;
+    
+    
+    private static Logger log = Fuseki.configLog ;
+    
+    private static String prefixes = StrUtils.strjoinNL(
+    "PREFIX fu:     <http://jena.apache.org/fuseki#>" ,
+    "PREFIX rdf:    <http://www.w3.org/1999/02/22-rdf-syntax-ns#>",
+    "PREFIX rdfs:   <http://www.w3.org/2000/01/rdf-schema#>",
+    "PREFIX tdb:    <http://jena.hpl.hp.com/2008/tdb#>",
+    "PREFIX list:   <http://jena.hpl.hp.com/ARQ/list#>",
+    "PREFIX list:   <http://jena.hpl.hp.com/ARQ/list#>",
+    "PREFIX xsd:     <http://www.w3.org/2001/XMLSchema#>",
+    "PREFIX apf:     <http://jena.hpl.hp.com/ARQ/property#>", 
+    "PREFIX afn:     <http://jena.hpl.hp.com/ARQ/function#>" ,
+    "") ;
+    
+    public static ServerConfig defaultConfiguration(String datasetPath, DatasetGraph dsg, boolean allowUpdate, boolean listenLocal)
+    {
+        DatasetRef dbDesc = new DatasetRef() ;
+        dbDesc.name = datasetPath ;
+        dbDesc.dataset = dsg ;
+        dbDesc.query.endpoints.add(HttpNames.ServiceQuery) ;
+        dbDesc.query.endpoints.add(HttpNames.ServiceQueryAlt) ;
+
+        if ( allowUpdate )
+        {
+            dbDesc.update.endpoints.add(HttpNames.ServiceUpdate) ;
+            dbDesc.upload.endpoints.add(HttpNames.ServiceUpload) ;
+            dbDesc.readWriteGraphStore.endpoints.add(HttpNames.ServiceData) ;
+            dbDesc.allowDatasetUpdate = true ;
+        }
+        else
+            dbDesc.readGraphStore.endpoints.add(HttpNames.ServiceData) ;
+        ServerConfig config = new ServerConfig() ;
+        config.datasets = Arrays.asList(dbDesc) ;
+        config.port = 3030 ;
+        config.mgtPort = 3031 ;
+        config.pagesPort = config.port ;
+        config.loopback = listenLocal ;
+        config.jettyConfigFile = null ;
+        config.pages = Fuseki.PagesStatic ;
+        config.enableCompression = true ;
+        config.verboseLogging = false ;
+        return config ;
+    }
+    
+    public static ServerConfig configure(String filename)
+    {
+        // Be absolutely sure everything has initialized.
+        // Some initialization registers assemblers and sets abbreviation vocabulary. 
+        ARQ.init();
+        TDB.init() ;
+        Fuseki.init() ;
+        Model m = FileManager.get().loadModel(filename) ;
+
+        // Find one server.
+        List<Resource> servers = getByType(FusekiVocab.tServer, m) ;
+        if ( servers.size() == 0 )
+            throw new FusekiConfigException("No server found (no resource with type "+strForResource(FusekiVocab.tServer)) ;
+        if ( servers.size() > 1 )
+            throw new FusekiConfigException(servers.size()+" servers found (must be exactly one in a configuration file)") ;
+        
+        // ---- Server 
+        Resource server = servers.get(0) ;
+        processServer(server) ;
+
+        // ---- Services
+        ResultSet rs = query("SELECT * { ?s fu:services [ list:member ?member ] }", m) ; 
+        if ( ! rs.hasNext() )
+            log.warn("No services found") ;
+        
+        List<DatasetRef> services =  new ArrayList<DatasetRef>() ; 
+        
+        for ( ; rs.hasNext() ; )
+        {
+            QuerySolution soln = rs.next() ;
+            Resource svc = soln.getResource("member") ;
+            DatasetRef sd = processService(svc) ;
+            services.add(sd) ;
+        }
+        
+        // TODO Properties for the other fields.
+        ServerConfig config = new ServerConfig() ;
+        config.datasets = services ;
+        config.port = 3030 ;
+        config.mgtPort = 3031 ;
+        config.pagesPort = config.port ;
+        config.jettyConfigFile = null ;
+        config.pages = Fuseki.PagesStatic ;
+        config.enableCompression = true ;
+        config.verboseLogging = false ;
+        return config ;
+    }
+    
+
+    // DatasetRef used where there isn't a real Dataset e.g. the SPARQL processor.  
+    
+    private static DatasetRef noDataset      = new DatasetRef() ;
+    private static DatasetGraph dummyDSG        = new DatasetGraphReadOnly(DatasetGraphFactory.createMemFixed()) ;
+    static {
+        noDataset.name = "" ;
+        noDataset.dataset = dummyDSG ;
+        noDataset.query.endpoints.add(HttpNames.ServiceQuery) ;
+        noDataset.query.endpoints.add(HttpNames.ServiceQueryAlt) ;
+        noDataset.allowDatasetUpdate = false ;
+        noDataset.init();
+        // Don't register it.  
+        // This is used as a placeholder and shoudl not be found by "all datasets"  
+        // DatasetRegistry.get().put("", noDataset) ;
+    }
+
+    /** Return the DatasetRef (read-only) for when there is no dataset, just a SPARQL Query processor */ 
+    public static DatasetRef serviceOnlyDatasetRef() { return noDataset ; }
+
+    private static void processServer(Resource server)
+    {
+        // Global, currently.
+        AssemblerUtils.setContext(server, Fuseki.getContext()) ;
+        
+        StmtIterator sIter = server.listProperties(JA.loadClass) ;
+        for( ; sIter.hasNext(); )
+        {
+            Statement s = sIter.nextStatement() ;
+            RDFNode rn = s.getObject() ;
+            String className = null ;
+            if ( rn instanceof Resource )
+            {
+                String uri = ((Resource)rn).getURI() ;
+                if ( uri == null )
+                {
+                    log.warn("Blank node for class to load") ;
+                    continue ;
+                }
+                String javaScheme = "java:" ;
+                if ( ! uri.startsWith(javaScheme) )
+                {
+                    log.warn("Class to load is not 'java:': "+uri) ;
+                    continue ;
+                }
+                className = uri.substring(javaScheme.length()) ;
+            }
+            if ( rn instanceof Literal )
+                className = ((Literal)rn).getLexicalForm() ; 
+            /*Loader.*/loadAndInit(className) ;
+        }
+        // ----
+    }
+
+    private static void loadAndInit(String className)
+    {
+        try {
+            Class<?> classObj = Class.forName(className);
+            log.info("Loaded "+className) ;
+            Method initMethod = classObj.getMethod("init");
+            initMethod.invoke(null);
+        } catch (ClassNotFoundException ex)
+        {
+            log.warn("Class not found: "+className);
+        } 
+        catch (Exception e)         { throw new FusekiConfigException(e) ; }
+    }
+
+    private static DatasetRef processService(Resource svc)
+    {
+        log.info("Service: "+nodeLabel(svc)) ;
+        DatasetRef sDesc = new DatasetRef() ;
+        sDesc.name = ((Literal)getOne(svc, "fu:name")).getLexicalForm() ;
+        log.info("  name = "+sDesc.name) ;
+
+        addServiceEP("query", sDesc.name, sDesc.query, svc, "fu:serviceQuery") ; 
+        addServiceEP("update", sDesc.name, sDesc.update, svc, "fu:serviceUpdate") ; 
+        addServiceEP("upload", sDesc.name, sDesc.upload, svc, "fu:serviceUpload") ;
+        addServiceEP("graphStore(RW)", sDesc.name, sDesc.readWriteGraphStore, svc, "fu:serviceReadWriteGraphStore") ;
+        addServiceEP("graphStore(R)", sDesc.name, sDesc.readGraphStore, svc, "fu:serviceReadGraphStore") ;
+        // Extract timeout overriding configuration if present.
+        if (svc.hasProperty(FusekiVocab.pAllowTimeoutOverride)) {
+            sDesc.allowTimeoutOverride = svc.getProperty(FusekiVocab.pAllowTimeoutOverride).getObject().asLiteral().getBoolean();
+            if (svc.hasProperty(FusekiVocab.pMaximumTimeoutOverride)) {
+                sDesc.maximumTimeoutOverride = (int) (svc.getProperty(FusekiVocab.pMaximumTimeoutOverride).getObject().asLiteral().getFloat() * 1000);
+            }
+        }
+        
+        Resource datasetDesc = ((Resource)getOne(svc, "fu:dataset")) ;
+
+        // Check if it is in the model.
+        if ( ! datasetDesc.hasProperty(RDF.type) )
+            throw new FusekiConfigException("No rdf:type for dataset "+nodeLabel(datasetDesc)) ;
+        
+        Dataset ds = (Dataset)Assembler.general.open(datasetDesc)  ;
+        sDesc.dataset = ds.asDatasetGraph() ; 
+        return sDesc ;
+        
+    }
+    
+    private static RDFNode getOne(Resource svc, String property)
+    {
+        String ln = property.substring(property.indexOf(':')+1) ;
+        ResultSet rs = query("SELECT * { ?svc "+property+" ?x}", svc.getModel(), "svc", svc) ;
+        if ( ! rs.hasNext() )
+            throw new FusekiConfigException("No "+ln+" for service "+nodeLabel(svc)) ;
+        RDFNode x = rs.next().get("x") ;
+        if ( rs.hasNext() )
+            throw new FusekiConfigException("Multiple "+ln+" for service "+nodeLabel(svc)) ;
+        return x ;
+    }
+    
+    private static List<Resource> getByType(Resource type, Model m)
+    {
+        ResIterator rIter = m.listSubjectsWithProperty(RDF.type, type) ;
+        return Iter.toList(rIter) ;
+    }
+
+    private static void addServiceEP(String label, String name, ServiceRef service, Resource svc, String property)
+    {
+        ResultSet rs = query("SELECT * { ?svc "+property+" ?ep}", svc.getModel(), "svc", svc) ;
+        for ( ; rs.hasNext() ; )
+        {
+            QuerySolution soln = rs.next() ;
+            String epName = soln.getLiteral("ep").getLexicalForm() ;
+            service.endpoints.add(epName) ;
+            log.info("  "+label+" = /"+name+"/"+epName) ;
+        }
+    }
+
+
+    private static ResultSet query(String string, Model m)
+    {
+        return query(string, m, null, null) ;
+    }
+
+    private static ResultSet query(String string, Model m, String varName, RDFNode value)
+    {
+        Query query = QueryFactory.create(prefixes+string) ;
+        QuerySolutionMap initValues = null ;
+        if ( varName != null )
+            initValues = querySolution(varName, value) ;
+        try(QueryExecution qExec = QueryExecutionFactory.create(query, m, initValues)) {
+            ResultSet rs = ResultSetFactory.copyResults(qExec.execSelect()) ;
+            return rs ;
+        }
+    }
+    
+    private static QuerySolutionMap querySolution(String varName, RDFNode value)
+    {
+        QuerySolutionMap qsm = new QuerySolutionMap() ;
+        querySolution(qsm, varName, value) ;
+        return qsm ;
+    }
+    
+    private static QuerySolutionMap querySolution(QuerySolutionMap qsm, String varName, RDFNode value)
+    {
+        qsm.add(varName, value) ;
+        return qsm ;
+    }
+    
+    // Node presentation
+    private 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() ;
+    }
+    
+    private static String strForResource(Resource r) { return strForResource(r, r.getModel()) ; }
+    
+    private 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 ) ;
+    }
+    
+    private 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/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/FusekiErrorHandler.java
----------------------------------------------------------------------
diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/FusekiErrorHandler.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/FusekiErrorHandler.java
new file mode 100644
index 0000000..d1660f5
--- /dev/null
+++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/FusekiErrorHandler.java
@@ -0,0 +1,92 @@
+/*
+ * 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 static java.lang.String.format ;
+
+import java.io.* ;
+
+import javax.servlet.http.HttpServletRequest ;
+import javax.servlet.http.HttpServletResponse ;
+
+import org.apache.jena.fuseki.Fuseki ;
+import org.apache.jena.web.HttpSC ;
+import org.eclipse.jetty.http.HttpHeaders ;
+import org.eclipse.jetty.http.HttpMethods ;
+import org.eclipse.jetty.http.MimeTypes ;
+import org.eclipse.jetty.server.AbstractHttpConnection ;
+import org.eclipse.jetty.server.Request ;
+import org.eclipse.jetty.server.handler.ErrorHandler ;
+
+public class FusekiErrorHandler extends ErrorHandler
+{
+    @Override
+    public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
+    {
+        AbstractHttpConnection connection = AbstractHttpConnection.getCurrentConnection();
+        connection.getRequest().setHandled(true);
+        String method = request.getMethod();
+     
+        if(!method.equals(HttpMethods.GET) && !method.equals(HttpMethods.POST) && !method.equals(HttpMethods.HEAD))
+            return;
+        
+        response.setContentType(MimeTypes.TEXT_PLAIN_UTF_8) ;
+        response.setHeader(HttpHeaders.CACHE_CONTROL, "must-revalidate,no-cache,no-store") ;
+        
+        ByteArrayOutputStream bytes = new ByteArrayOutputStream(1024) ;
+        //String writer = IO.UTF8(null) ;
+        try(Writer writer = new OutputStreamWriter(bytes, "UTF-8")) {
+
+            handleErrorPage(request, writer, connection.getResponse().getStatus(), connection.getResponse().getReason());
+
+            if ( ! Fuseki.VERSION.equalsIgnoreCase("development") )
+            {
+                writer.write("\n") ;
+                writer.write("\n") ;
+                writer.write(format("Fuseki - version %s (Build date: %s)\n", Fuseki.VERSION, Fuseki.BUILD_DATE)) ;
+            }
+            writer.flush();
+        }
+        response.setContentLength(bytes.size()) ;
+        // Copy
+        response.getOutputStream().write(bytes.toByteArray()) ;
+    }
+    
+    @Override
+    protected void handleErrorPage(HttpServletRequest request, Writer writer, int code, String message)
+        throws IOException
+    {
+        if ( message == null )
+            message = HttpSC.getMessage(code) ;
+        writer.write(format("Error %d: %s\n", code, message)) ;
+        
+        Throwable th = (Throwable)request.getAttribute("javax.servlet.error.exception");
+        while(th!=null)
+        {
+            writer.write("\n");
+            StringWriter sw = new StringWriter();
+            PrintWriter pw = new PrintWriter(sw);
+            th.printStackTrace(pw);
+            pw.flush();
+            writer.write(sw.getBuffer().toString());
+            writer.write("\n");
+            th = th.getCause();
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/FusekiServletContextListener.java
----------------------------------------------------------------------
diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/FusekiServletContextListener.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/FusekiServletContextListener.java
new file mode 100644
index 0000000..f1c9642
--- /dev/null
+++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/FusekiServletContextListener.java
@@ -0,0 +1,43 @@
+/**
+ * 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 javax.servlet.ServletContextEvent ;
+import javax.servlet.ServletContextListener ;
+
+public class FusekiServletContextListener implements ServletContextListener {
+    // This could do the initialization. 
+    private final SPARQLServer sparqlServer ;
+    public FusekiServletContextListener(SPARQLServer sparqlServer) {
+        this.sparqlServer = sparqlServer ;
+    }
+
+    @Override
+    public void contextInitialized(ServletContextEvent sce) {
+//        Fuseki.serverLog.info("contextInitialized") ;
+//        for ( DatasetRef dsRef : sparqlServer.getDatasets() )
+//            Fuseki.serverLog.info("Dataset: "+dsRef.getName()) ;
+    }
+
+    @Override
+    public void contextDestroyed(ServletContextEvent sce) {
+//        Fuseki.serverLog.info("contextDestroyed") ;
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/FusekiVocab.java
----------------------------------------------------------------------
diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/FusekiVocab.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/FusekiVocab.java
new file mode 100644
index 0000000..d4d4e54
--- /dev/null
+++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/FusekiVocab.java
@@ -0,0 +1,62 @@
+/*
+ * 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.FusekiException ;
+import org.apache.jena.iri.IRI ;
+import org.apache.jena.riot.system.IRIResolver ;
+
+import com.hp.hpl.jena.rdf.model.Property ;
+import com.hp.hpl.jena.rdf.model.Resource ;
+import com.hp.hpl.jena.rdf.model.ResourceFactory ;
+
+public class FusekiVocab
+{
+    public static String NS = "http://jena.apache.org/fuseki#" ;
+
+    public static final Resource tServer = resource("Server") ;
+
+    public static final Property pServices = property("services") ;
+    public static final Property pServiceName = property("name") ;
+    
+    public static final Property pServiceQueryEP = property("serviceQuery") ;
+    public static final Property pServiceUpdateEP = property("serviceUpdate") ;
+    public static final Property pServiceUploadEP = property("serviceUpload") ;
+    public static final Property pServiceReadWriteGraphStoreEP = property("serviceReadWriteGraphStore") ;
+    public static final Property pServiceReadgraphStoreEP = property("serviceReadGraphStore") ;
+
+    public static final Property pAllowTimeoutOverride = property("allowTimeoutOverride");
+    public static final Property pMaximumTimeoutOverride = property("maximumTimeoutOverride");
+
+    private static Resource resource(String localname) { return ResourceFactory.createResource(iri(localname)) ; }
+    private static Property property(String localname) { return ResourceFactory.createProperty(iri(localname)) ; }
+        
+    private static String iri(String localname)
+    {
+        String uri = NS+localname ;
+        IRI iri = IRIResolver.parseIRI(uri) ;
+        if ( iri.hasViolation(true) )
+            throw new FusekiException("Bad IRI: "+iri) ;
+        if ( ! iri.isAbsolute() )
+            throw new FusekiException("Bad IRI: "+iri) ;
+        
+        return uri ;
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/SPARQLServer.java
----------------------------------------------------------------------
diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/SPARQLServer.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/SPARQLServer.java
new file mode 100644
index 0000000..7448eb2
--- /dev/null
+++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/SPARQLServer.java
@@ -0,0 +1,484 @@
+/*
+ * 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 static java.lang.String.format ;
+import static org.apache.jena.fuseki.Fuseki.serverLog ;
+
+import java.io.FileInputStream ;
+import java.util.* ;
+
+import javax.servlet.DispatcherType ;
+import javax.servlet.http.HttpServlet ;
+
+import org.apache.jena.atlas.lib.FileOps ;
+import org.apache.jena.fuseki.Fuseki ;
+import org.apache.jena.fuseki.FusekiException ;
+import org.apache.jena.fuseki.HttpNames ;
+import org.apache.jena.fuseki.mgt.ActionDataset ;
+import org.apache.jena.fuseki.mgt.MgtFunctions ;
+import org.apache.jena.fuseki.mgt.PageNames ;
+import org.apache.jena.fuseki.servlets.* ;
+import org.apache.jena.fuseki.validation.DataValidator ;
+import org.apache.jena.fuseki.validation.IRIValidator ;
+import org.apache.jena.fuseki.validation.QueryValidator ;
+import org.apache.jena.fuseki.validation.UpdateValidator ;
+import org.apache.jena.riot.WebContent ;
+import org.eclipse.jetty.http.MimeTypes ;
+import org.eclipse.jetty.security.* ;
+import org.eclipse.jetty.security.authentication.BasicAuthenticator ;
+import org.eclipse.jetty.server.Connector ;
+import org.eclipse.jetty.server.Server ;
+import org.eclipse.jetty.server.nio.BlockingChannelConnector ;
+import org.eclipse.jetty.servlet.DefaultServlet ;
+import org.eclipse.jetty.servlet.ServletContextHandler ;
+import org.eclipse.jetty.servlet.ServletHolder ;
+import org.eclipse.jetty.servlets.GzipFilter ;
+import org.eclipse.jetty.util.security.Constraint ;
+import org.eclipse.jetty.xml.XmlConfiguration ;
+
+import com.hp.hpl.jena.sparql.mgt.ARQMgt ;
+import com.hp.hpl.jena.sparql.util.Utils ;
+
+/**
+ * SPARQLServer is the Jena server instance which wraps/utilizes 
+ * {@link org.eclipse.jetty.server.Server}. This class provides
+ * immediate access to the {@link org.eclipse.jetty.server.Server#start()} and 
+ * {@link org.eclipse.jetty.server.Server#stop()} commands as well as obtaining
+ * instances of the server and server configuration. Finally we can obtain 
+ * instances of {@link org.apache.jena.fuseki.server.ServerConfig}.
+ *
+ */
+public class SPARQLServer {
+    static {
+        Fuseki.init() ;
+    }
+
+    private ServerConfig        serverConfig ;
+
+    private Server              server         = null ;
+    private static List<String> epDataset      = Arrays.asList("*") ;
+
+    /**
+     * Default constructor which requires a {@link org.apache.jena.fuseki.server.ServerConfig}
+     * object as input. We use this config to specify (verbose) logging, enable compression
+     * etc. 
+     * @param config
+     */
+    public SPARQLServer(ServerConfig config) {
+        this.serverConfig = config ;
+        // Currently server-wide.
+        Fuseki.verboseLogging = config.verboseLogging ;
+
+        // GZip compression
+        // Note that regardless of this setting we'll always leave it turned off
+        // for the servlets
+        // where it makes no sense to have it turned on e.g. update and upload
+
+        ServletContextHandler context = buildServer(serverConfig.jettyConfigFile, config.enableCompression) ;
+        configureDatasets(context) ;
+    }
+
+    private void configureDatasets(ServletContextHandler context) {
+        // Build them all.
+        for (DatasetRef dsDesc : serverConfig.datasets)
+            configureOneDataset(context, dsDesc, serverConfig.enableCompression) ;
+        
+    }
+    
+    /**
+     * Initialize the {@link SPARQLServer} instance.
+     */
+    public void start() {
+        String now = Utils.nowAsString() ;
+        serverLog.info(format("%s %s %s", Fuseki.NAME, Fuseki.VERSION, Fuseki.BUILD_DATE)) ;
+        // This does not get set usefully for Jetty as we use it.
+        // String jettyVersion = org.eclipse.jetty.server.Server.getVersion() ;
+        // serverLog.info(format("Jetty %s",jettyVersion)) ;
+        String host = server.getConnectors()[0].getHost() ;
+        if ( host != null )
+            serverLog.info("Incoming connections limited to " + host) ;
+        serverLog.info(format("Started %s on port %d", now, server.getConnectors()[0].getPort())) ;
+
+        try {
+            server.start() ;
+        } catch (java.net.BindException ex) {
+            serverLog.error("SPARQLServer: Failed to start server: " + ex.getMessage()) ;
+            System.exit(1) ;
+        } catch (Exception ex) {
+            serverLog.error("SPARQLServer: Failed to start server: " + ex.getMessage(), ex) ;
+            System.exit(1) ;
+        }
+
+        ServletContextHandler context = (ServletContextHandler)server.getHandler() ;
+    }
+
+    /**
+     * Stop the {@link SPARQLServer} instance.
+     */
+    public void stop() {
+        String now = Utils.nowAsString() ;
+        serverLog.info(format("Stopped %s on port %d", now, server.getConnectors()[0].getPort())) ;
+        try {
+            server.stop() ;
+        } catch (Exception ex) {
+            Fuseki.serverLog.warn("SPARQLServer: Exception while stopping server: " + ex.getMessage(), ex) ;
+        }
+        removeJMX() ;
+    }
+
+    /**
+     * Get the Jetty instance.
+     * @return Server
+     */
+    public Server getServer() {
+        return server ;
+    }
+
+    /**
+     * Get the datasets associated with the server.
+     * @return returns the datasets via {@link org.apache.jena.fuseki.server.ServerConfig#datasets}
+     */
+    public List<DatasetRef> getDatasets() {
+        return serverConfig.datasets ;
+    }
+
+    /**
+     * Obtain the {@link org.apache.jena.fuseki.server.ServerConfig}
+     * @return ServerConfig
+     */
+    public ServerConfig getServerConfig() {
+        return serverConfig ;
+    }
+
+    // Later : private and in constructor.
+    private ServletContextHandler buildServer(String jettyConfig, boolean enableCompression) {
+        if ( jettyConfig != null ) {
+            // --jetty-config=jetty-fuseki.xml
+            // for detailed configuration of the server using Jetty features.
+            server = configServer(jettyConfig) ;
+        } else
+            server = defaultServerConfig(serverConfig.port, serverConfig.loopback) ;
+        // Keep the server to a maximum number of threads.
+        // server.setThreadPool(new QueuedThreadPool(ThreadPoolSize)) ;
+
+        ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS) ;
+        context.setErrorHandler(new FusekiErrorHandler()) ;
+        context.addEventListener(new FusekiServletContextListener(this));
+        
+        // Increase form size.
+        context.getServletContext().getContextHandler().setMaxFormContentSize(10 * 1000 * 1000) ;
+
+        // Wire up authentication if appropriate
+        if ( jettyConfig == null && serverConfig.authConfigFile != null ) {
+            Constraint constraint = new Constraint() ;
+            constraint.setName(Constraint.__BASIC_AUTH) ;
+            constraint.setRoles(new String[]{"fuseki"}) ;
+            constraint.setAuthenticate(true) ;
+
+            ConstraintMapping mapping = new ConstraintMapping() ;
+            mapping.setConstraint(constraint) ;
+            mapping.setPathSpec("/*") ;
+
+            IdentityService identService = new DefaultIdentityService() ;
+
+            ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler() ;
+            securityHandler.addConstraintMapping(mapping) ;
+            securityHandler.setIdentityService(identService) ;
+
+            HashLoginService loginService = new HashLoginService("Fuseki Authentication", serverConfig.authConfigFile) ;
+            loginService.setIdentityService(identService) ;
+
+            securityHandler.setLoginService(loginService) ;
+            securityHandler.setAuthenticator(new BasicAuthenticator()) ;
+
+            context.setSecurityHandler(securityHandler) ;
+
+            serverLog.debug("Basic Auth Configuration = " + serverConfig.authConfigFile) ;
+        }
+
+        // Wire up context handler to server
+        server.setHandler(context) ;
+
+        // Constants. Add RDF types.
+        MimeTypes mt = new MimeTypes() ;
+        mt.addMimeMapping("rdf", WebContent.contentTypeRDFXML + ";charset=utf-8") ;
+        mt.addMimeMapping("ttl", WebContent.contentTypeTurtle + ";charset=utf-8") ;
+        mt.addMimeMapping("nt", WebContent.contentTypeNTriples + ";charset=ascii") ;
+        mt.addMimeMapping("nq", WebContent.contentTypeNQuads + ";charset=ascii") ;
+        mt.addMimeMapping("trig", WebContent.contentTypeTriG + ";charset=utf-8") ;
+
+        // mt.addMimeMapping("tpl", "text/html;charset=utf-8") ;
+        context.setMimeTypes(mt) ;
+        server.setHandler(context) ;
+
+        serverLog.debug("Pages = " + serverConfig.pages) ;
+
+        boolean installManager = true ;
+        boolean installServices = true ;
+
+        String validationRoot = "/validate" ;
+
+        // Should all services be /_/.... or some such?
+
+        if ( installManager || installServices ) {
+            // TODO Respect port.
+            if ( serverConfig.pagesPort != serverConfig.port )
+                serverLog.warn("Not supported yet - pages on a different port to services") ;
+             if ( serverConfig.pages != null ) {
+                 if ( ! FileOps.exists(serverConfig.pages) )
+                     serverLog.warn("No pages directory - "+serverConfig.pages) ;
+                String base = serverConfig.pages ;
+                Map<String, Object> data = new HashMap<String, Object>() ;
+                data.put("mgt", new MgtFunctions()) ;
+                SimpleVelocityServlet templateEngine = new SimpleVelocityServlet(base, data) ;
+                addServlet(context, templateEngine, "*.tpl", false) ;
+             }
+        }
+
+        if ( installManager ) {
+            // Action when control panel selects a dataset.
+            HttpServlet datasetChooser = new ActionDataset() ;
+            addServlet(context, datasetChooser, PageNames.actionDatasetNames, false) ;
+        }
+
+        if ( installServices ) {
+            // Validators
+            HttpServlet validateQuery = new QueryValidator() ;
+            HttpServlet validateUpdate = new UpdateValidator() ;
+            HttpServlet validateData = new DataValidator() ;
+            HttpServlet validateIRI = new IRIValidator() ;
+
+            HttpServlet dumpService = new DumpServlet() ;
+            HttpServlet generalQueryService = new SPARQL_QueryGeneral() ;
+
+            addServlet(context, validateQuery, validationRoot + "/query", false) ;
+            addServlet(context, validateUpdate, validationRoot + "/update", false) ;
+            addServlet(context, validateData, validationRoot + "/data", false) ;
+            addServlet(context, validateIRI, validationRoot + "/iri", false) ;
+
+            // general query processor.
+            addServlet(context, generalQueryService, HttpNames.ServiceGeneralQuery, enableCompression) ;
+        }
+
+        if ( installManager || installServices ) {
+            String[] files = {"fuseki.html", "index.html"} ;
+            context.setWelcomeFiles(files) ;
+            addContent(context, "/", serverConfig.pages) ;
+        }
+
+        return context ;
+    }
+
+    /** Experimental - off by default. The überservlet sits on the dataset name and handles all requests.
+      * Includes direct naming and quad access to the dataset. 
+      */
+    public static boolean       überServlet       = false ;
+    
+    private static List<String> ListOfEmptyString = Arrays.asList("") ;
+
+    private void configureOneDataset(ServletContextHandler context, DatasetRef dsDesc, boolean enableCompression) {
+        String datasetPath = dsDesc.name ;
+        if ( datasetPath.equals("/") )
+            datasetPath = "" ;
+        else
+            if ( !datasetPath.startsWith("/") )
+                datasetPath = "/" + datasetPath ;
+
+        if ( datasetPath.endsWith("/") )
+            datasetPath = datasetPath.substring(0, datasetPath.length() - 1) ;
+
+        dsDesc.init() ;
+
+        DatasetRegistry.get().put(datasetPath, dsDesc) ;
+        serverLog.info(format("Dataset path = %s", datasetPath)) ;
+
+        HttpServlet sparqlQuery = new SPARQL_QueryDataset() ;
+        HttpServlet sparqlUpdate = new SPARQL_Update() ;
+        HttpServlet sparqlUpload = new SPARQL_Upload() ;
+        HttpServlet sparqlHttpR = new SPARQL_REST_R() ;
+        HttpServlet sparqlHttpRW = new SPARQL_REST_RW() ;
+        HttpServlet sparqlDataset = new SPARQL_UberServlet.AccessByConfig() ;
+
+        if ( !überServlet ) {
+            // If uberserver, these are unnecessary but can be used.
+            // If just means the überservlet isn't handling these operations.
+            addServlet(context, datasetPath, sparqlQuery, dsDesc.query, enableCompression) ;
+            addServlet(context, datasetPath, sparqlUpdate, dsDesc.update, false) ;
+            addServlet(context, datasetPath, sparqlUpload, dsDesc.upload, false) ; // No point - no results of any size.
+            addServlet(context, datasetPath, sparqlHttpR, dsDesc.readGraphStore, enableCompression) ;
+            addServlet(context, datasetPath, sparqlHttpRW, dsDesc.readWriteGraphStore, enableCompression) ;
+            // This adds direct operations on the dataset itself.
+            // addServlet(context, datasetPath, sparqlDataset,
+            // ListOfEmptyString, enableCompression) ;
+        } else {
+            // This is the servlet that analyses requests and dispatches them to
+            // the appropriate servlet.
+            // SPARQL Query, SPARQL Update -- handles dataset?query=
+            // dataset?update=
+            // Graph Store Protocol (direct and indirect naming) if enabled.
+            // GET/PUT/POST on the dataset itself.
+            // It also checks for a request that looks like a service request
+            // and passes it
+            // on to the service (this takes precedence over direct naming).
+            addServlet(context, datasetPath, sparqlDataset, epDataset, enableCompression) ;
+        }
+
+        // Add JMX beans to record daatset and it's services.
+        addJMX(dsDesc) ;
+    }
+
+    private static Server configServer(String jettyConfig) {
+        try {
+            serverLog.info("Jetty server config file = " + jettyConfig) ;
+            Server server = new Server() ;
+            XmlConfiguration configuration = new XmlConfiguration(new FileInputStream(jettyConfig)) ;
+            configuration.configure(server) ;
+            return server ;
+        } catch (Exception ex) {
+            serverLog.error("SPARQLServer: Failed to configure server: " + ex.getMessage(), ex) ;
+            throw new FusekiException("Failed to configure a server using configuration file '" + jettyConfig + "'") ;
+        }
+    }
+
+    private static Server defaultServerConfig(int port, boolean loopback) {
+        // Server, with one NIO-based connector, large input buffer size (for
+        // long URLs, POSTed forms (queries, updates)).
+        Server server = new Server() ;
+
+        // Using "= new SelectChannelConnector() ;" on Darwin (OS/X) causes
+        // problems
+        // with initialization not seen (thread scheduling?) in Joseki.
+
+        // BlockingChannelConnector is better for pumping large responses back
+        // but there have been observed problems with DirectMemory allocation
+        // (-XX:MaxDirectMemorySize=1G does not help)
+        // Connector connector = new SelectChannelConnector() ;
+
+        // Connector and specific settings.
+        BlockingChannelConnector bcConnector = new BlockingChannelConnector() ;
+        // bcConnector.setUseDirectBuffers(false) ;
+
+        Connector connector = bcConnector ;
+        // Ignore. 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.
+        if ( loopback )
+            connector.setHost("localhost");
+        connector.setPort(port) ;
+        // Some people do try very large operations ...
+        connector.setRequestHeaderSize(64 * 1024) ;
+        connector.setRequestBufferSize(5 * 1024 * 1024) ;
+        connector.setResponseBufferSize(5 * 1024 * 1024) ;
+        server.addConnector(connector) ;
+        return server ;
+    }
+
+    private static void addContent(ServletContextHandler context, String pathSpec, String pages) {
+        DefaultServlet staticServlet = new DefaultServlet() ;
+        ServletHolder staticContent = new ServletHolder(staticServlet) ;
+        staticContent.setInitParameter("resourceBase", pages) ;
+
+        // Note we set GZip to false for static content because the Jetty
+        // DefaultServlet has
+        // a built-in GZip capability that is better for static content than the
+        // mechanism the
+        // GzipFilter uses for dynamic content
+        addServlet(context, staticContent, pathSpec, false) ;
+    }
+
+    private void addServlet(ServletContextHandler context, String datasetPath, HttpServlet servlet,
+                            ServiceRef serviceRef, boolean enableCompression) {
+        addServlet(context, datasetPath, servlet, serviceRef.endpoints, enableCompression) ;
+    }
+
+    // SHARE
+    private static void addServlet(ServletContextHandler context, String datasetPath, HttpServlet servlet,
+                                   List<String> pathSpecs, boolean enableCompression) {
+        for (String pathSpec : pathSpecs) {
+            if ( pathSpec.equals("") ) {
+                // "" is special -- add as "base" and "base/"
+                addServlet(context, servlet, datasetPath + "/", enableCompression) ;
+                addServlet(context, servlet, datasetPath, enableCompression) ;
+                continue ;
+            }
+
+            if ( pathSpec.endsWith("/") )
+                pathSpec = pathSpec.substring(0, pathSpec.length() - 1) ;
+            if ( pathSpec.startsWith("/") )
+                pathSpec = pathSpec.substring(1, pathSpec.length()) ;
+            addServlet(context, servlet, datasetPath + "/" + pathSpec, enableCompression) ;
+        }
+    }
+
+    private static void addServlet(ServletContextHandler context, HttpServlet servlet, String pathSpec,
+                                   boolean enableCompression) {
+        ServletHolder holder = new ServletHolder(servlet) ;
+        addServlet(context, holder, pathSpec, enableCompression) ;
+    }
+
+    private static void addServlet(ServletContextHandler context, ServletHolder holder, String pathSpec,
+                                   boolean enableCompression) {
+        if ( serverLog.isDebugEnabled() ) {
+            if ( enableCompression )
+                serverLog.debug("Add servlet @ " + pathSpec + " (with gzip)") ;
+            else
+                serverLog.debug("Add servlet @ " + pathSpec) ;
+        }
+        context.addServlet(holder, pathSpec) ;
+
+        if ( enableCompression )
+            context.addFilter(GzipFilter.class, pathSpec, EnumSet.allOf(DispatcherType.class)) ;
+    }
+
+    private void addJMX() {
+        DatasetRegistry registry = DatasetRegistry.get() ;
+        for (String ds : registry.keys()) {
+            DatasetRef dsRef = registry.get(ds) ;
+            addJMX(dsRef) ;
+        }
+    }
+
+    private void addJMX(DatasetRef dsRef) {
+        String x = dsRef.name ;
+        // if ( x.startsWith("/") )
+        // x = x.substring(1) ;
+        ARQMgt.register(Fuseki.PATH + ".dataset:name=" + x, dsRef) ;
+        // For all endpoints
+        for (ServiceRef sRef : dsRef.getServiceRefs()) {
+            ARQMgt.register(Fuseki.PATH + ".dataset:name=" + x + "/" + sRef.name, sRef) ;
+        }
+    }
+
+    private void removeJMX() {
+        DatasetRegistry registry = DatasetRegistry.get() ;
+        for (String ds : registry.keys()) {
+            DatasetRef ref = registry.get(ds) ;
+        }
+    }
+
+    private void removeJMX(DatasetRef dsRef) {
+        String x = dsRef.getName() ;
+        ARQMgt.unregister(Fuseki.PATH + ".dataset:name=" + x) ;
+        for (ServiceRef sRef : dsRef.getServiceRefs()) {
+            ARQMgt.unregister(Fuseki.PATH + ".dataset:name=" + x + "/" + sRef.name) ;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/ServerConfig.java
----------------------------------------------------------------------
diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/ServerConfig.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/ServerConfig.java
new file mode 100644
index 0000000..4e0b865
--- /dev/null
+++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/ServerConfig.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.server;
+
+import java.util.List ;
+
+/** This represents a configuration of a SPARQL server.
+ */
+
+public class ServerConfig
+{
+    public ServerConfig() {}
+    
+    /** Port to run the server service on */
+    public int port ;
+    /** Port for the management interface : -1 for no management interface */ 
+    public int mgtPort ;
+    /** Port for the pages UI : this can be the same as the services port. */ 
+    public int pagesPort ;
+    /** Jetty config file - if null, use the built-in configuration of Jetty */
+    public String jettyConfigFile = null ;
+    /** Listen only on the loopback (localhost) interface */
+    public boolean loopback = false ;
+    /** The local directory for serving the static pages */ 
+    public String pages ;
+    /** The list of datasets */
+    public List<DatasetRef> datasets ;
+    /** Enable Accept-Encoding compression. Set to false by default.*/
+    public boolean enableCompression = false ;
+    
+    /** Enable additional logging */
+    public boolean verboseLogging = false ;
+    /**
+     * Authentication config file used to setup Jetty Basic auth, if a Jetty config file was set this is ignored since Jetty config allows much more complex auth methods to be implemented
+     */
+    public String authConfigFile ;
+
+}
+