You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by rv...@apache.org on 2014/10/22 13:39:59 UTC
[44/68] [abbrv] [partial] reverting accidentally deleted files
http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/FusekiConfig.java
----------------------------------------------------------------------
diff --git a/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/FusekiConfig.java b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/FusekiConfig.java
new file mode 100644
index 0000000..9c36a7c
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/FusekiConfig.java
@@ -0,0 +1,374 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.server;
+
+import java.lang.reflect.Method ;
+import java.util.ArrayList ;
+import java.util.Arrays ;
+import java.util.List ;
+
+import org.apache.jena.atlas.iterator.Iter ;
+import org.apache.jena.atlas.lib.StrUtils ;
+import org.apache.jena.fuseki.Fuseki ;
+import org.apache.jena.fuseki.FusekiConfigException ;
+import org.apache.jena.fuseki.HttpNames ;
+import org.slf4j.Logger ;
+
+import com.hp.hpl.jena.assembler.Assembler ;
+import com.hp.hpl.jena.assembler.JA ;
+import com.hp.hpl.jena.query.ARQ ;
+import com.hp.hpl.jena.query.Dataset ;
+import com.hp.hpl.jena.query.Query ;
+import com.hp.hpl.jena.query.QueryExecution ;
+import com.hp.hpl.jena.query.QueryExecutionFactory ;
+import com.hp.hpl.jena.query.QueryFactory ;
+import com.hp.hpl.jena.query.QuerySolution ;
+import com.hp.hpl.jena.query.QuerySolutionMap ;
+import com.hp.hpl.jena.query.ResultSet ;
+import com.hp.hpl.jena.query.ResultSetFactory ;
+import com.hp.hpl.jena.rdf.model.Literal ;
+import com.hp.hpl.jena.rdf.model.Model ;
+import com.hp.hpl.jena.rdf.model.RDFNode ;
+import com.hp.hpl.jena.rdf.model.ResIterator ;
+import com.hp.hpl.jena.rdf.model.Resource ;
+import com.hp.hpl.jena.rdf.model.Statement ;
+import com.hp.hpl.jena.rdf.model.StmtIterator ;
+import com.hp.hpl.jena.shared.PrefixMapping ;
+import com.hp.hpl.jena.sparql.core.DatasetGraph ;
+import com.hp.hpl.jena.sparql.core.DatasetGraphFactory ;
+import com.hp.hpl.jena.sparql.core.DatasetGraphReadOnly ;
+import com.hp.hpl.jena.sparql.core.assembler.AssemblerUtils ;
+import com.hp.hpl.jena.tdb.TDB ;
+import com.hp.hpl.jena.util.FileManager ;
+import com.hp.hpl.jena.vocabulary.RDF ;
+import com.hp.hpl.jena.vocabulary.RDFS ;
+
+public class FusekiConfig
+{
+ static { Fuseki.init(); }
+
+ // The datastructure that captures a servers configuration.
+
+ // Server port
+ int port ;
+ // Management command port - -1 for none.
+ int mgtPort ;
+ List<DatasetRef> datasets = null ;
+
+
+ private static Logger log = Fuseki.configLog ;
+
+ private static String prefixes = StrUtils.strjoinNL(
+ "PREFIX fu: <http://jena.apache.org/fuseki#>" ,
+ "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>",
+ "PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>",
+ "PREFIX tdb: <http://jena.hpl.hp.com/2008/tdb#>",
+ "PREFIX list: <http://jena.hpl.hp.com/ARQ/list#>",
+ "PREFIX list: <http://jena.hpl.hp.com/ARQ/list#>",
+ "PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>",
+ "PREFIX apf: <http://jena.hpl.hp.com/ARQ/property#>",
+ "PREFIX afn: <http://jena.hpl.hp.com/ARQ/function#>" ,
+ "") ;
+
+ public static ServerConfig defaultConfiguration(String datasetPath, DatasetGraph dsg, boolean allowUpdate, boolean listenLocal)
+ {
+ DatasetRef dbDesc = new DatasetRef() ;
+ dbDesc.name = datasetPath ;
+ dbDesc.dataset = dsg ;
+ dbDesc.query.endpoints.add(HttpNames.ServiceQuery) ;
+ dbDesc.query.endpoints.add(HttpNames.ServiceQueryAlt) ;
+
+ if ( allowUpdate )
+ {
+ dbDesc.update.endpoints.add(HttpNames.ServiceUpdate) ;
+ dbDesc.upload.endpoints.add(HttpNames.ServiceUpload) ;
+ dbDesc.readWriteGraphStore.endpoints.add(HttpNames.ServiceData) ;
+ dbDesc.allowDatasetUpdate = true ;
+ }
+ else
+ dbDesc.readGraphStore.endpoints.add(HttpNames.ServiceData) ;
+ ServerConfig config = new ServerConfig() ;
+ config.datasets = Arrays.asList(dbDesc) ;
+ config.port = 3030 ;
+ config.mgtPort = 3031 ;
+ config.pagesPort = config.port ;
+ config.loopback = listenLocal ;
+ config.jettyConfigFile = null ;
+ config.pages = Fuseki.PagesStatic ;
+ config.enableCompression = true ;
+ config.verboseLogging = false ;
+ return config ;
+ }
+
+ public static ServerConfig configure(String filename)
+ {
+ // Be absolutely sure everything has initialized.
+ // Some initialization registers assemblers and sets abbreviation vocabulary.
+ ARQ.init();
+ TDB.init() ;
+ Fuseki.init() ;
+ Model m = FileManager.get().loadModel(filename) ;
+
+ // Find one server.
+ List<Resource> servers = getByType(FusekiVocab.tServer, m) ;
+ if ( servers.size() == 0 )
+ throw new FusekiConfigException("No server found (no resource with type "+strForResource(FusekiVocab.tServer)) ;
+ if ( servers.size() > 1 )
+ throw new FusekiConfigException(servers.size()+" servers found (must be exactly one in a configuration file)") ;
+
+ // ---- Server
+ Resource server = servers.get(0) ;
+ processServer(server) ;
+
+ // ---- Services
+ ResultSet rs = query("SELECT * { ?s fu:services [ list:member ?member ] }", m) ;
+ if ( ! rs.hasNext() )
+ log.warn("No services found") ;
+
+ List<DatasetRef> services = new ArrayList<DatasetRef>() ;
+
+ for ( ; rs.hasNext() ; )
+ {
+ QuerySolution soln = rs.next() ;
+ Resource svc = soln.getResource("member") ;
+ DatasetRef sd = processService(svc) ;
+ services.add(sd) ;
+ }
+
+ // TODO Properties for the other fields.
+ ServerConfig config = new ServerConfig() ;
+ config.datasets = services ;
+ config.port = 3030 ;
+ config.mgtPort = 3031 ;
+ config.pagesPort = config.port ;
+ config.jettyConfigFile = null ;
+ config.pages = Fuseki.PagesStatic ;
+ config.enableCompression = true ;
+ config.verboseLogging = false ;
+ return config ;
+ }
+
+
+ // DatasetRef used where there isn't a real Dataset e.g. the SPARQL processor.
+
+ private static DatasetRef noDataset = new DatasetRef() ;
+ private static DatasetGraph dummyDSG = new DatasetGraphReadOnly(DatasetGraphFactory.createMemFixed()) ;
+ static {
+ noDataset.name = "" ;
+ noDataset.dataset = dummyDSG ;
+ noDataset.query.endpoints.add(HttpNames.ServiceQuery) ;
+ noDataset.query.endpoints.add(HttpNames.ServiceQueryAlt) ;
+ noDataset.allowDatasetUpdate = false ;
+ noDataset.init();
+ // Don't register it.
+ // This is used as a placeholder and shoudl not be found by "all datasets"
+ // DatasetRegistry.get().put("", noDataset) ;
+ }
+
+ /** Return the DatasetRef (read-only) for when there is no dataset, just a SPARQL Query processor */
+ public static DatasetRef serviceOnlyDatasetRef() { return noDataset ; }
+
+ private static void processServer(Resource server)
+ {
+ // Global, currently.
+ AssemblerUtils.setContext(server, Fuseki.getContext()) ;
+
+ StmtIterator sIter = server.listProperties(JA.loadClass) ;
+ for( ; sIter.hasNext(); )
+ {
+ Statement s = sIter.nextStatement() ;
+ RDFNode rn = s.getObject() ;
+ String className = null ;
+ if ( rn instanceof Resource )
+ {
+ String uri = ((Resource)rn).getURI() ;
+ if ( uri == null )
+ {
+ log.warn("Blank node for class to load") ;
+ continue ;
+ }
+ String javaScheme = "java:" ;
+ if ( ! uri.startsWith(javaScheme) )
+ {
+ log.warn("Class to load is not 'java:': "+uri) ;
+ continue ;
+ }
+ className = uri.substring(javaScheme.length()) ;
+ }
+ if ( rn instanceof Literal )
+ className = ((Literal)rn).getLexicalForm() ;
+ /*Loader.*/loadAndInit(className) ;
+ }
+ // ----
+ }
+
+ private static void loadAndInit(String className)
+ {
+ try {
+ Class<?> classObj = Class.forName(className);
+ log.info("Loaded "+className) ;
+ Method initMethod = classObj.getMethod("init");
+ initMethod.invoke(null);
+ } catch (ClassNotFoundException ex)
+ {
+ log.warn("Class not found: "+className);
+ }
+ catch (Exception e) { throw new FusekiConfigException(e) ; }
+ }
+
+ private static DatasetRef processService(Resource svc)
+ {
+ log.info("Service: "+nodeLabel(svc)) ;
+ DatasetRef sDesc = new DatasetRef() ;
+ sDesc.name = ((Literal)getOne(svc, "fu:name")).getLexicalForm() ;
+ log.info(" name = "+sDesc.name) ;
+
+ addServiceEP("query", sDesc.name, sDesc.query, svc, "fu:serviceQuery") ;
+ addServiceEP("update", sDesc.name, sDesc.update, svc, "fu:serviceUpdate") ;
+ addServiceEP("upload", sDesc.name, sDesc.upload, svc, "fu:serviceUpload") ;
+ addServiceEP("graphStore(RW)", sDesc.name, sDesc.readWriteGraphStore, svc, "fu:serviceReadWriteGraphStore") ;
+ addServiceEP("graphStore(R)", sDesc.name, sDesc.readGraphStore, svc, "fu:serviceReadGraphStore") ;
+ // Extract timeout overriding configuration if present.
+ if (svc.hasProperty(FusekiVocab.pAllowTimeoutOverride)) {
+ sDesc.allowTimeoutOverride = svc.getProperty(FusekiVocab.pAllowTimeoutOverride).getObject().asLiteral().getBoolean();
+ if (svc.hasProperty(FusekiVocab.pMaximumTimeoutOverride)) {
+ sDesc.maximumTimeoutOverride = (int) (svc.getProperty(FusekiVocab.pMaximumTimeoutOverride).getObject().asLiteral().getFloat() * 1000);
+ }
+ }
+
+ Resource datasetDesc = ((Resource)getOne(svc, "fu:dataset")) ;
+
+ // Check if it is in the model.
+ if ( ! datasetDesc.hasProperty(RDF.type) )
+ throw new FusekiConfigException("No rdf:type for dataset "+nodeLabel(datasetDesc)) ;
+
+ Dataset ds = (Dataset)Assembler.general.open(datasetDesc) ;
+ sDesc.dataset = ds.asDatasetGraph() ;
+ return sDesc ;
+
+ }
+
+ private static RDFNode getOne(Resource svc, String property)
+ {
+ String ln = property.substring(property.indexOf(':')+1) ;
+ ResultSet rs = query("SELECT * { ?svc "+property+" ?x}", svc.getModel(), "svc", svc) ;
+ if ( ! rs.hasNext() )
+ throw new FusekiConfigException("No "+ln+" for service "+nodeLabel(svc)) ;
+ RDFNode x = rs.next().get("x") ;
+ if ( rs.hasNext() )
+ throw new FusekiConfigException("Multiple "+ln+" for service "+nodeLabel(svc)) ;
+ return x ;
+ }
+
+ private static List<Resource> getByType(Resource type, Model m)
+ {
+ ResIterator rIter = m.listSubjectsWithProperty(RDF.type, type) ;
+ return Iter.toList(rIter) ;
+ }
+
+ private static void addServiceEP(String label, String name, ServiceRef service, Resource svc, String property)
+ {
+ ResultSet rs = query("SELECT * { ?svc "+property+" ?ep}", svc.getModel(), "svc", svc) ;
+ for ( ; rs.hasNext() ; )
+ {
+ QuerySolution soln = rs.next() ;
+ String epName = soln.getLiteral("ep").getLexicalForm() ;
+ service.endpoints.add(epName) ;
+ log.info(" "+label+" = /"+name+"/"+epName) ;
+ }
+ }
+
+
+ private static ResultSet query(String string, Model m)
+ {
+ return query(string, m, null, null) ;
+ }
+
+ private static ResultSet query(String string, Model m, String varName, RDFNode value)
+ {
+ Query query = QueryFactory.create(prefixes+string) ;
+ QuerySolutionMap initValues = null ;
+ if ( varName != null )
+ initValues = querySolution(varName, value) ;
+ try(QueryExecution qExec = QueryExecutionFactory.create(query, m, initValues)) {
+ ResultSet rs = ResultSetFactory.copyResults(qExec.execSelect()) ;
+ return rs ;
+ }
+ }
+
+ private static QuerySolutionMap querySolution(String varName, RDFNode value)
+ {
+ QuerySolutionMap qsm = new QuerySolutionMap() ;
+ querySolution(qsm, varName, value) ;
+ return qsm ;
+ }
+
+ private static QuerySolutionMap querySolution(QuerySolutionMap qsm, String varName, RDFNode value)
+ {
+ qsm.add(varName, value) ;
+ return qsm ;
+ }
+
+ // Node presentation
+ private static String nodeLabel(RDFNode n)
+ {
+ if ( n == null )
+ return "<null>" ;
+ if ( n instanceof Resource )
+ return strForResource((Resource)n) ;
+
+ Literal lit = (Literal)n ;
+ return lit.getLexicalForm() ;
+ }
+
+ private static String strForResource(Resource r) { return strForResource(r, r.getModel()) ; }
+
+ private static String strForResource(Resource r, PrefixMapping pm)
+ {
+ if ( r == null )
+ return "NULL ";
+ if ( r.hasProperty(RDFS.label))
+ {
+ RDFNode n = r.getProperty(RDFS.label).getObject() ;
+ if ( n instanceof Literal )
+ return ((Literal)n).getString() ;
+ }
+
+ if ( r.isAnon() )
+ return "<<blank node>>" ;
+
+ if ( pm == null )
+ pm = r.getModel() ;
+
+ return strForURI(r.getURI(), pm ) ;
+ }
+
+ private static String strForURI(String uri, PrefixMapping pm)
+ {
+ if ( pm != null )
+ {
+ String x = pm.shortForm(uri) ;
+
+ if ( ! x.equals(uri) )
+ return x ;
+ }
+ return "<"+uri+">" ;
+ }
+}
+
http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/FusekiErrorHandler.java
----------------------------------------------------------------------
diff --git a/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/FusekiErrorHandler.java b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/FusekiErrorHandler.java
new file mode 100644
index 0000000..d1660f5
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/FusekiErrorHandler.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.server;
+
+import static java.lang.String.format ;
+
+import java.io.* ;
+
+import javax.servlet.http.HttpServletRequest ;
+import javax.servlet.http.HttpServletResponse ;
+
+import org.apache.jena.fuseki.Fuseki ;
+import org.apache.jena.web.HttpSC ;
+import org.eclipse.jetty.http.HttpHeaders ;
+import org.eclipse.jetty.http.HttpMethods ;
+import org.eclipse.jetty.http.MimeTypes ;
+import org.eclipse.jetty.server.AbstractHttpConnection ;
+import org.eclipse.jetty.server.Request ;
+import org.eclipse.jetty.server.handler.ErrorHandler ;
+
+public class FusekiErrorHandler extends ErrorHandler
+{
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
+ {
+ AbstractHttpConnection connection = AbstractHttpConnection.getCurrentConnection();
+ connection.getRequest().setHandled(true);
+ String method = request.getMethod();
+
+ if(!method.equals(HttpMethods.GET) && !method.equals(HttpMethods.POST) && !method.equals(HttpMethods.HEAD))
+ return;
+
+ response.setContentType(MimeTypes.TEXT_PLAIN_UTF_8) ;
+ response.setHeader(HttpHeaders.CACHE_CONTROL, "must-revalidate,no-cache,no-store") ;
+
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream(1024) ;
+ //String writer = IO.UTF8(null) ;
+ try(Writer writer = new OutputStreamWriter(bytes, "UTF-8")) {
+
+ handleErrorPage(request, writer, connection.getResponse().getStatus(), connection.getResponse().getReason());
+
+ if ( ! Fuseki.VERSION.equalsIgnoreCase("development") )
+ {
+ writer.write("\n") ;
+ writer.write("\n") ;
+ writer.write(format("Fuseki - version %s (Build date: %s)\n", Fuseki.VERSION, Fuseki.BUILD_DATE)) ;
+ }
+ writer.flush();
+ }
+ response.setContentLength(bytes.size()) ;
+ // Copy
+ response.getOutputStream().write(bytes.toByteArray()) ;
+ }
+
+ @Override
+ protected void handleErrorPage(HttpServletRequest request, Writer writer, int code, String message)
+ throws IOException
+ {
+ if ( message == null )
+ message = HttpSC.getMessage(code) ;
+ writer.write(format("Error %d: %s\n", code, message)) ;
+
+ Throwable th = (Throwable)request.getAttribute("javax.servlet.error.exception");
+ while(th!=null)
+ {
+ writer.write("\n");
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ th.printStackTrace(pw);
+ pw.flush();
+ writer.write(sw.getBuffer().toString());
+ writer.write("\n");
+ th = th.getCause();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/FusekiServletContextListener.java
----------------------------------------------------------------------
diff --git a/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/FusekiServletContextListener.java b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/FusekiServletContextListener.java
new file mode 100644
index 0000000..f1c9642
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/FusekiServletContextListener.java
@@ -0,0 +1,43 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.server;
+
+import javax.servlet.ServletContextEvent ;
+import javax.servlet.ServletContextListener ;
+
+public class FusekiServletContextListener implements ServletContextListener {
+ // This could do the initialization.
+ private final SPARQLServer sparqlServer ;
+ public FusekiServletContextListener(SPARQLServer sparqlServer) {
+ this.sparqlServer = sparqlServer ;
+ }
+
+ @Override
+ public void contextInitialized(ServletContextEvent sce) {
+// Fuseki.serverLog.info("contextInitialized") ;
+// for ( DatasetRef dsRef : sparqlServer.getDatasets() )
+// Fuseki.serverLog.info("Dataset: "+dsRef.getName()) ;
+ }
+
+ @Override
+ public void contextDestroyed(ServletContextEvent sce) {
+// Fuseki.serverLog.info("contextDestroyed") ;
+ }
+}
+
http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/FusekiVocab.java
----------------------------------------------------------------------
diff --git a/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/FusekiVocab.java b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/FusekiVocab.java
new file mode 100644
index 0000000..d4d4e54
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/FusekiVocab.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.server;
+
+import org.apache.jena.fuseki.FusekiException ;
+import org.apache.jena.iri.IRI ;
+import org.apache.jena.riot.system.IRIResolver ;
+
+import com.hp.hpl.jena.rdf.model.Property ;
+import com.hp.hpl.jena.rdf.model.Resource ;
+import com.hp.hpl.jena.rdf.model.ResourceFactory ;
+
+public class FusekiVocab
+{
+ public static String NS = "http://jena.apache.org/fuseki#" ;
+
+ public static final Resource tServer = resource("Server") ;
+
+ public static final Property pServices = property("services") ;
+ public static final Property pServiceName = property("name") ;
+
+ public static final Property pServiceQueryEP = property("serviceQuery") ;
+ public static final Property pServiceUpdateEP = property("serviceUpdate") ;
+ public static final Property pServiceUploadEP = property("serviceUpload") ;
+ public static final Property pServiceReadWriteGraphStoreEP = property("serviceReadWriteGraphStore") ;
+ public static final Property pServiceReadgraphStoreEP = property("serviceReadGraphStore") ;
+
+ public static final Property pAllowTimeoutOverride = property("allowTimeoutOverride");
+ public static final Property pMaximumTimeoutOverride = property("maximumTimeoutOverride");
+
+ private static Resource resource(String localname) { return ResourceFactory.createResource(iri(localname)) ; }
+ private static Property property(String localname) { return ResourceFactory.createProperty(iri(localname)) ; }
+
+ private static String iri(String localname)
+ {
+ String uri = NS+localname ;
+ IRI iri = IRIResolver.parseIRI(uri) ;
+ if ( iri.hasViolation(true) )
+ throw new FusekiException("Bad IRI: "+iri) ;
+ if ( ! iri.isAbsolute() )
+ throw new FusekiException("Bad IRI: "+iri) ;
+
+ return uri ;
+ }
+}
+
http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/SPARQLServer.java
----------------------------------------------------------------------
diff --git a/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/SPARQLServer.java b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/SPARQLServer.java
new file mode 100644
index 0000000..7448eb2
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/SPARQLServer.java
@@ -0,0 +1,484 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.server ;
+
+import static java.lang.String.format ;
+import static org.apache.jena.fuseki.Fuseki.serverLog ;
+
+import java.io.FileInputStream ;
+import java.util.* ;
+
+import javax.servlet.DispatcherType ;
+import javax.servlet.http.HttpServlet ;
+
+import org.apache.jena.atlas.lib.FileOps ;
+import org.apache.jena.fuseki.Fuseki ;
+import org.apache.jena.fuseki.FusekiException ;
+import org.apache.jena.fuseki.HttpNames ;
+import org.apache.jena.fuseki.mgt.ActionDataset ;
+import org.apache.jena.fuseki.mgt.MgtFunctions ;
+import org.apache.jena.fuseki.mgt.PageNames ;
+import org.apache.jena.fuseki.servlets.* ;
+import org.apache.jena.fuseki.validation.DataValidator ;
+import org.apache.jena.fuseki.validation.IRIValidator ;
+import org.apache.jena.fuseki.validation.QueryValidator ;
+import org.apache.jena.fuseki.validation.UpdateValidator ;
+import org.apache.jena.riot.WebContent ;
+import org.eclipse.jetty.http.MimeTypes ;
+import org.eclipse.jetty.security.* ;
+import org.eclipse.jetty.security.authentication.BasicAuthenticator ;
+import org.eclipse.jetty.server.Connector ;
+import org.eclipse.jetty.server.Server ;
+import org.eclipse.jetty.server.nio.BlockingChannelConnector ;
+import org.eclipse.jetty.servlet.DefaultServlet ;
+import org.eclipse.jetty.servlet.ServletContextHandler ;
+import org.eclipse.jetty.servlet.ServletHolder ;
+import org.eclipse.jetty.servlets.GzipFilter ;
+import org.eclipse.jetty.util.security.Constraint ;
+import org.eclipse.jetty.xml.XmlConfiguration ;
+
+import com.hp.hpl.jena.sparql.mgt.ARQMgt ;
+import com.hp.hpl.jena.sparql.util.Utils ;
+
+/**
+ * SPARQLServer is the Jena server instance which wraps/utilizes
+ * {@link org.eclipse.jetty.server.Server}. This class provides
+ * immediate access to the {@link org.eclipse.jetty.server.Server#start()} and
+ * {@link org.eclipse.jetty.server.Server#stop()} commands as well as obtaining
+ * instances of the server and server configuration. Finally we can obtain
+ * instances of {@link org.apache.jena.fuseki.server.ServerConfig}.
+ *
+ */
+public class SPARQLServer {
+ static {
+ Fuseki.init() ;
+ }
+
+ private ServerConfig serverConfig ;
+
+ private Server server = null ;
+ private static List<String> epDataset = Arrays.asList("*") ;
+
+ /**
+ * Default constructor which requires a {@link org.apache.jena.fuseki.server.ServerConfig}
+ * object as input. We use this config to specify (verbose) logging, enable compression
+ * etc.
+ * @param config
+ */
+ public SPARQLServer(ServerConfig config) {
+ this.serverConfig = config ;
+ // Currently server-wide.
+ Fuseki.verboseLogging = config.verboseLogging ;
+
+ // GZip compression
+ // Note that regardless of this setting we'll always leave it turned off
+ // for the servlets
+ // where it makes no sense to have it turned on e.g. update and upload
+
+ ServletContextHandler context = buildServer(serverConfig.jettyConfigFile, config.enableCompression) ;
+ configureDatasets(context) ;
+ }
+
+ private void configureDatasets(ServletContextHandler context) {
+ // Build them all.
+ for (DatasetRef dsDesc : serverConfig.datasets)
+ configureOneDataset(context, dsDesc, serverConfig.enableCompression) ;
+
+ }
+
+ /**
+ * Initialize the {@link SPARQLServer} instance.
+ */
+ public void start() {
+ String now = Utils.nowAsString() ;
+ serverLog.info(format("%s %s %s", Fuseki.NAME, Fuseki.VERSION, Fuseki.BUILD_DATE)) ;
+ // This does not get set usefully for Jetty as we use it.
+ // String jettyVersion = org.eclipse.jetty.server.Server.getVersion() ;
+ // serverLog.info(format("Jetty %s",jettyVersion)) ;
+ String host = server.getConnectors()[0].getHost() ;
+ if ( host != null )
+ serverLog.info("Incoming connections limited to " + host) ;
+ serverLog.info(format("Started %s on port %d", now, server.getConnectors()[0].getPort())) ;
+
+ try {
+ server.start() ;
+ } catch (java.net.BindException ex) {
+ serverLog.error("SPARQLServer: Failed to start server: " + ex.getMessage()) ;
+ System.exit(1) ;
+ } catch (Exception ex) {
+ serverLog.error("SPARQLServer: Failed to start server: " + ex.getMessage(), ex) ;
+ System.exit(1) ;
+ }
+
+ ServletContextHandler context = (ServletContextHandler)server.getHandler() ;
+ }
+
+ /**
+ * Stop the {@link SPARQLServer} instance.
+ */
+ public void stop() {
+ String now = Utils.nowAsString() ;
+ serverLog.info(format("Stopped %s on port %d", now, server.getConnectors()[0].getPort())) ;
+ try {
+ server.stop() ;
+ } catch (Exception ex) {
+ Fuseki.serverLog.warn("SPARQLServer: Exception while stopping server: " + ex.getMessage(), ex) ;
+ }
+ removeJMX() ;
+ }
+
+ /**
+ * Get the Jetty instance.
+ * @return Server
+ */
+ public Server getServer() {
+ return server ;
+ }
+
+ /**
+ * Get the datasets associated with the server.
+ * @return returns the datasets via {@link org.apache.jena.fuseki.server.ServerConfig#datasets}
+ */
+ public List<DatasetRef> getDatasets() {
+ return serverConfig.datasets ;
+ }
+
+ /**
+ * Obtain the {@link org.apache.jena.fuseki.server.ServerConfig}
+ * @return ServerConfig
+ */
+ public ServerConfig getServerConfig() {
+ return serverConfig ;
+ }
+
+ // Later : private and in constructor.
+ private ServletContextHandler buildServer(String jettyConfig, boolean enableCompression) {
+ if ( jettyConfig != null ) {
+ // --jetty-config=jetty-fuseki.xml
+ // for detailed configuration of the server using Jetty features.
+ server = configServer(jettyConfig) ;
+ } else
+ server = defaultServerConfig(serverConfig.port, serverConfig.loopback) ;
+ // Keep the server to a maximum number of threads.
+ // server.setThreadPool(new QueuedThreadPool(ThreadPoolSize)) ;
+
+ ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS) ;
+ context.setErrorHandler(new FusekiErrorHandler()) ;
+ context.addEventListener(new FusekiServletContextListener(this));
+
+ // Increase form size.
+ context.getServletContext().getContextHandler().setMaxFormContentSize(10 * 1000 * 1000) ;
+
+ // Wire up authentication if appropriate
+ if ( jettyConfig == null && serverConfig.authConfigFile != null ) {
+ Constraint constraint = new Constraint() ;
+ constraint.setName(Constraint.__BASIC_AUTH) ;
+ constraint.setRoles(new String[]{"fuseki"}) ;
+ constraint.setAuthenticate(true) ;
+
+ ConstraintMapping mapping = new ConstraintMapping() ;
+ mapping.setConstraint(constraint) ;
+ mapping.setPathSpec("/*") ;
+
+ IdentityService identService = new DefaultIdentityService() ;
+
+ ConstraintSecurityHandler securityHandler = new ConstraintSecurityHandler() ;
+ securityHandler.addConstraintMapping(mapping) ;
+ securityHandler.setIdentityService(identService) ;
+
+ HashLoginService loginService = new HashLoginService("Fuseki Authentication", serverConfig.authConfigFile) ;
+ loginService.setIdentityService(identService) ;
+
+ securityHandler.setLoginService(loginService) ;
+ securityHandler.setAuthenticator(new BasicAuthenticator()) ;
+
+ context.setSecurityHandler(securityHandler) ;
+
+ serverLog.debug("Basic Auth Configuration = " + serverConfig.authConfigFile) ;
+ }
+
+ // Wire up context handler to server
+ server.setHandler(context) ;
+
+ // Constants. Add RDF types.
+ MimeTypes mt = new MimeTypes() ;
+ mt.addMimeMapping("rdf", WebContent.contentTypeRDFXML + ";charset=utf-8") ;
+ mt.addMimeMapping("ttl", WebContent.contentTypeTurtle + ";charset=utf-8") ;
+ mt.addMimeMapping("nt", WebContent.contentTypeNTriples + ";charset=ascii") ;
+ mt.addMimeMapping("nq", WebContent.contentTypeNQuads + ";charset=ascii") ;
+ mt.addMimeMapping("trig", WebContent.contentTypeTriG + ";charset=utf-8") ;
+
+ // mt.addMimeMapping("tpl", "text/html;charset=utf-8") ;
+ context.setMimeTypes(mt) ;
+ server.setHandler(context) ;
+
+ serverLog.debug("Pages = " + serverConfig.pages) ;
+
+ boolean installManager = true ;
+ boolean installServices = true ;
+
+ String validationRoot = "/validate" ;
+
+ // Should all services be /_/.... or some such?
+
+ if ( installManager || installServices ) {
+ // TODO Respect port.
+ if ( serverConfig.pagesPort != serverConfig.port )
+ serverLog.warn("Not supported yet - pages on a different port to services") ;
+ if ( serverConfig.pages != null ) {
+ if ( ! FileOps.exists(serverConfig.pages) )
+ serverLog.warn("No pages directory - "+serverConfig.pages) ;
+ String base = serverConfig.pages ;
+ Map<String, Object> data = new HashMap<String, Object>() ;
+ data.put("mgt", new MgtFunctions()) ;
+ SimpleVelocityServlet templateEngine = new SimpleVelocityServlet(base, data) ;
+ addServlet(context, templateEngine, "*.tpl", false) ;
+ }
+ }
+
+ if ( installManager ) {
+ // Action when control panel selects a dataset.
+ HttpServlet datasetChooser = new ActionDataset() ;
+ addServlet(context, datasetChooser, PageNames.actionDatasetNames, false) ;
+ }
+
+ if ( installServices ) {
+ // Validators
+ HttpServlet validateQuery = new QueryValidator() ;
+ HttpServlet validateUpdate = new UpdateValidator() ;
+ HttpServlet validateData = new DataValidator() ;
+ HttpServlet validateIRI = new IRIValidator() ;
+
+ HttpServlet dumpService = new DumpServlet() ;
+ HttpServlet generalQueryService = new SPARQL_QueryGeneral() ;
+
+ addServlet(context, validateQuery, validationRoot + "/query", false) ;
+ addServlet(context, validateUpdate, validationRoot + "/update", false) ;
+ addServlet(context, validateData, validationRoot + "/data", false) ;
+ addServlet(context, validateIRI, validationRoot + "/iri", false) ;
+
+ // general query processor.
+ addServlet(context, generalQueryService, HttpNames.ServiceGeneralQuery, enableCompression) ;
+ }
+
+ if ( installManager || installServices ) {
+ String[] files = {"fuseki.html", "index.html"} ;
+ context.setWelcomeFiles(files) ;
+ addContent(context, "/", serverConfig.pages) ;
+ }
+
+ return context ;
+ }
+
+ /** Experimental - off by default. The überservlet sits on the dataset name and handles all requests.
+ * Includes direct naming and quad access to the dataset.
+ */
+ public static boolean überServlet = false ;
+
+ private static List<String> ListOfEmptyString = Arrays.asList("") ;
+
+ private void configureOneDataset(ServletContextHandler context, DatasetRef dsDesc, boolean enableCompression) {
+ String datasetPath = dsDesc.name ;
+ if ( datasetPath.equals("/") )
+ datasetPath = "" ;
+ else
+ if ( !datasetPath.startsWith("/") )
+ datasetPath = "/" + datasetPath ;
+
+ if ( datasetPath.endsWith("/") )
+ datasetPath = datasetPath.substring(0, datasetPath.length() - 1) ;
+
+ dsDesc.init() ;
+
+ DatasetRegistry.get().put(datasetPath, dsDesc) ;
+ serverLog.info(format("Dataset path = %s", datasetPath)) ;
+
+ HttpServlet sparqlQuery = new SPARQL_QueryDataset() ;
+ HttpServlet sparqlUpdate = new SPARQL_Update() ;
+ HttpServlet sparqlUpload = new SPARQL_Upload() ;
+ HttpServlet sparqlHttpR = new SPARQL_REST_R() ;
+ HttpServlet sparqlHttpRW = new SPARQL_REST_RW() ;
+ HttpServlet sparqlDataset = new SPARQL_UberServlet.AccessByConfig() ;
+
+ if ( !überServlet ) {
+ // If uberserver, these are unnecessary but can be used.
+ // If just means the überservlet isn't handling these operations.
+ addServlet(context, datasetPath, sparqlQuery, dsDesc.query, enableCompression) ;
+ addServlet(context, datasetPath, sparqlUpdate, dsDesc.update, false) ;
+ addServlet(context, datasetPath, sparqlUpload, dsDesc.upload, false) ; // No point - no results of any size.
+ addServlet(context, datasetPath, sparqlHttpR, dsDesc.readGraphStore, enableCompression) ;
+ addServlet(context, datasetPath, sparqlHttpRW, dsDesc.readWriteGraphStore, enableCompression) ;
+ // This adds direct operations on the dataset itself.
+ // addServlet(context, datasetPath, sparqlDataset,
+ // ListOfEmptyString, enableCompression) ;
+ } else {
+ // This is the servlet that analyses requests and dispatches them to
+ // the appropriate servlet.
+ // SPARQL Query, SPARQL Update -- handles dataset?query=
+ // dataset?update=
+ // Graph Store Protocol (direct and indirect naming) if enabled.
+ // GET/PUT/POST on the dataset itself.
+ // It also checks for a request that looks like a service request
+ // and passes it
+ // on to the service (this takes precedence over direct naming).
+ addServlet(context, datasetPath, sparqlDataset, epDataset, enableCompression) ;
+ }
+
+ // Add JMX beans to record daatset and it's services.
+ addJMX(dsDesc) ;
+ }
+
+ private static Server configServer(String jettyConfig) {
+ try {
+ serverLog.info("Jetty server config file = " + jettyConfig) ;
+ Server server = new Server() ;
+ XmlConfiguration configuration = new XmlConfiguration(new FileInputStream(jettyConfig)) ;
+ configuration.configure(server) ;
+ return server ;
+ } catch (Exception ex) {
+ serverLog.error("SPARQLServer: Failed to configure server: " + ex.getMessage(), ex) ;
+ throw new FusekiException("Failed to configure a server using configuration file '" + jettyConfig + "'") ;
+ }
+ }
+
+ private static Server defaultServerConfig(int port, boolean loopback) {
+ // Server, with one NIO-based connector, large input buffer size (for
+ // long URLs, POSTed forms (queries, updates)).
+ Server server = new Server() ;
+
+ // Using "= new SelectChannelConnector() ;" on Darwin (OS/X) causes
+ // problems
+ // with initialization not seen (thread scheduling?) in Joseki.
+
+ // BlockingChannelConnector is better for pumping large responses back
+ // but there have been observed problems with DirectMemory allocation
+ // (-XX:MaxDirectMemorySize=1G does not help)
+ // Connector connector = new SelectChannelConnector() ;
+
+ // Connector and specific settings.
+ BlockingChannelConnector bcConnector = new BlockingChannelConnector() ;
+ // bcConnector.setUseDirectBuffers(false) ;
+
+ Connector connector = bcConnector ;
+ // Ignore. If set, then if this goes off, it keeps going off
+ // and you get a lot of log messages.
+ connector.setMaxIdleTime(0) ; // Jetty outputs a lot of messages if this
+ // goes off.
+ if ( loopback )
+ connector.setHost("localhost");
+ connector.setPort(port) ;
+ // Some people do try very large operations ...
+ connector.setRequestHeaderSize(64 * 1024) ;
+ connector.setRequestBufferSize(5 * 1024 * 1024) ;
+ connector.setResponseBufferSize(5 * 1024 * 1024) ;
+ server.addConnector(connector) ;
+ return server ;
+ }
+
+ private static void addContent(ServletContextHandler context, String pathSpec, String pages) {
+ DefaultServlet staticServlet = new DefaultServlet() ;
+ ServletHolder staticContent = new ServletHolder(staticServlet) ;
+ staticContent.setInitParameter("resourceBase", pages) ;
+
+ // Note we set GZip to false for static content because the Jetty
+ // DefaultServlet has
+ // a built-in GZip capability that is better for static content than the
+ // mechanism the
+ // GzipFilter uses for dynamic content
+ addServlet(context, staticContent, pathSpec, false) ;
+ }
+
+ private void addServlet(ServletContextHandler context, String datasetPath, HttpServlet servlet,
+ ServiceRef serviceRef, boolean enableCompression) {
+ addServlet(context, datasetPath, servlet, serviceRef.endpoints, enableCompression) ;
+ }
+
+ // SHARE
+ private static void addServlet(ServletContextHandler context, String datasetPath, HttpServlet servlet,
+ List<String> pathSpecs, boolean enableCompression) {
+ for (String pathSpec : pathSpecs) {
+ if ( pathSpec.equals("") ) {
+ // "" is special -- add as "base" and "base/"
+ addServlet(context, servlet, datasetPath + "/", enableCompression) ;
+ addServlet(context, servlet, datasetPath, enableCompression) ;
+ continue ;
+ }
+
+ if ( pathSpec.endsWith("/") )
+ pathSpec = pathSpec.substring(0, pathSpec.length() - 1) ;
+ if ( pathSpec.startsWith("/") )
+ pathSpec = pathSpec.substring(1, pathSpec.length()) ;
+ addServlet(context, servlet, datasetPath + "/" + pathSpec, enableCompression) ;
+ }
+ }
+
+ private static void addServlet(ServletContextHandler context, HttpServlet servlet, String pathSpec,
+ boolean enableCompression) {
+ ServletHolder holder = new ServletHolder(servlet) ;
+ addServlet(context, holder, pathSpec, enableCompression) ;
+ }
+
+ private static void addServlet(ServletContextHandler context, ServletHolder holder, String pathSpec,
+ boolean enableCompression) {
+ if ( serverLog.isDebugEnabled() ) {
+ if ( enableCompression )
+ serverLog.debug("Add servlet @ " + pathSpec + " (with gzip)") ;
+ else
+ serverLog.debug("Add servlet @ " + pathSpec) ;
+ }
+ context.addServlet(holder, pathSpec) ;
+
+ if ( enableCompression )
+ context.addFilter(GzipFilter.class, pathSpec, EnumSet.allOf(DispatcherType.class)) ;
+ }
+
+ private void addJMX() {
+ DatasetRegistry registry = DatasetRegistry.get() ;
+ for (String ds : registry.keys()) {
+ DatasetRef dsRef = registry.get(ds) ;
+ addJMX(dsRef) ;
+ }
+ }
+
+ private void addJMX(DatasetRef dsRef) {
+ String x = dsRef.name ;
+ // if ( x.startsWith("/") )
+ // x = x.substring(1) ;
+ ARQMgt.register(Fuseki.PATH + ".dataset:name=" + x, dsRef) ;
+ // For all endpoints
+ for (ServiceRef sRef : dsRef.getServiceRefs()) {
+ ARQMgt.register(Fuseki.PATH + ".dataset:name=" + x + "/" + sRef.name, sRef) ;
+ }
+ }
+
+ private void removeJMX() {
+ DatasetRegistry registry = DatasetRegistry.get() ;
+ for (String ds : registry.keys()) {
+ DatasetRef ref = registry.get(ds) ;
+ }
+ }
+
+ private void removeJMX(DatasetRef dsRef) {
+ String x = dsRef.getName() ;
+ ARQMgt.unregister(Fuseki.PATH + ".dataset:name=" + x) ;
+ for (ServiceRef sRef : dsRef.getServiceRefs()) {
+ ARQMgt.unregister(Fuseki.PATH + ".dataset:name=" + x + "/" + sRef.name) ;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/ServerConfig.java
----------------------------------------------------------------------
diff --git a/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/ServerConfig.java b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/ServerConfig.java
new file mode 100644
index 0000000..4e0b865
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/ServerConfig.java
@@ -0,0 +1,55 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.server;
+
+import java.util.List ;
+
+/** This represents a configuration of a SPARQL server.
+ */
+
+public class ServerConfig
+{
+ public ServerConfig() {}
+
+ /** Port to run the server service on */
+ public int port ;
+ /** Port for the management interface : -1 for no management interface */
+ public int mgtPort ;
+ /** Port for the pages UI : this can be the same as the services port. */
+ public int pagesPort ;
+ /** Jetty config file - if null, use the built-in configuration of Jetty */
+ public String jettyConfigFile = null ;
+ /** Listen only on the loopback (localhost) interface */
+ public boolean loopback = false ;
+ /** The local directory for serving the static pages */
+ public String pages ;
+ /** The list of datasets */
+ public List<DatasetRef> datasets ;
+ /** Enable Accept-Encoding compression. Set to false by default.*/
+ public boolean enableCompression = false ;
+
+ /** Enable additional logging */
+ public boolean verboseLogging = false ;
+ /**
+ * Authentication config file used to setup Jetty Basic auth, if a Jetty config file was set this is ignored since Jetty config allows much more complex auth methods to be implemented
+ */
+ public String authConfigFile ;
+
+}
+
http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/ServiceMXBean.java
----------------------------------------------------------------------
diff --git a/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/ServiceMXBean.java b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/ServiceMXBean.java
new file mode 100644
index 0000000..11c7330
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/ServiceMXBean.java
@@ -0,0 +1,32 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.server;
+
+public interface ServiceMXBean
+{
+ String getName() ;
+
+ long getRequests() ;
+ long getRequestsGood() ;
+ long getRequestsBad() ;
+
+// void enable() ;
+// void disable() ;
+}
+
http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/ServiceRef.java
----------------------------------------------------------------------
diff --git a/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/ServiceRef.java b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/ServiceRef.java
new file mode 100644
index 0000000..6236050
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/server/ServiceRef.java
@@ -0,0 +1,63 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.server;
+
+import java.util.ArrayList ;
+import java.util.List ;
+
+/** Configuration of an individual service */
+public class ServiceRef implements ServiceMXBean, Counters
+{
+ public final String name ;
+
+ // Service-level counters.
+ private final CounterSet counters = new CounterSet() ;
+ @Override
+ public CounterSet getCounters() { return counters ; }
+
+ /** Endpoints (as absolute path URLs) */
+ public List<String> endpoints = new ArrayList<String>() ;
+
+ // Attach counters to services or datasets
+ // Can we have a counter of the same name on different services?
+ // Cost : number of maps.
+ // +ve: Multiple services with the same name counter
+
+ public ServiceRef(String serviceName) {
+ this.name = serviceName ;
+ }
+
+ public boolean isActive() { return endpoints.isEmpty() ; }
+
+ @Override
+ public String getName() { return name ; }
+
+ @Override public long getRequests() {
+ return counters.value(CounterName.Requests) ;
+ }
+ @Override
+ public long getRequestsGood() {
+ return counters.value(CounterName.RequestsGood) ;
+ }
+ @Override
+ public long getRequestsBad() {
+ return counters.value(CounterName.RequestsBad) ;
+ }
+}
+
http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/ActionErrorException.java
----------------------------------------------------------------------
diff --git a/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/ActionErrorException.java b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/ActionErrorException.java
new file mode 100644
index 0000000..6c5ebe9
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/ActionErrorException.java
@@ -0,0 +1,32 @@
+/*
+ * 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.servlets;
+
+class ActionErrorException extends RuntimeException
+{
+ final Throwable exception ;
+ final String message ;
+ final int rc ;
+ ActionErrorException(Throwable ex, String message, int rc)
+ {
+ this.exception = ex ;
+ this.message = message ;
+ this.rc = rc ;
+ }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/ConcurrencyPolicyMRSW.java
----------------------------------------------------------------------
diff --git a/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/ConcurrencyPolicyMRSW.java b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/ConcurrencyPolicyMRSW.java
new file mode 100644
index 0000000..259453d
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/ConcurrencyPolicyMRSW.java
@@ -0,0 +1,113 @@
+/*
+ * 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.servlets;
+
+import java.util.ConcurrentModificationException ;
+import java.util.concurrent.atomic.AtomicLong ;
+
+import org.apache.jena.fuseki.Fuseki ;
+import org.slf4j.Logger ;
+
+public final class ConcurrencyPolicyMRSW
+{
+ static private Logger log = Fuseki.requestLog ; //org.slf4j.LoggerFactory.getLogger(ConcurrencyPolicyMRSW.class) ;
+ static private final boolean logging = false ; //log.isDebugEnabled() ;
+
+ // This is a simplified version of ConcurrencyPolicyMRSW from TDB.
+ private final AtomicLong readCounter = new AtomicLong(0) ;
+ private final AtomicLong writeCounter = new AtomicLong(0) ;
+ static private final AtomicLong policyCounter = new AtomicLong(0) ;
+
+ public ConcurrencyPolicyMRSW()
+ { policyCounter.incrementAndGet() ; }
+
+ // Loggin -inside the operation.
+
+ //@Override
+ public void startRead()
+ {
+ readCounter.getAndIncrement() ;
+ log() ;
+ checkConcurrency() ;
+ }
+
+ //@Override
+ public void finishRead()
+ {
+ log() ;
+ readCounter.decrementAndGet() ;
+ checkConcurrency() ;
+ }
+
+ //@Override
+ public void startUpdate()
+ {
+ writeCounter.getAndIncrement() ;
+ log() ;
+ checkConcurrency() ;
+ }
+
+ //@Override
+ public void finishUpdate()
+ {
+ log() ;
+ writeCounter.decrementAndGet() ;
+ checkConcurrency() ;
+ }
+
+ private synchronized void checkConcurrency()
+ {
+ long R = readCounter.get() ;
+ long W = writeCounter.get() ;
+ long id = policyCounter.get();
+ if ( R > 0 && W > 0 )
+ policyError(id, R, W) ;
+ if ( W > 1 )
+ policyError(id, R, W) ;
+ }
+
+ private void log()
+ {
+ if ( ! logging )
+ return ;
+ long R , W , id ;
+ synchronized(this)
+ {
+ R = readCounter.get() ;
+ W = writeCounter.get() ;
+ id = policyCounter.get();
+ }
+ log.info(format(id, R, W)) ;
+ }
+
+ private static void policyError(long id, long R, long W)
+ {
+ policyError(format(id, R, W)) ;
+ }
+
+ private static void policyError(String message)
+ {
+ throw new ConcurrentModificationException(message) ;
+ }
+
+ private static String format(long id, long R, long W)
+ {
+ return String.format("(lock=%d) Reader = %d, Writer = %d", id, R, W) ;
+ }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/DumpServlet.java
----------------------------------------------------------------------
diff --git a/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/DumpServlet.java b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/DumpServlet.java
new file mode 100644
index 0000000..7ece249
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/DumpServlet.java
@@ -0,0 +1,313 @@
+/*
+ * 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.servlets;
+
+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 ;
+
+public class DumpServlet extends HttpServlet
+{
+ private static final long serialVersionUID = 99L; // Serilizable.
+
+
+ public DumpServlet()
+ {
+
+ }
+
+ @Override
+ public void init()
+ {
+ return ;
+ }
+
+ @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)
+ {
+ StringWriter sw = new StringWriter() ;
+ try( PrintWriter pw = new PrintWriter(sw) ) {
+ // Standard environment
+ pw.println("Method: "+req.getMethod());
+ pw.println("getContentLength: "+Integer.toString(req.getContentLength()));
+ 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 ( Cookie aC : c )
+ {
+ pw.println( "Cookie: " + aC.getName() );
+ pw.println( " value: " + aC.getValue() );
+ pw.println( " version: " + aC.getVersion() );
+ pw.println( " comment: " + aC.getComment() );
+ pw.println( " domain: " + aC.getDomain() );
+ pw.println( " maxAge: " + aC.getMaxAge() );
+ pw.println( " path: " + aC.getPath() );
+ pw.println( " secure: " + aC.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() ;
+ pw.flush();
+ //printBody(pw, req) ;
+ return sw.toString() ;
+ }
+
+ }
+
+ 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();
+
+ StringWriter sw = new StringWriter() ;
+ try(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() ;
+ pw.flush() ;
+ return sw.toString() ;
+ }
+ }
+
+ public String dumpServletContext()
+ {
+ StringWriter sw = new StringWriter() ;
+ try(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() ;
+ pw.close() ;
+ }
+ return sw.toString() ;
+ }
+
+
+ @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/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/HttpAction.java
----------------------------------------------------------------------
diff --git a/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/HttpAction.java b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/HttpAction.java
new file mode 100644
index 0000000..39663dd
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/HttpAction.java
@@ -0,0 +1,290 @@
+/*
+ * 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.servlets;
+
+import static com.hp.hpl.jena.query.ReadWrite.READ ;
+import static com.hp.hpl.jena.query.ReadWrite.WRITE ;
+
+import java.util.HashMap ;
+import java.util.Map ;
+
+import javax.servlet.http.HttpServletRequest ;
+import javax.servlet.http.HttpServletResponse ;
+
+import org.apache.jena.atlas.logging.Log ;
+import org.apache.jena.atlas.web.MediaType ;
+import org.apache.jena.fuseki.DEF ;
+import org.apache.jena.fuseki.conneg.ConNeg ;
+import org.apache.jena.fuseki.server.DatasetRef ;
+import org.apache.jena.fuseki.server.ServiceRef ;
+
+import com.hp.hpl.jena.query.ReadWrite ;
+import com.hp.hpl.jena.sparql.SystemARQ ;
+import com.hp.hpl.jena.sparql.core.DatasetGraph ;
+import com.hp.hpl.jena.sparql.core.DatasetGraphWithLock ;
+import com.hp.hpl.jena.sparql.core.DatasetGraphWrapper ;
+import com.hp.hpl.jena.sparql.core.Transactional ;
+
+/**
+ * HTTP action that represents the user request lifecycle. Its state is handled in the
+ * {@link SPARQL_ServletBase#executeLifecycle(HttpAction)} method.
+ */
+public class HttpAction
+{
+ public final long id ;
+ public final boolean verbose ;
+
+ // Phase two items - set and valida after the datasetRef is known.
+ private DatasetGraph dsg ; // The data
+ public DatasetRef dsRef ;
+ public ServiceRef srvRef ;
+
+ private Transactional transactional ;
+ private boolean isTransactional;
+ private DatasetGraph activeDSG ; // Set when inside begin/end.
+ private ReadWrite activeMode ; // Set when inside begin/end.
+
+ private boolean startTimeIsSet = false ;
+ private boolean finishTimeIsSet = false ;
+
+ private long startTime = -2 ;
+ private long finishTime = -2 ;
+
+ // Incoming
+ //public final
+
+ // Outcome.
+ int statusCode = -1 ;
+ String message = null ;
+ int contentLength = -1 ;
+ String contentType = null ;
+
+ // Cleared to archive:
+ Map <String, String> headers = new HashMap<String, String>() ;
+ public HttpServletRequest request;
+ public HttpServletResponseTracker response ;
+
+ /**
+ * Creates a new HTTP Action, using the HTTP request and response, and a given ID.
+ *
+ * @param id given ID
+ * @param request HTTP request
+ * @param response HTTP response
+ * @param verbose verbose flag
+ */
+ public HttpAction(long id, HttpServletRequest request, HttpServletResponse response, boolean verbose) {
+ this.id = id ;
+ this.request = request ;
+ this.response = new HttpServletResponseTracker(this, response) ;
+ // Should this be set when setDataset is called from the dataset context?
+ // Currently server-wide, e.g. from the command line.
+ this.verbose = verbose ;
+ }
+
+ /**
+ * <p>Sets the action dataset. Setting a {@link DatasetRef} will replace any existing DatasetRef, as well as
+ * as the {@link DatasetGraph} of the current HTTP Action.</p>
+ *
+ * <p>Once it has updated its members, the HTTP Action will change its transactional state and
+ * {@link Transactional} instance according to its base dataset graph.</p>
+ *
+ * @param desc {@link DatasetRef}
+ * @see Transactional
+ * @see DatasetGraphWrapper
+ */
+ public void setDataset(DatasetRef desc) {
+ this.dsRef = desc ;
+ this.dsg = desc.dataset ;
+ DatasetGraph basedsg = unwrap(dsg) ;
+
+ if ( isTransactional(basedsg) && isTransactional(dsg) ) {
+ // Use transactional if it looks safe - abort is necessary.
+ transactional = (Transactional)dsg ;
+ isTransactional = true ;
+ } else {
+ // Unsure if safe
+ transactional = new DatasetGraphWithLock(dsg) ;
+ // No real abort.
+ isTransactional = false ;
+ }
+ }
+
+ /**
+ * Returns <code>true</code> iff the given {@link DatasetGraph} is an instance of {@link Transactional},
+ * <code>false otherwise</code>.
+ *
+ * @param dsg a {@link DatasetGraph}
+ * @return <code>true</code> iff the given {@link DatasetGraph} is an instance of {@link Transactional},
+ * <code>false otherwise</code>
+ */
+ private static boolean isTransactional(DatasetGraph dsg) {
+ return (dsg instanceof Transactional) ;
+ }
+
+ /**
+ * A {@link DatasetGraph} may contain other <strong>wrapped DatasetGraph's</strong>. This method will return
+ * the first instance (including the argument to this method) that <strong>is not</strong> an instance of
+ * {@link DatasetGraphWrapper}.
+ *
+ * @param dsg a {@link DatasetGraph}
+ * @return the first found {@link DatasetGraph} that is not an instance of {@link DatasetGraphWrapper}
+ */
+ private static DatasetGraph unwrap(DatasetGraph dsg) {
+ while (dsg instanceof DatasetGraphWrapper) {
+ dsg = ((DatasetGraphWrapper)dsg).getWrapped() ;
+ }
+ return dsg ;
+ }
+
+ /**
+ * Sets the {@link ServiceRef}.
+ *
+ * @param srvRef a {@link ServiceRef}
+ */
+ public void setService(ServiceRef srvRef) {
+ this.srvRef = srvRef ;
+ }
+
+ /**
+ * Returns whether or not the underlying DatasetGraph is fully transactional (supports rollback).
+ * @return <code>true</code> if the underlying DatasetGraph is fully transactional (supports rollback),
+ * <code>false</code> otherwise.
+ */
+ public boolean isTransactional() {
+ return isTransactional ;
+ }
+
+ public void beginRead() {
+ activeMode = READ ;
+ transactional.begin(READ) ;
+ activeDSG = dsg ;
+ dsRef.startTxn(READ) ;
+ }
+
+ public void endRead() {
+ dsRef.finishTxn(READ) ;
+ activeMode = null ;
+ transactional.end() ;
+ activeDSG = null ;
+ }
+
+ public void beginWrite() {
+ transactional.begin(WRITE) ;
+ activeMode = WRITE ;
+ activeDSG = dsg ;
+ dsRef.startTxn(WRITE) ;
+ }
+
+ public void commit() {
+ transactional.commit() ;
+ activeDSG = null ;
+ }
+
+ public void abort() {
+ try { transactional.abort() ; }
+ catch (Exception ex) {
+ // Some datasets claim to be transactional but
+ // don't provide a real abort. We tried to avoid
+ // them earlier but even if they sneek through,
+ // we try to continue server operation.
+ Log.warn(this, "Exception during abort (operation attempts to continue): "+ex.getMessage()) ;
+ }
+ activeDSG = null ;
+ }
+
+ public void endWrite() {
+ dsRef.finishTxn(WRITE) ;
+ activeMode = null ;
+
+ if ( transactional.isInTransaction() ) {
+ Log.warn(this, "Transaction still active in endWriter - no commit or abort seen (forced abort)") ;
+ try {
+ transactional.abort() ;
+ } catch (RuntimeException ex) {
+ Log.warn(this, "Exception in forced abort (trying to continue)", ex) ;
+ }
+ }
+ transactional.end() ;
+ activeDSG = null ;
+ }
+
+ public final DatasetGraph getActiveDSG() {
+ return activeDSG ;
+ }
+
+ public final DatasetRef getDatasetRef() {
+ return dsRef ;
+ }
+
+ /** Reduce to a size that can be kept around for sometime */
+ public void minimize() {
+ this.request = null ;
+ this.response = null ;
+ }
+
+ public void setStartTime() {
+ if ( startTimeIsSet )
+ Log.warn(this, "Start time reset") ;
+ startTimeIsSet = true ;
+ this.startTime = System.nanoTime() ;
+ }
+
+ public void setFinishTime() {
+ if ( finishTimeIsSet )
+ Log.warn(this, "Finish time reset") ;
+ finishTimeIsSet = true ;
+ this.finishTime = System.nanoTime() ;
+ }
+
+ public HttpServletRequest getRequest() { return request ; }
+
+ public HttpServletResponseTracker getResponse() { return response ; }
+
+ /** Return the recorded time taken in milliseconds.
+ * {@linkplain #setStartTime} and {@linkplain #setFinishTime}
+ * must have been called.
+ */
+ public long getTime()
+ {
+ if ( ! startTimeIsSet )
+ Log.warn(this, "Start time not set") ;
+ if ( ! finishTimeIsSet )
+ Log.warn(this, "Finish time not set") ;
+ return (finishTime-startTime)/(1000*1000) ;
+ }
+
+ public void sync() {
+ SystemARQ.sync(dsg) ;
+ }
+
+ public static MediaType contentNegotationRDF(HttpAction action) {
+ MediaType mt = ConNeg.chooseContentType(action.request, DEF.rdfOffer, DEF.acceptRDFXML) ;
+ if ( mt == null )
+ return null ;
+ if ( mt.getContentType() != null )
+ action.response.setContentType(mt.getContentType()) ;
+ if ( mt.getCharset() != null )
+ action.response.setCharacterEncoding(mt.getCharset()) ;
+ return mt ;
+ }
+
+ public static MediaType contentNegotationQuads(HttpAction action) {
+ return ConNeg.chooseContentType(action.request, DEF.quadsOffer, DEF.acceptNQuads) ;
+ }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/HttpServletResponseTracker.java
----------------------------------------------------------------------
diff --git a/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/HttpServletResponseTracker.java b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/HttpServletResponseTracker.java
new file mode 100644
index 0000000..c39e728
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/HttpServletResponseTracker.java
@@ -0,0 +1,140 @@
+/*
+ * 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.servlets;
+
+import java.io.IOException ;
+
+import javax.servlet.http.HttpServletResponse ;
+import javax.servlet.http.HttpServletResponseWrapper ;
+
+import org.apache.jena.atlas.logging.Log ;
+
+/** Intercepting wrapper so we can track the response settings for logging purposes */
+
+public class HttpServletResponseTracker extends HttpServletResponseWrapper
+{
+ private final HttpAction action ;
+
+ public HttpServletResponseTracker(HttpAction action, HttpServletResponse response)
+ {
+ super(response) ;
+ this.action = action ;
+ }
+
+ @Override
+ public void sendError(int sc, String msg) throws IOException
+ {
+ action.statusCode = sc ;
+ action.message = msg ;
+ super.sendError(sc, msg) ;
+ }
+
+ @Override
+ public void sendError(int sc) throws IOException
+ {
+ action.statusCode = sc ;
+ action.message = null ;
+ super.sendError(sc) ;
+ }
+
+ @Override
+ public void setHeader(String name, String value)
+ {
+ super.setHeader(name, value) ;
+ action.headers.put(name, value) ;
+ }
+
+ @Override
+ public void addHeader(String name, String value)
+ {
+ Log.warn(this, "Unexpected addHeader - not recorded in log") ;
+ super.addHeader(name, value) ;
+ }
+ @Override
+ public void setStatus(int sc)
+ {
+ action.statusCode = sc ;
+ action.message = null ;
+ super.setStatus(sc) ;
+ }
+
+ @Override
+ @Deprecated
+ public void setStatus(int sc, String sm)
+ {
+ action.statusCode = sc ;
+ action.message = sm ;
+ super.setStatus(sc, sm) ;
+ }
+
+ @Override
+ public void setContentLength(int len)
+ {
+ action.contentLength = len ;
+ super.setContentLength(len) ;
+ }
+
+ @Override
+ public void setContentType(String type)
+ {
+ action.contentType = type ;
+ super.setContentType(type) ;
+ }
+
+ // From HttpServletResponse
+// public void addCookie(Cookie cookie) {}
+// public boolean containsHeader(String name) {}
+// public String encodeURL(String url) { }
+// public String encodeRedirectURL(String url) {}
+// public String encodeUrl(String url) {}
+// public String encodeRedirectUrl(String url) {}
+// public void sendError(int sc, String msg) throws IOException
+// public void sendError(int sc) throws IOException
+// public void sendRedirect(String location) throws IOException {}
+// public void setDateHeader(String name, long date) {}
+// public void addDateHeader(String name, long date) {}
+// public void setHeader(String name, String value)
+// public void addHeader(String name, String value)
+// public void setIntHeader(String name, int value) {}
+// public void addIntHeader(String name, int value) {}
+// public void setStatus(int sc)
+// public void setStatus(int sc, String sm)
+// public void sendRedirect(String location) throws IOException {}
+// public void setDateHeader(String name, long date) {}
+// public void addDateHeader(String name, long date) {}
+
+ // From ServletResponse.
+// public ServletResponse getResponse() {}
+// public void setResponse(ServletResponse response) {}
+// public void setCharacterEncoding(String charset) {}
+// public String getCharacterEncoding() {}
+// public ServletOutputStream getOutputStream() throws IOException {}
+// public PrintWriter getWriter() throws IOException {}
+// public void setContentLength(int len) {}
+// public void setContentType(String type) {}
+// public String getContentType() {
+// public void setBufferSize(int size) {}
+// public int getBufferSize() {}
+// public void flushBuffer() throws IOException {}
+// public boolean isCommitted() {}
+// public void reset() {}
+// public void resetBuffer() {}
+// public void setLocale(Locale loc) {}
+// public Locale getLocale() {}
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/NullOutputStream.java
----------------------------------------------------------------------
diff --git a/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/NullOutputStream.java b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/NullOutputStream.java
new file mode 100644
index 0000000..63e6562
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/NullOutputStream.java
@@ -0,0 +1,53 @@
+/*
+ * 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.servlets;
+
+import java.io.* ;
+
+/**
+* Code needed to implement an OutputStream that does nothing.
+*/
+
+
+public class NullOutputStream extends /*Filter*/OutputStream
+{
+ public NullOutputStream()
+ {
+ }
+
+ // The OutputStream operations
+ @Override
+ public void close() { /* .close() ;*/ }
+ @Override
+ public void flush() { /* .flush() ;*/ }
+
+ // Need to implement this one.
+ @Override
+ public void write(int b) { /* .write(b) ;*/ }
+ @Override
+ public void write(byte b[]) { /* this.write(b, 0, b.length) ; */}
+
+ // Good to implement this one.
+ @Override
+ public void write(byte[] b, int off, int len)
+ {
+ // Work function
+ }
+
+}