You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by an...@apache.org on 2018/09/21 10:15:26 UTC

[15/70] [abbrv] [partial] jena git commit: JENA-1597: separate jena-fuseki-webapp module

http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/mgt/Backup.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/mgt/Backup.java b/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/mgt/Backup.java
new file mode 100644
index 0000000..4be3604
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/mgt/Backup.java
@@ -0,0 +1,131 @@
+/**
+ * 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.* ;
+import java.util.Collections;
+import java.util.Set ;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.zip.GZIPOutputStream ;
+
+import org.apache.jena.atlas.io.IO ;
+import org.apache.jena.atlas.lib.DateTimeUtils ;
+import org.apache.jena.atlas.logging.Log ;
+import org.apache.jena.fuseki.Fuseki ;
+import org.apache.jena.fuseki.FusekiException ;
+import org.apache.jena.fuseki.webapp.FusekiSystem;
+import org.apache.jena.query.ReadWrite ;
+import org.apache.jena.riot.Lang ;
+import org.apache.jena.riot.RDFDataMgr ;
+import org.apache.jena.sparql.core.DatasetGraph ;
+import org.apache.jena.sparql.core.Transactional ;
+import org.apache.jena.sparql.core.TransactionalNull ;
+
+/** Perform a backup */ 
+public class Backup
+{
+    public static String chooseFileName(String dsName) {
+        // Without the "/" - ie. a relative name.
+        String ds = dsName ;
+        if ( ds.startsWith("/") )
+            ds = ds.substring(1) ;
+        if ( ds.contains("/") ) {
+            Fuseki.adminLog.warn("Dataset name: weird format: "+dsName) ;
+            // Some kind of fixup
+            ds = ds.replace("/",  "_") ;
+        }
+
+        String timestamp = DateTimeUtils.nowAsString("yyyy-MM-dd_HH-mm-ss") ;
+        String filename = ds + "_" + timestamp ;
+        filename = FusekiSystem.dirBackups.resolve(filename).toString() ;
+        return filename ;
+    }
+    
+    // Record of all backups so we don't attempt to backup the
+    // same dataset multiple times at the same time. 
+    private static Set<DatasetGraph> activeBackups = Collections.newSetFromMap(new ConcurrentHashMap<>());
+    
+    /** Perform a backup.
+     *  A backup is a dump of the dataset in compressed N-Quads, done inside a transaction.
+     */
+    public static void backup(Transactional transactional, DatasetGraph dsg, String backupfile) {
+        if ( transactional == null )
+            transactional = new TransactionalNull() ;
+        transactional.begin(ReadWrite.READ);
+        try {
+            Backup.backup(dsg, backupfile) ;
+        } catch (Exception ex) {
+            Log.warn(Fuseki.serverLog, "Exception in backup", ex);
+        }
+        finally {
+            transactional.end() ;
+        }
+    }
+
+    /** Perform a backup.
+     * 
+     * @see #backup(Transactional, DatasetGraph, String)
+     */
+    private static void backup(DatasetGraph dsg, String backupfile) {
+        if ( !backupfile.endsWith(".nq") )
+            backupfile = backupfile + ".nq" ;
+
+        // Per backup source lock. 
+        synchronized(activeBackups) {
+            // Atomically check-and-set
+            if ( activeBackups.contains(dsg) )
+                Log.warn(Fuseki.serverLog, "Backup already in progress") ;
+            activeBackups.add(dsg) ;
+        }
+
+        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(Fuseki.serverLog, "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 */}
+            // Remove lock.
+            synchronized(activeBackups) {
+                activeBackups.remove(dsg) ;
+            }
+        }
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/mgt/DumpServlet.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/mgt/DumpServlet.java b/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/mgt/DumpServlet.java
new file mode 100644
index 0000000..c9e679e
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/mgt/DumpServlet.java
@@ -0,0 +1,300 @@
+/*
+ * 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.BufferedReader ;
+import java.io.IOException ;
+import java.io.PrintWriter ;
+import java.io.StringWriter ;
+import java.util.Date ;
+import java.util.Enumeration ;
+import java.util.Locale ;
+import java.util.Properties ;
+
+import javax.servlet.ServletContext ;
+import javax.servlet.http.Cookie ;
+import javax.servlet.http.HttpServlet ;
+import javax.servlet.http.HttpServletRequest ;
+import javax.servlet.http.HttpServletResponse ;
+
+import org.apache.jena.atlas.io.IO ;
+
+public class DumpServlet extends HttpServlet
+{
+    public DumpServlet() { }
+
+    @Override
+    public void doGet(HttpServletRequest req, HttpServletResponse resp)
+    {
+        try {
+            PrintWriter out = resp.getWriter() ;
+            resp.setContentType("text/html");
+
+            String now = new Date().toString() ;
+
+            // HEAD
+            out.println("<html>") ;
+            out.println("<head>") ;
+            out.println("<Title>Dump @ "+now+"</Title>") ;
+            // Reduce the desire to cache it.
+            out.println("<meta CONTENT=now HTTP-EQUIV=expires>") ;
+            out.println("</head>") ;
+
+            // BODY
+            out.println("<body>") ;
+            out.println("<pre>") ;
+
+            out.println("Dump : "+now);
+            out.println() ;
+            out.println("==== Request");
+            out.println() ;
+            out.print(dumpRequest(req)) ;
+            out.println() ;
+                        
+            out.println(">>>> Body");
+            out.println() ;
+            printBody(out, req) ;
+            out.println("<<<< Body");
+            
+            out.println("==== ServletContext");
+            out.println() ;
+            out.print(dumpServletContext());
+            out.println() ;
+
+            out.println("==== Environment");
+            out.println() ;
+            out.print(dumpEnvironment());
+            out.println() ;
+
+            out.println("</pre>") ;
+
+            out.println("</body>") ;
+            out.println("</html>") ;
+            out.flush() ;
+        } catch (IOException e)
+        { }
+    }
+
+    // This resets the input stream
+
+    static public String dumpRequest(HttpServletRequest req)
+    {
+        try ( StringWriter sw = new StringWriter() ;
+              PrintWriter pw = new PrintWriter(sw) ) {
+            // Standard environment
+            pw.println("Method:                 "+req.getMethod());
+            pw.println("getContentLength:       "+Long.toString(req.getContentLengthLong()));
+            pw.println("getContentType:         "+req.getContentType());
+            pw.println("getRequestURI:          "+req.getRequestURI());
+            pw.println("getRequestURL:          "+req.getRequestURL());
+            pw.println("getContextPath:         "+req.getContextPath());
+            pw.println("getServletPath:         "+req.getServletPath());
+            pw.println("getPathInfo:            "+req.getPathInfo());
+            pw.println("getPathTranslated:      "+req.getPathTranslated());
+            pw.println("getQueryString:         "+req.getQueryString());
+            pw.println("getProtocol:            "+req.getProtocol());
+            pw.println("getScheme:              "+req.getScheme());
+            pw.println("getServerName:          "+req.getServerName());
+            pw.println("getServerPort:          "+req.getServerPort());
+            pw.println("getRemoteUser:          "+req.getRemoteUser());
+            pw.println("getRemoteAddr:          "+req.getRemoteAddr());
+            pw.println("getRemoteHost:          "+req.getRemoteHost());
+            pw.println("getRequestedSessionId:  "+req.getRequestedSessionId());
+            {
+                Cookie c[] = req.getCookies() ;
+                if ( c == null )
+                    pw.println("getCookies:            <none>");
+                else
+                {
+                    for ( int i = 0 ; i < c.length ; i++ )            
+                    {
+                        pw.println("Cookie:        "+c[i].getName());
+                        pw.println("    value:     "+c[i].getValue());
+                        pw.println("    version:   "+c[i].getVersion());
+                        pw.println("    comment:   "+c[i].getComment());
+                        pw.println("    domain:    "+c[i].getDomain());
+                        pw.println("    maxAge:    "+c[i].getMaxAge());
+                        pw.println("    path:      "+c[i].getPath());
+                        pw.println("    secure:    "+c[i].getSecure());
+                        pw.println();
+                    }
+                }
+            }
+            
+            {
+                // To do: create a string for the output so can send to console and return it.
+                Enumeration<String> en = req.getHeaderNames() ;
+
+                for ( ; en.hasMoreElements() ; )
+                {
+                    String name = en.nextElement() ;
+                    String value = req.getHeader(name) ;
+                    pw.println("Head: "+name + " = " + value) ;
+                }
+            }
+            
+            Enumeration<String> en2 = req.getAttributeNames() ;
+            if ( en2.hasMoreElements() )
+                pw.println();
+            for ( ; en2.hasMoreElements() ; )
+            {
+                String name = en2.nextElement() ;
+                String value = req.getAttribute(name).toString() ;
+                pw.println("Attr: "+name + " = " + value) ;
+            }
+
+            // Note that doing this on a form causes the forms content (body) to be read
+            // and parsed as form variables.
+//            en = req.getParameterNames() ;
+//            if ( en.hasMoreElements() )
+//                pw.println();
+//            for ( ; en.hasMoreElements() ; )
+//            {
+//                String name = (String)en.nextElement() ;
+//                String value = req.getParameter(name) ;
+//                pw.println("Param: "+name + " = " + value) ;
+//            }
+
+
+            
+//            MultiMap<String, String> map = WebLib.parseQueryString(req) ;
+//            for ( String name : map.keys() )
+//                for ( String value : map.get(name) )
+//                    pw.println("Param: "+name + " = " + value) ;
+            
+            Enumeration<Locale> en = req.getLocales() ;
+            if ( en.hasMoreElements() )
+                pw.println();
+            for ( ; en.hasMoreElements() ; )
+            {
+                String name = en.nextElement().toString() ;
+                pw.println("Locale: "+name) ;
+            }
+
+            pw.println() ;
+            //printBody(pw, req) ;
+
+            return sw.toString() ;
+        } catch (IOException e) { return null ; }
+    }
+
+    static void printBody(PrintWriter pw, HttpServletRequest req) throws IOException
+    {
+        BufferedReader in = req.getReader() ;
+        if ( req.getContentLength() > 0 )
+            // Need +2 because last line may not have a CR/LF on it.
+            in.mark(req.getContentLength()+2) ;
+        else
+            // This is a dump - try to do something that works, even if inefficient.
+            in.mark(100*1024) ;
+
+        while(true)
+        {
+            String x = in.readLine() ;
+            if ( x == null )
+                break ;
+            x = x.replaceAll("&", "&amp;") ;
+            x = x.replaceAll("<", "&lt;") ;
+            x = x.replaceAll(">", "&gt;") ;
+            pw.println(x) ;
+        }
+        try { in.reset() ; } catch (IOException e) { System.out.println("DumpServlet: Reset of content failed: "+e) ; }
+    }
+    
+    /**
+     * <code>dumpEnvironment</code>
+     * @return String that is the HTML of the System properties as name/value pairs.
+     * The values are with single quotes independent of whether or not the value has
+     * single quotes in it.
+     */
+    static public String dumpEnvironment()
+    {
+        Properties properties = System.getProperties();
+        try ( StringWriter sw = new StringWriter() ;
+            PrintWriter pw = new PrintWriter(sw) ; ) {
+            Enumeration<Object> en = properties.keys();
+            while(en.hasMoreElements())
+            {
+                String key = en.nextElement().toString();
+                pw.println(key+": '"+properties.getProperty(key)+"'");
+            }
+
+            pw.println() ;
+            return sw.toString() ;
+        } catch (IOException e) { IO.exception(e); return null ; }
+    }
+
+    public String dumpServletContext()
+    {
+        try ( StringWriter sw = new StringWriter() ;
+              PrintWriter pw = new PrintWriter(sw) ; ) {
+            ServletContext sc =  getServletContext();
+            pw.println("majorVersion: '"+sc.getMajorVersion()+"'");
+            pw.println("minorVersion: '"+sc.getMinorVersion()+"'");
+            pw.println("contextName:  '"+sc.getServletContextName()+"'");
+            pw.println("servletInfo:  '"+getServletInfo()+"'");
+            pw.println("serverInfo:  '"+sc.getServerInfo()+"'");
+    
+            {
+                Enumeration<String> en = sc.getInitParameterNames();
+                if (en != null) {
+                    pw.println("initParameters: ");
+                    while(en.hasMoreElements())
+                    {
+                        String key = en.nextElement();
+                        pw.println(key+": '"+sc.getInitParameter(key)+"'");
+                    }
+                }
+            }
+            
+            {
+                Enumeration<String> en = sc.getAttributeNames();
+                if (en != null) {
+                    pw.println("attributes: ");
+                    while(en.hasMoreElements())
+                    {
+                        String key = en.nextElement();
+                        pw.println(key+": '"+sc.getAttribute(key)+"'");
+                    }
+                }
+            }
+            pw.println() ;
+         
+             return sw.toString() ;
+        } catch (IOException e) { IO.exception(e); return null ; }
+    }
+
+    
+    @Override
+    public void doPost(HttpServletRequest req, HttpServletResponse resp)
+    {
+        doGet(req, resp) ;
+    }
+
+
+    @Override
+    public String getServletInfo()
+    {
+        return "Dump";
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/mgt/ServerMgtConst.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/mgt/ServerMgtConst.java b/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/mgt/ServerMgtConst.java
new file mode 100644
index 0000000..2af5e44
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/mgt/ServerMgtConst.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.mgt;
+
+/**
+ * Various constants used in the management API functions and JSON responses in the
+ * webapp/full server.
+ */
+public class ServerMgtConst {
+    public static final String  opDatasets      = "datasets" ;
+    public static final String  opListBackups   = "backups-list" ;
+    public static final String  opServer        = "server" ;
+    
+    public static final String uptime           = "uptime" ;
+    public static final String startDT          = "startDateTime" ;
+//    public static final String server           = "server" ;
+//    public static final String port             = "port" ;
+    public static final String hostname         = "hostname" ;
+    public static final String admin            = "admin" ;
+    public static final String version          = "version" ;
+    public static final String built            = "built" ;
+    public static final String services         = "services" ;
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/mgt/Template.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/mgt/Template.java b/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/mgt/Template.java
new file mode 100644
index 0000000..0749ea1
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/mgt/Template.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.mgt;
+
+import java.nio.file.Path ;
+
+import org.apache.jena.fuseki.webapp.FusekiEnv;
+
+public class Template
+{
+    public static Path getPath(String templateName) {
+        return FusekiEnv.FUSEKI_BASE.resolve(templateName) ;
+    }
+    
+    public static final String templateDir          = "templates" ;
+    
+    // These are used by the command line start up.
+    public static final String templateServiceFN    = templateDir+"/config-service" ;       // Dummy used by dataset-less service.
+
+    // TDB1 - for backwards compatibility, the files are called "tdb"
+    public static final String templateTDB1_FN        = templateDir+"/config-tdb" ;
+    public static final String templateTDB1_MemFN     = templateDir+"/config-tdb-mem" ; 
+    public static final String templateTDB1_DirFN     = templateDir+"/config-tdb-dir" ;
+    public static final String templateTDB1_DirReadFN = templateDir+"/config-tdb-dir-read-only" ;
+    
+    public static final String templateTDB2_FN        = templateDir+"/config-tdb2" ;
+    public static final String templateTDB2_MemFN     = templateDir+"/config-tdb2-mem" ; 
+    public static final String templateTDB2_DirFN     = templateDir+"/config-tdb2-dir" ;
+    public static final String templateTDB2_DirReadFN = templateDir+"/config-tdb2-dir-read-only" ;
+    
+    
+    public static final String templateTIM_MemFN      = templateDir+"/config-mem" ;
+
+    // Template may be in a resources area of a jar file so you can't do a directory listing.
+    public static final String[] templateNames = {
+        templateTIM_MemFN,
+        templateServiceFN,
+        
+        templateTDB1_FN ,
+        templateTDB1_MemFN ,
+        templateTDB1_DirFN ,
+        //templateTDB1_DirReadFN,
+        
+        templateTDB2_FN ,
+        templateTDB2_MemFN ,
+        templateTDB2_DirFN ,
+        //templateTDB2_DirReadFN
+    } ;
+    
+    public static final String NAME = "NAME" ;
+    public static final String DATA = "DATA" ;
+    public static final String DIR =  "DIR" ;
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/mgt/TemplateFunctions.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/mgt/TemplateFunctions.java b/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/mgt/TemplateFunctions.java
new file mode 100644
index 0000000..abf5ff3
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/mgt/TemplateFunctions.java
@@ -0,0 +1,86 @@
+/**
+ * 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.InputStream ;
+import java.util.Map ;
+import java.util.Map.Entry ;
+
+import org.apache.jena.atlas.io.IO ;
+import org.apache.jena.fuseki.Fuseki ;
+import org.apache.jena.riot.Lang ;
+import org.apache.jena.util.FileUtils ;
+
+public class TemplateFunctions
+{
+    /** Read in a template from a file, substitute for {NAME} and return the string. */
+    public static String templateFile(String templateName, Map<String, String> params, Lang lang) {
+        String templateFilename = Template.getPath(templateName).toString() ;
+        String template ;
+        try { template = FileUtils.readWholeFileAsUTF8(templateFilename) ; }
+        catch (IOException ex) { 
+            Fuseki.serverLog.error("File not found: "+templateFilename);
+            IO.exception(ex); return null ;
+        }
+        return templateString(template, params, lang) ;
+    }
+    
+    /** Read a template file, substitute for {NAME} and return the model. */
+    public static String templateResource(String resourceName, Map<String, String> params, Lang lang) {
+        String template ;
+        try {
+            InputStream in = TemplateFunctions.class.getClassLoader().getResourceAsStream(resourceName) ;
+            if ( in == null )
+                Fuseki.serverLog.error("Resource not found: "+resourceName);
+            template = FileUtils.readWholeFileAsUTF8(in) ;
+        }
+        catch (IOException ex) { 
+            Fuseki.serverLog.error("Error reading resource: "+resourceName);
+            IO.exception(ex); return null ;
+        }
+        return templateString(template, params, lang) ;
+    }
+
+    /** Create a template from a String */ 
+    public static String templateString(String template, Map<String, String> params, Lang lang) {
+        for ( Entry<String, String> e : params.entrySet() ) {
+            // Literal string replacement.
+            // If using .replaceAll, need to use Match.quoteReplacement on the value.
+            String x = e.getValue() ;
+            String k = "{"+e.getKey()+"}" ;
+            
+            if ( lang != null ) {
+                if ( Lang.TTL.equals(lang)     ||
+                     Lang.TRIG.equals(lang)    ||
+                     Lang.NT.equals(lang)      ||
+                     Lang.NQ.equals(lang)      ||
+                     Lang.JSONLD.equals(lang)  ||
+                     Lang.RDFJSON.equals(lang) 
+                    ) {
+                    // Make safe for a RDF language ""-string - especially MS Windows \ path separators.
+                    x = x.replace("\\", "\\\\") ;
+                    x = x.replace("\"", "\\\"") ;
+                }
+            }
+            template = template.replace(k, x) ;
+        }
+        return template ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/webapp/FusekiEnv.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/webapp/FusekiEnv.java b/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/webapp/FusekiEnv.java
new file mode 100644
index 0000000..e725db1
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/webapp/FusekiEnv.java
@@ -0,0 +1,168 @@
+/**
+ * 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.webapp;
+
+import java.nio.file.Path ;
+import java.nio.file.Paths ;
+
+/** 
+ * Separate initialization for FUSEKI_HOME and FUSEKI_BASE so that 
+ * FusekiLogging can use these values.
+ * This code must not touch Jena.  
+ * 
+ * @see FusekiSystem 
+ */ 
+public class FusekiEnv {
+    // Initialization logging happens via stdout/stderr directly.
+    // Fuseki logging is not initialized to avoid going in circles.
+    
+    private static final boolean LogInit         = false ;
+    
+    /** Unused */
+    // public static final String DFT_FUSEKI_HOME = isWindows 
+    //        ? /*What's correct here?*/ "/usr/share/fuseki"
+    //        : "/usr/share/fuseki" ;
+    static final boolean isWindows = determineIfWindows() ;
+    static final String  DFT_FUSEKI_BASE = isWindows ? /* What's correct here? */"/etc/fuseki" : "/etc/fuseki" ;
+    
+    /** Initialization mode, depending on the way Fuseki is started:
+        <ul>
+        <li>{@code WAR} - Running as a WAR file.</li>
+        <li>{@code EMBEDDED}</li>
+        <li>{@code STANDALONE} - Running as the standalone server in Jetty</li>
+        <li>{@code TEST} - Running inside maven/JUnit and as the standalone server</li>
+        <li>{@code UNSET} - Initial state.</li>
+        </ul>
+        <p> 
+        If at server initialization, the MODE is UNSET, then assume WAR setup.
+        A WAR file does not have the opportunity to set the mode.
+        <p>
+        TEST:  (better to set FUSEKI_HOME, FUSEKI_BASE from the test environment</li>
+    */
+    public enum INIT {
+        // Default values of FUSEKI_HOME, and FUSEKI_BASE. 
+        WAR         (null, "/etc/fuseki") , 
+        EMBEDDED    (null, null) ,
+        STANDALONE  (".", "run") ,
+        TEST        ("src/main/webapp", "target/run") ,
+        UNSET       (null, null) ;
+        
+        final String dftFusekiHome ;
+        final String dftFusekiBase ;
+        
+        INIT(String home, String base) {
+            this.dftFusekiHome = home ;
+            this.dftFusekiBase = base ;
+        }
+    }
+    
+    public static INIT mode = INIT.UNSET ;
+    
+    /** Root of the Fuseki installation for fixed files. 
+     *  This may be null (e.g. running inside a web application container) */ 
+    public static Path FUSEKI_HOME = null ;
+    
+    /** Root of the varying files in this deployment. Often $FUSEKI_HOME/run.
+     * This is not null - it may be /etc/fuseki, which must be writable.
+     */ 
+    public static Path FUSEKI_BASE = null ;
+    
+    // Copied from SystemTDB to avoid dependency.
+    // This code must not touch Jena.  
+    private static boolean determineIfWindows() {
+        String s = System.getProperty("os.name") ;
+        if ( s == null )
+            return false ;
+        return s.startsWith("Windows ") ;
+    }
+ 
+    public static final String   ENV_runArea     = "run" ;
+    private static boolean       initialized     = false ;
+    
+    /** Initialize the server : standalone and WAR versions : not embedded */
+    public static synchronized void setEnvironment() {
+        if ( initialized )
+            return ;
+        resetEnvironment();
+    }
+    
+    /** Reset environment - use with care and before server start up */ 
+    public static synchronized void resetEnvironment() {
+        initialized = true ;
+        logInit("FusekiEnv:Start: ENV_FUSEKI_HOME = %s : ENV_FUSEKI_BASE = %s : MODE = %s", FUSEKI_HOME, FUSEKI_BASE, mode) ;
+        
+        if ( mode == null || mode == INIT.UNSET )
+            mode = INIT.WAR ;
+
+        if ( FUSEKI_HOME == null ) {
+            // Make absolute
+            String x1 = getenv("FUSEKI_HOME") ;
+            if ( x1 == null )
+                x1 = mode.dftFusekiHome ;
+            if ( x1 != null )
+                FUSEKI_HOME = Paths.get(x1) ;
+        }
+
+        if ( FUSEKI_BASE == null ) {
+            String x2 = getenv("FUSEKI_BASE") ;
+            if ( x2 == null )
+                x2 = mode.dftFusekiBase ;
+            if ( x2 != null )
+                FUSEKI_BASE = Paths.get(x2) ;
+            else {
+                if ( FUSEKI_HOME != null )
+                    FUSEKI_BASE = FUSEKI_HOME.resolve(ENV_runArea) ;
+                else {
+                    // This is bad - there should have been a default by now.
+                    logInitError("Can't find a setting for FUSEKI_BASE - guessing wildy") ;
+                    // Neither FUSEKI_HOME nor FUSEKI_BASE set.
+                    FUSEKI_BASE = Paths.get(DFT_FUSEKI_BASE) ;
+                }
+            }
+        }
+
+        if ( FUSEKI_HOME != null )
+            FUSEKI_HOME = FUSEKI_HOME.toAbsolutePath() ;
+
+        FUSEKI_BASE = FUSEKI_BASE.toAbsolutePath() ;
+
+        logInit("FusekiEnv:Finish: ENV_FUSEKI_HOME = %s : ENV_FUSEKI_BASE = %s", FUSEKI_HOME, FUSEKI_BASE) ;
+    }
+    
+    private static void logInit(String fmt, Object ... args) {
+        if ( LogInit ) {
+            System.out.printf(fmt, args) ; 
+            System.out.println() ;
+        }
+    }
+    
+    private static void logInitError(String fmt, Object ... args) {
+        System.err.printf(fmt, args) ; 
+        System.err.println() ;
+    }
+
+    /** Get environment variable value (maybe in system properties) */
+    public static String getenv(String name) {
+        String x = System.getenv(name) ;
+        if ( x == null )
+            x = System.getProperty(name) ;
+        return x ;
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/webapp/FusekiServerEnvironmentInit.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/webapp/FusekiServerEnvironmentInit.java b/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/webapp/FusekiServerEnvironmentInit.java
new file mode 100644
index 0000000..6d3d889
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/webapp/FusekiServerEnvironmentInit.java
@@ -0,0 +1,54 @@
+/**
+ * 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.webapp;
+
+import javax.servlet.ServletContextEvent ;
+import javax.servlet.ServletContextListener ;
+
+import org.apache.jena.fuseki.system.FusekiLogging;
+import org.apache.jena.sys.JenaSystem ;
+
+/** Setup the environment and logging.
+ *  Runs before the {@link ShiroEnvironmentLoader}.
+ *  The main configuration happens in {@link FusekiServerListener} which runs after {@link ShiroEnvironmentLoader}.
+ */
+public class FusekiServerEnvironmentInit implements ServletContextListener {
+
+    public FusekiServerEnvironmentInit() { }
+    
+    @Override
+    public void contextInitialized(ServletContextEvent sce) {
+        // These two do not touch Jena.
+        FusekiEnv.setEnvironment() ;
+        FusekiLogging.setLogging(FusekiEnv.FUSEKI_BASE);
+        JenaSystem.init() ;
+    }
+
+    @Override
+    public void contextDestroyed(ServletContextEvent sce) {
+        // Stop handling requests.
+        
+        // ActionService uses DataAccessPointRegistry to map URI to services (DataAccessPoint)
+        
+        // DataService -> DataService
+//        DataAccessPointRegistry.shutdown() ;
+//        DatasetDescriptionRegistry.reset() ;
+        JenaSystem.shutdown(); 
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/webapp/FusekiServerListener.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/webapp/FusekiServerListener.java b/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/webapp/FusekiServerListener.java
new file mode 100644
index 0000000..8c1b5fe
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/webapp/FusekiServerListener.java
@@ -0,0 +1,107 @@
+/**
+ * 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.webapp;
+
+import javax.servlet.ServletContext ;
+import javax.servlet.ServletContextEvent ;
+import javax.servlet.ServletContextListener ;
+
+import org.apache.jena.fuseki.Fuseki ;
+import org.apache.jena.fuseki.FusekiException;
+import org.apache.jena.fuseki.server.DataAccessPointRegistry;
+import org.apache.jena.fuseki.server.FusekiInfo;
+import org.apache.jena.fuseki.server.FusekiInitialConfig;
+import org.apache.jena.fuseki.servlets.ServiceDispatchRegistry;
+import org.apache.jena.tdb.StoreConnection ;
+
+/** Setup configuration.
+ * The order is controlled by {@code web.xml}:
+ * <ul>
+ * <li>{@link FusekiServerEnvironmentInit}
+ * <li>{@link ShiroEnvironmentLoader}
+ * <li>{@link FusekiServerListener}, the main configuration
+ * </ul>
+ */
+
+public class FusekiServerListener implements ServletContextListener {
+
+    public FusekiServerListener() { }
+    
+    public static FusekiInitialConfig initialSetup = null ;
+
+    private boolean initialized = false ;
+
+    @Override
+    public void contextInitialized(ServletContextEvent sce) {
+        ServletContext servletContext = sce.getServletContext() ;
+        String x = servletContext.getContextPath() ;
+        if ( ! x.isEmpty() ) 
+            Fuseki.configLog.info("Context path = "+x) ;
+        serverInitialization(servletContext) ;
+    }
+
+    @Override
+    public void contextDestroyed(ServletContextEvent sce) {
+//        DataAccessPointRegistry.get().forEach((key, dap) -> {
+//            ??
+//        }) ;
+        // But in flight-transactions?
+        StoreConnection.reset();
+    }
+
+    private synchronized void serverInitialization(ServletContext servletContext) {
+        if ( initialized )
+            return ;
+        initialized = true ;
+
+        ServiceDispatchRegistry serviceDispatchRegistry = new ServiceDispatchRegistry(true);
+        ServiceDispatchRegistry.set(servletContext, serviceDispatchRegistry);
+        DataAccessPointRegistry dataAccessPointRegistry = new DataAccessPointRegistry() ;
+        DataAccessPointRegistry.set(servletContext, dataAccessPointRegistry);
+        
+        try {
+            FusekiSystem.formatBaseArea() ; 
+            if ( ! FusekiSystem.serverInitialized ) {
+                Fuseki.serverLog.error("Failed to initialize : Server not running") ;
+                return ;
+            }
+            
+            // The command line code sets initialSetup.
+            // In a non-commandline startup, initialSetup is null. 
+            if ( initialSetup == null ) {
+                initialSetup = new FusekiInitialConfig() ;
+                String cfg = FusekiEnv.FUSEKI_BASE.resolve(FusekiSystem.DFT_CONFIG).toAbsolutePath().toString() ;
+                initialSetup.fusekiServerConfigFile = cfg ;
+            }
+
+            if ( initialSetup == null ) {
+                Fuseki.serverLog.error("No configuration") ;
+                throw new FusekiException("No configuration") ;
+            }                
+            Fuseki.setVerbose(servletContext, initialSetup.verbose);
+            FusekiSystem.initializeDataAccessPoints(dataAccessPointRegistry,
+                                                    initialSetup, FusekiSystem.dirConfiguration.toString()) ;
+        } catch (Throwable th) { 
+            Fuseki.serverLog.error("Exception in initialization: {}", th.getMessage()) ;
+            throw th ;
+        }
+        FusekiInfo.info(initialSetup, dataAccessPointRegistry);
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/webapp/FusekiSystem.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/webapp/FusekiSystem.java b/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/webapp/FusekiSystem.java
new file mode 100644
index 0000000..a38cb69
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/webapp/FusekiSystem.java
@@ -0,0 +1,472 @@
+/**
+ * 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.webapp;
+
+import static java.lang.String.format ;
+
+import java.io.File ;
+import java.io.IOException ;
+import java.io.InputStream ;
+import java.io.StringReader ;
+import java.net.URL ;
+import java.nio.file.DirectoryStream ;
+import java.nio.file.Files ;
+import java.nio.file.Path ;
+import java.nio.file.StandardCopyOption ;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import jena.cmd.CmdException ;
+import org.apache.jena.atlas.io.IO ;
+import org.apache.jena.atlas.lib.FileOps ;
+import org.apache.jena.atlas.lib.InternalErrorException ;
+import org.apache.jena.fuseki.Fuseki ;
+import org.apache.jena.fuseki.FusekiConfigException;
+import org.apache.jena.fuseki.build.DatasetDescriptionRegistry;
+import org.apache.jena.fuseki.build.FusekiBuilder;
+import org.apache.jena.fuseki.build.FusekiConfig;
+import org.apache.jena.fuseki.mgt.Template;
+import org.apache.jena.fuseki.mgt.TemplateFunctions;
+import org.apache.jena.fuseki.server.DataAccessPoint;
+import org.apache.jena.fuseki.server.DataAccessPointRegistry;
+import org.apache.jena.fuseki.server.DataService;
+import org.apache.jena.fuseki.server.FusekiInitialConfig;
+import org.apache.jena.fuseki.server.FusekiVocab;
+import org.apache.jena.fuseki.servlets.HttpAction ;
+import org.apache.jena.fuseki.servlets.ServletOps ;
+import org.apache.jena.rdf.model.* ;
+import org.apache.jena.riot.Lang ;
+import org.apache.jena.riot.RDFDataMgr ;
+import org.apache.jena.riot.RDFLanguages ;
+import org.apache.jena.sparql.core.DatasetGraph ;
+import org.apache.jena.sparql.core.assembler.AssemblerUtils;
+import org.apache.jena.tdb.sys.Names ;
+
+public class FusekiSystem
+{
+    // Initialization of FUSEKI_HOME and FUSEKI_BASE is done in FusekiEnv.setEnvironment()
+    // so that the code is independent of any logging.  FusekiLogging can use
+    // initialized values of FUSEKI_BASE while looking forlog4j configuration.
+    
+    /* * Root of the Fuseki installation for fixed files. 
+     * This may be null (e.g. running inside a web application container) */ 
+    //public static Path FUSEKI_HOME = null ;
+    
+    /* * Root of the varying files in this deployment. Often $FUSEKI_HOME/run.
+     * This is not null - it may be /etc/fuseki, which must be writable.
+     */ 
+    //public static Path FUSEKI_BASE = null ;
+    
+    // Relative names of directories in the FUSEKI_BASE area.
+    public static final String     runArea                  = FusekiEnv.ENV_runArea ;
+    public static final String     databasesLocationBase    = "databases" ;
+    // Place to put Lucene text and spatial indexes.
+    //private static final String        databaseIndexesDir       = "indexes" ;
+      
+    public static final String     backupDirNameBase        = "backups" ;
+    public static final String     configDirNameBase        = "configuration" ;
+    public static final String     logsNameBase             = "logs" ;
+    public static final String     systemDatabaseNameBase   = "system" ;
+    public static final String     systemFileAreaBase       = "system_files" ;
+    public static final String     templatesNameBase        = "templates" ;
+    // This name is in web.xml as well.
+    public static final String     DFT_SHIRO_INI            = "shiro.ini" ; 
+    // In FUSEKI_BASE
+    public static final String     DFT_CONFIG               = "config.ttl" ;
+    
+    /** Directory for TDB databases - this is known to the assembler templates */
+    public static Path        dirDatabases       = null ;
+    
+    /** Directory for writing backups */
+    public static Path        dirBackups         = null ;
+
+    /** Directory for assembler files */
+    public static Path        dirConfiguration   = null ;
+    
+    /** Directory for assembler files */
+    public static Path        dirLogs            = null ;
+
+    /** Directory for system database */
+    public static Path        dirSystemDatabase  = null ;
+
+    /** Directory for files uploaded (e.g upload assembler descriptions); not data uploads. */
+    public static Path        dirFileArea        = null ;
+    
+    /** Directory for assembler files */
+    public static Path        dirTemplates       = null ;
+
+    private static boolean    initialized        = false ;
+    // Marks the end of successful initialization.
+    /*package*/static boolean serverInitialized  = false ;
+
+    /*package*/ synchronized static void formatBaseArea() {
+        if ( initialized )
+            return ;
+        initialized = true ;
+        try {
+            FusekiEnv.setEnvironment() ;
+            Path FUSEKI_HOME = FusekiEnv.FUSEKI_HOME ;
+            Path FUSEKI_BASE = FusekiEnv.FUSEKI_BASE ;
+            
+            Fuseki.init() ;
+            Fuseki.configLog.info("FUSEKI_HOME="+ ((FUSEKI_HOME==null) ? "unset" : FUSEKI_HOME.toString())) ;
+            Fuseki.configLog.info("FUSEKI_BASE="+FUSEKI_BASE.toString());
+
+            // ----  Check FUSEKI_HOME and FUSEKI_BASE
+            // If FUSEKI_HOME exists, it may be FUSEKI_BASE.
+
+            if ( FUSEKI_HOME != null ) {
+                if ( ! Files.isDirectory(FUSEKI_HOME) )
+                    throw new FusekiConfigException("FUSEKI_HOME is not a directory: "+FUSEKI_HOME) ;
+                if ( ! Files.isReadable(FUSEKI_HOME) )
+                    throw new FusekiConfigException("FUSEKI_HOME is not readable: "+FUSEKI_HOME) ;
+            }
+
+            if ( Files.exists(FUSEKI_BASE) ) {
+                if ( ! Files.isDirectory(FUSEKI_BASE) )
+                    throw new FusekiConfigException("FUSEKI_BASE is not a directory: "+FUSEKI_BASE) ;
+                if ( ! Files.isWritable(FUSEKI_BASE) )
+                    throw new FusekiConfigException("FUSEKI_BASE is not writable: "+FUSEKI_BASE) ;
+            } else {
+                ensureDir(FUSEKI_BASE);
+            }
+
+            // Ensure FUSEKI_BASE has the assumed directories.
+            dirTemplates        = writeableDirectory(FUSEKI_BASE, templatesNameBase) ;
+            dirDatabases        = writeableDirectory(FUSEKI_BASE, databasesLocationBase) ;
+            dirBackups          = writeableDirectory(FUSEKI_BASE, backupDirNameBase) ;
+            dirConfiguration    = writeableDirectory(FUSEKI_BASE, configDirNameBase) ;
+            dirLogs             = writeableDirectory(FUSEKI_BASE, logsNameBase) ;
+            dirSystemDatabase   = writeableDirectory(FUSEKI_BASE, systemDatabaseNameBase) ;
+            dirFileArea         = writeableDirectory(FUSEKI_BASE, systemFileAreaBase) ;
+            //Possible intercept point
+
+            // ---- Initialize with files.
+
+            if ( Files.isRegularFile(FUSEKI_BASE) ) 
+                throw new FusekiConfigException("FUSEKI_BASE exists but is a file") ;
+
+            // Copy missing files into FUSEKI_BASE
+            copyFileIfMissing(null, DFT_SHIRO_INI, FUSEKI_BASE) ;
+            copyFileIfMissing(null, DFT_CONFIG, FUSEKI_BASE) ;
+            for ( String n : Template.templateNames ) {
+                copyFileIfMissing(null, n, FUSEKI_BASE) ;
+            }
+
+            serverInitialized = true ;
+        } catch (RuntimeException ex) {
+            Fuseki.serverLog.error("Exception in server initialization", ex) ;
+            throw ex ;
+        }
+    }
+    
+    /** Copy a file from src to dst under name fn.
+     * If src is null, try as a classpath resource
+     * @param src   Source directory, or null meaning use java resource. 
+     * @param fn    File name, a relative path.
+     * @param dst   Destination directory.
+     * 
+     */
+    private static void copyFileIfMissing(Path src, String fn, Path dst) {
+        
+        Path dstFile = dst.resolve(fn) ;
+        if ( Files.exists(dstFile) )
+            return ;
+        
+        // fn may be a path.
+        if ( src != null ) {
+            try {
+                Files.copy(src.resolve(fn), dstFile, StandardCopyOption.COPY_ATTRIBUTES) ;
+            } catch (IOException e) {
+                IO.exception("Failed to copy file "+src.resolve(fn), e);
+                e.printStackTrace();
+            }
+        } else {
+            copyFileFromResource(fn, dstFile);
+        }
+    }
+    
+    public static void copyFileFromResource(String fn, Path dstFile) {
+        try {
+            // Get from the file from area "org/apache/jena/fuseki/server"  (our package)
+            URL url = FusekiSystem.class.getResource(fn) ;
+            if ( url == null )
+                throw new FusekiConfigException("Failed to find resource '"+fn+"'") ; 
+            InputStream in = url.openStream() ;
+            Files.copy(in, dstFile) ;
+        }
+        catch (IOException e) {
+            IO.exception("Failed to copy file from resource: "+fn, e);
+            e.printStackTrace();
+        }
+    }
+
+    public static void initializeDataAccessPoints(DataAccessPointRegistry registry, FusekiInitialConfig initialSetup, String configDir) {
+        List<DataAccessPoint> configFileDBs = initServerConfiguration(initialSetup) ;
+        List<DataAccessPoint> directoryDBs =  FusekiConfig.readConfigurationDirectory(configDir) ;
+        List<DataAccessPoint> systemDBs =     FusekiConfig.readSystemDatabase(SystemState.getDataset()) ;
+        
+        List<DataAccessPoint> datapoints = new ArrayList<>() ;
+        datapoints.addAll(configFileDBs) ;
+        datapoints.addAll(directoryDBs) ;
+        datapoints.addAll(systemDBs) ;
+        
+        // Having found them, set them all running.
+        enable(registry, datapoints);
+    }
+
+    private static void enable(DataAccessPointRegistry registry, List<DataAccessPoint> datapoints) {
+        for ( DataAccessPoint dap : datapoints ) {
+            Fuseki.configLog.info("Register: "+dap.getName()) ;
+            dap.getDataService().goActive();
+            registry.register(dap);
+        }
+    }
+
+    private static List<DataAccessPoint> initServerConfiguration(FusekiInitialConfig params) { 
+        // Has a side effect of global context setting
+        // when processing a config file.
+        // Compatibility.
+        
+        List<DataAccessPoint> datasets = new ArrayList<>();
+        if ( params == null )
+            return datasets ;
+
+        if ( params.fusekiCmdLineConfigFile != null ) {
+            List<DataAccessPoint> confDatasets = processServerConfigFile(params.fusekiCmdLineConfigFile) ;
+            datasets.addAll(confDatasets) ;
+        }
+        else if ( params.fusekiServerConfigFile != null ) {
+            List<DataAccessPoint> confDatasets = processServerConfigFile(params.fusekiServerConfigFile) ;
+            datasets.addAll(confDatasets) ;
+        }
+        else if ( params.dsg != null ) {
+            DataAccessPoint dap = datasetDefaultConfiguration(params.datasetPath, params.dsg, params.allowUpdate) ;
+            datasets.add(dap) ;
+        } else if ( params.argTemplateFile != null ) {
+            DataAccessPoint dap = configFromTemplate(params.argTemplateFile, params.datasetPath, params.allowUpdate, params.params) ;
+            datasets.add(dap) ;
+        }
+        // No datasets is valid.
+        return datasets ;
+    }
+    
+    private static List<DataAccessPoint> processServerConfigFile(String configFilename) {
+        if ( ! FileOps.exists(configFilename) ) {
+            Fuseki.configLog.warn("Configuration file '" + configFilename+"' does not exist") ;
+            return Collections.emptyList(); 
+        }
+        Fuseki.configLog.info("Configuration file: " + configFilename) ;
+        //return FusekiConfig.readServerConfigFile(configFilename);
+        Model model = AssemblerUtils.readAssemblerFile(configFilename) ;
+        if ( model.size() == 0 )
+            return Collections.emptyList() ;
+        FusekiConfig.processServerConfig(model);
+        return FusekiConfig.servicesAndDatasets(model) ;
+    }
+    
+    private static DataAccessPoint configFromTemplate(String templateFile, String datasetPath, 
+                                                      boolean allowUpdate, Map<String, String> params) {
+        DatasetDescriptionRegistry registry = new DatasetDescriptionRegistry() ; 
+        // ---- Setup
+        if ( params == null ) {
+            params = new HashMap<>() ;
+            params.put(Template.NAME, datasetPath) ;
+        } else {
+            if ( ! params.containsKey(Template.NAME) ) {
+                Fuseki.configLog.warn("No NAME found in template parameters (added)") ;
+                params.put(Template.NAME, datasetPath) ;   
+            }
+        }
+        //-- Logging
+        Fuseki.configLog.info("Template file: " + templateFile) ;
+        String dir = params.get(Template.DIR) ;
+        if ( dir != null ) {
+            if ( Objects.equals(dir, Names.memName) ) {
+                Fuseki.configLog.info("TDB dataset: in-memory") ;
+            } else {
+                if ( !FileOps.exists(dir) )
+                    throw new CmdException("Directory not found: " + dir) ;
+                Fuseki.configLog.info("TDB dataset: directory=" + dir) ;
+            }
+        }
+        //-- Logging
+        
+        datasetPath = DataAccessPoint.canonical(datasetPath) ;
+        
+        // DRY -- ActionDatasets (and others?)
+        addGlobals(params); 
+
+        String str = TemplateFunctions.templateFile(templateFile, params, Lang.TTL) ;
+        Lang lang = RDFLanguages.filenameToLang(str, Lang.TTL) ;
+        StringReader sr =  new StringReader(str) ;
+        Model model = ModelFactory.createDefaultModel() ;
+        RDFDataMgr.read(model, sr, datasetPath, lang);
+        
+        // ---- DataAccessPoint
+        Statement stmt = getOne(model, null, FusekiVocab.pServiceName, null) ;
+        if ( stmt == null ) {
+            StmtIterator sIter = model.listStatements(null, FusekiVocab.pServiceName, (RDFNode)null ) ;
+            if ( ! sIter.hasNext() )
+                ServletOps.errorBadRequest("No name given in description of Fuseki service") ;
+            sIter.next() ;
+            if ( sIter.hasNext() )
+                ServletOps.errorBadRequest("Multiple names given in description of Fuseki service") ;
+            throw new InternalErrorException("Inconsistent: getOne didn't fail the second time") ;
+        }
+        Resource subject = stmt.getSubject() ;
+        if ( ! allowUpdate ) {
+            // Opportunity for more sophisticated "read-only" mode.
+            //  1 - clean model, remove "fu:serviceUpdate", "fu:serviceUpload", "fu:serviceReadGraphStore", "fu:serviceReadWriteGraphStore"
+            //  2 - set a flag on DataAccessPoint
+        }
+        DataAccessPoint dap = FusekiBuilder.buildDataAccessPoint(subject, registry) ;
+        return dap ;
+    }
+    
+    public static void addGlobals(Map<String, String> params) {
+        if ( params == null ) {
+            Fuseki.configLog.warn("FusekiServer.addGlobals : params is null", new Throwable()) ;
+            return ;
+        }
+        
+        if ( ! params.containsKey("FUSEKI_BASE") )
+            params.put("FUSEKI_BASE", pathStringOrElse(FusekiEnv.FUSEKI_BASE, "unset")) ;
+        if ( ! params.containsKey("FUSEKI_HOME") )
+            params.put("FUSEKI_HOME", pathStringOrElse(FusekiEnv.FUSEKI_HOME, "unset")) ;
+    }
+
+    private static String pathStringOrElse(Path path, String dft) {
+        if ( path == null )
+            return dft ;
+        return path.toString() ;
+    }
+    
+    // DRY -- ActionDatasets (and others?)
+    private static Statement getOne(Model m, Resource s, Property p, RDFNode o) {
+        StmtIterator iter = m.listStatements(s, p, o) ;
+        if ( ! iter.hasNext() )
+            return null ;
+        Statement stmt = iter.next() ;
+        if ( iter.hasNext() )
+            return null ;
+        return stmt ;
+    }
+    
+    private static DataAccessPoint datasetDefaultConfiguration( String name, DatasetGraph dsg, boolean allowUpdate) {
+        name = DataAccessPoint.canonical(name) ;
+        DataService ds = FusekiBuilder.buildDataServiceStd(dsg, allowUpdate) ;
+        DataAccessPoint dap = new DataAccessPoint(name, ds) ;
+        return dap ;
+    }
+    
+    // ---- Helpers
+
+    /** Ensure a directory exists, creating it if necessary.
+     */
+    private static void ensureDir(Path directory) {
+        File dir = directory.toFile() ;
+        if ( ! dir.exists() ) {
+            boolean b = dir.mkdirs() ;
+            if ( ! b )
+                throw new FusekiConfigException("Failed to create directory: "+directory) ;
+        }
+        else if ( ! dir.isDirectory())
+            throw new FusekiConfigException("Not a directory: "+directory) ;
+    }
+
+    private static void mustExist(Path directory) {
+        File dir = directory.toFile() ;
+        if ( ! dir.exists() )
+            throw new FusekiConfigException("Does not exist: "+directory) ; 
+        if ( ! dir.isDirectory())
+            throw new FusekiConfigException("Not a directory: "+directory) ;
+    }
+    
+    private static boolean emptyDir(Path dir) {
+        return dir.toFile().list().length <= 2 ;
+    }
+    
+    private static boolean exists(Path directory) {
+        File dir = directory.toFile() ;
+        return dir.exists() ;
+    }
+
+    private static Path writeableDirectory(Path root , String relName ) {
+        Path p = makePath(root, relName) ;
+        ensureDir(p);
+        if ( ! Files.isWritable(p) )
+            throw new FusekiConfigException("Not writable: "+p) ;
+        return p ;
+    }
+    
+    private static Path makePath(Path root , String relName ) {
+        Path path = root.resolve(relName) ;
+        // Must exist
+//        try { path = path.toRealPath() ; }
+//        catch (IOException e) { IO.exception(e) ; }
+        return path ;
+    }
+
+    /**
+     * Dataset set name to configuration file name. Return a configuration file name -
+     * existing one or ".ttl" form if new
+     */
+    public static String datasetNameToConfigurationFile(HttpAction action, String dsName) {
+        List<String> existing = existingConfigurationFile(dsName) ;
+        if ( ! existing.isEmpty() ) {
+            if ( existing.size() > 1 ) {
+                action.log.warn(format("[%d] Multiple existing configuration files for %s : %s",
+                                       action.id, dsName, existing));
+                ServletOps.errorBadRequest("Multiple existing configuration files for "+dsName);
+                return null ;
+            }
+            return existing.get(0).toString() ;
+        }
+        
+        return generateConfigurationFilename(dsName) ;
+    }
+
+    /** New configuration file name - absiolute filename */
+    public static String generateConfigurationFilename(String dsName) {
+        String filename = dsName ;
+        // Without "/"
+        if ( filename.startsWith("/"))
+            filename = filename.substring(1) ;
+        Path p = FusekiSystem.dirConfiguration.resolve(filename+".ttl") ;
+        return p.toString();
+    }
+
+    /** Return the filenames of all matching files in the configuration directory (absolute paths returned ). */  
+    public static List<String> existingConfigurationFile(String baseFilename) {
+        try { 
+            List<String> paths = new ArrayList<>() ;
+            try (DirectoryStream<Path> stream = Files.newDirectoryStream(FusekiSystem.dirConfiguration, baseFilename+".*") ) {
+                stream.forEach((p)-> paths.add(FusekiSystem.dirConfiguration.resolve(p).toString() ));
+            }
+            return paths ;
+        } catch (IOException ex) {
+            throw new InternalErrorException("Failed to read configuration directory "+FusekiSystem.dirConfiguration) ;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/webapp/ShiroEnvironmentLoader.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/webapp/ShiroEnvironmentLoader.java b/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/webapp/ShiroEnvironmentLoader.java
new file mode 100644
index 0000000..b8eaa9b
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/webapp/ShiroEnvironmentLoader.java
@@ -0,0 +1,163 @@
+/**
+ * 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.webapp;
+
+import java.io.IOException ;
+import java.io.InputStream ;
+import java.nio.file.Path ;
+import java.nio.file.Paths ;
+
+import javax.servlet.ServletContext ;
+import javax.servlet.ServletContextEvent ;
+import javax.servlet.ServletContextListener ;
+
+import org.apache.jena.fuseki.Fuseki ;
+import org.apache.jena.util.FileUtils ;
+import org.apache.shiro.config.ConfigurationException ;
+import org.apache.shiro.io.ResourceUtils ;
+import org.apache.shiro.web.env.EnvironmentLoader ;
+import org.apache.shiro.web.env.ResourceBasedWebEnvironment ;
+import org.apache.shiro.web.env.WebEnvironment ;
+
+/** A place to perform Fuseki-specific initialization of Apache Shiro.
+ *  Runs after listener {@link FusekiServerEnvironmentInit} and before {@link FusekiServerListener}.
+ *  This means finding shiro.ini in multiple possible places, based on
+ *  different deployment setups.
+ */
+public class ShiroEnvironmentLoader extends EnvironmentLoader implements ServletContextListener {
+    private ServletContext servletContext ; 
+    
+    public ShiroEnvironmentLoader() {}
+    
+    @Override
+    public void contextInitialized(ServletContextEvent sce) {
+        FusekiSystem.formatBaseArea() ; 
+        this.servletContext = sce.getServletContext() ;
+        try { 
+            // Shiro.
+            initEnvironment(servletContext);
+        } catch (ConfigurationException  ex) {
+            Fuseki.configLog.error("Shiro initialization failed: "+ex.getMessage());
+            // Exit?
+            throw ex ;
+        }
+    }
+
+    @Override
+    public void contextDestroyed(ServletContextEvent sce) {
+        destroyEnvironment(sce.getServletContext());
+    }
+
+    /** 
+     * Normal Shiro initialization only supports one location for an INI file.
+     *  
+     * When given multiple locations for the shiro.ini file, and 
+     * if a {@link ResourceBasedWebEnvironment}, check the list of configuration
+     * locations, testing whether the name identified an existing resource.  
+     * For the first resource name found to exist, reset the {@link ResourceBasedWebEnvironment}
+     * to name that resource alone so the normal Shiro initialization  
+     */
+    @Override
+    protected void customizeEnvironment(WebEnvironment environment) {
+        if ( environment instanceof ResourceBasedWebEnvironment ) {
+            ResourceBasedWebEnvironment env = (ResourceBasedWebEnvironment)environment ;
+            String[] locations = env.getConfigLocations() ;
+            String loc = huntForShiroIni(locations) ;
+            Fuseki.configLog.info("Shiro file: "+loc);
+            if (loc != null )
+                locations = new String[] {loc} ;
+            env.setConfigLocations(locations);
+        }
+    }
+    
+    private static final String FILE = "file" ;
+    
+    /** Look for a Shiro ini file, or return null */
+    private static String huntForShiroIni(String[] locations) {
+        FusekiEnv.setEnvironment() ;
+        Fuseki.init();
+        for ( String loc : locations ) {
+            // If file:, look for that file.
+            // If a relative name without scheme, look in FUSEKI_BASE, FUSEKI_HOME, webapp. 
+            String scheme = FileUtils.getScheme(loc) ;
+            
+            // Covers C:\\ as a "scheme name"
+            if ( scheme != null ) {
+                if ( scheme.equalsIgnoreCase(FILE)) {
+                    // Test file: for exists
+                    Path p = Paths.get(loc.substring(FILE.length()+1)) ;
+                    if ( ! p.toFile().exists() )
+                        continue ;
+                    // Fall through.
+                }
+                // Can't test - try 
+                return loc ;
+            }
+            // No scheme .
+            Path p = Paths.get(loc) ;
+            
+            String fn = resolve(FusekiEnv.FUSEKI_BASE, p) ;
+            if ( fn != null )
+                return "file://"+fn ;
+            fn = resolve(FusekiEnv.FUSEKI_HOME, p) ;
+            if ( fn != null )
+                return "file://"+fn ;
+            
+            // Try in webapp.
+            
+            try ( InputStream is = ResourceUtils.getInputStreamForPath(loc); ) {
+                boolean exists = (is != null ) ;
+                return loc ;
+            } catch (IOException e) { }
+        }
+        return null ;
+    }
+    
+    /** Directory + name -> filename if it exists */ 
+    private static String resolve(Path dir, Path file) {
+        Path p = dir.resolve(file) ;
+        if ( p.toFile().exists() )
+            return p.normalize().toString() ;
+        return null ;
+    }
+
+//    /** 
+//     * Test whether a name identified an existing resource
+//     * @param resource    A String in Shiro-resource name format (e.g. URL scheme names) 
+//     * @return True/false as to whether the resource can be found or not. 
+//     */
+//    
+//    private boolean resourceExists(String resource) {
+//        try {
+//            // See IniWebEnvironment.convertPathToIni
+//            if (!ResourceUtils.hasResourcePrefix(resource)) {
+//                //Sort out "path" and open as a webapp resource.
+//                resource = WebUtils.normalize(resource);
+//                URL url = servletContext.getResource(resource) ;
+//                return ( url == null ) ;
+//            } else {
+//                // Treat as a plain name. 
+//                InputStream is = ResourceUtils.getInputStreamForPath(resource);
+//                boolean exists = (is != null ) ;
+//                is.close() ;
+//                return exists ;
+//            }
+//        } catch (IOException e) { return false ; }
+//    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/webapp/SystemState.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/webapp/SystemState.java b/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/webapp/SystemState.java
new file mode 100644
index 0000000..c5431a2
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/webapp/SystemState.java
@@ -0,0 +1,96 @@
+/**
+ * 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.webapp;
+
+import org.apache.jena.atlas.lib.FileOps ;
+import org.apache.jena.fuseki.Fuseki ;
+import org.apache.jena.query.Dataset ;
+import org.apache.jena.tdb.StoreConnection ;
+import org.apache.jena.tdb.TDB ;
+import org.apache.jena.tdb.TDBFactory ;
+import org.apache.jena.tdb.base.block.FileMode ;
+import org.apache.jena.tdb.base.file.Location ;
+import org.apache.jena.tdb.setup.StoreParams ;
+import org.apache.jena.tdb.transaction.DatasetGraphTransaction ;
+
+/** 
+ * Small database to record the system state.
+ * Used in the Full Fuseki server.
+ */
+public class SystemState {
+    private static String SystemDatabaseLocation ;
+    // Testing may reset this.
+    public static Location location ; 
+    
+    private  static Dataset                 dataset   = null ;
+    private  static DatasetGraphTransaction dsg       = null ;
+    
+    public static Dataset getDataset() {
+        init() ;
+        return dataset ;
+    }
+    
+    public static DatasetGraphTransaction getDatasetGraph() {
+        init() ;
+        return dsg ;
+    }
+    
+    private static boolean initialized = false ; 
+    private static void init() {
+        init$() ;
+    }
+    
+    /** Small footprint database.  The system database records the server state.
+     * It should not be performance critical, mainly being used for system admin
+     * functions.
+     * <p>Direct mode so that it is not competing for OS file cache space.
+     * <p>Small caches - 
+     */
+    private static final StoreParams systemDatabaseParams = StoreParams.builder()
+        .fileMode(FileMode.direct)
+        .blockSize(1024)
+        .blockReadCacheSize(50)
+        .blockWriteCacheSize(20)
+        .node2NodeIdCacheSize(500)
+        .nodeId2NodeCacheSize(500)
+        .nodeMissCacheSize(100)
+        .build() ;
+    
+    public /* for testing */ static void init$() {
+        if ( initialized )
+            return ;
+        initialized = true ;
+        
+        if ( location == null )
+            location = Location.create(FusekiSystem.dirSystemDatabase.toString()) ;
+        
+        if ( ! location.isMem() )
+            FileOps.ensureDir(location.getDirectoryPath()) ;
+        
+        // Force it into the store connection as a low footprint
+        if ( StoreConnection.getExisting(location) != null )
+            Fuseki.serverLog.warn("System database already in the StoreConnection cache") ;
+        StoreConnection.make(location, systemDatabaseParams) ;
+        
+        dataset = TDBFactory.createDataset(location) ;
+        dsg     = (DatasetGraphTransaction)(dataset.asDatasetGraph()) ;
+        dsg.getContext().set(TDB.symUnionDefaultGraph, false) ;
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/e8abcbb6/jena-fuseki2/jena-fuseki-webapp/src/main/resources/META-INF/DEPENDENCIES
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/resources/META-INF/DEPENDENCIES b/jena-fuseki2/jena-fuseki-webapp/src/main/resources/META-INF/DEPENDENCIES
new file mode 100644
index 0000000..910b788
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-webapp/src/main/resources/META-INF/DEPENDENCIES
@@ -0,0 +1,24 @@
+This file lists the dependences for Apache Jena Fuseki.
+  Version numbers are given in the POM file for a particular distribution. 
+
+Apache Projects:   Apache Software License
+  Apache Jena, including the Jena IRI library
+  Apache Xerces-J
+  Apache log4j
+  Apache HttpComponents (HTTP Client)
+  Apache Commons Codec
+  Apache Common FileUpload
+
+ICU4J : http://icu-project.org/
+   IBM X License (to version ICU4J 3.4.4)
+
+SLF4J : http://www.slf4j.org/
+  Copyright (c) 2004-2008 QOS.ch
+  MIT License
+
+JUnit : http://junit.org/
+  Common Public License - v 1.0
+
+Jetty: http://www.eclipse.org/jetty/
+  Apache License 2.0 
+  (also avilable under Eclipse Public License 1.0)