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("&", "&") ;
+ x = x.replaceAll("<", "<") ;
+ x = x.replaceAll(">", ">") ;
+ 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)