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 2015/01/14 11:31:46 UTC
[44/93] [abbrv] [partial] jena git commit: Maven modules for Fuseki2
http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/RequestLog.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/RequestLog.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/RequestLog.java
new file mode 100644
index 0000000..db79d6a
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/RequestLog.java
@@ -0,0 +1,148 @@
+/**
+ * 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.text.DateFormat ;
+import java.text.SimpleDateFormat ;
+import java.util.Collection ;
+import java.util.Date ;
+import java.util.Enumeration ;
+import java.util.TimeZone ;
+
+import javax.servlet.http.HttpServletRequest ;
+import javax.servlet.http.HttpServletResponse ;
+
+import org.apache.jena.atlas.logging.Log ;
+import org.apache.jena.fuseki.servlets.HttpAction ;
+
+/** Create standard request logs (NCSA etc) */
+public class RequestLog {
+ /*
+ http://httpd.apache.org/docs/current/mod/mod_log_config.html
+Common Log Format (CLF)
+ "%h %l %u %t \"%r\" %>s %b"
+Common Log Format with Virtual Host
+ "%v %h %l %u %t \"%r\" %>s %b"
+NCSA extended/combined log format
+ "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\""
+ */
+ /*
+ %l -- Identity or -
+ %u -- Remote user or -
+ %t -- Timestamp
+ "%r" -- Request line
+ %>s -- Final request status
+ %b -- Size in bytes
+ Headers.
+ %{}i for request header.
+ %{}o for response header.
+ */
+
+ private static DateFormat dateFormatter ;
+ static {
+ dateFormatter = new SimpleDateFormat("dd/MMM/yyyy:HH:mm:ss Z") ;
+ dateFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
+ }
+
+ /** NCSA combined log format *
+ * LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combinedfwd
+ * XXX.XXX.XXX.XXX - - [01/Feb/2014:03:19:09 +0000] "GET / HTTP/1.1" 200 6190 "-" "check_http/v1.4.16 (nagios-plugins 1.4.16)"
+ */
+ public static String combinedNCSA(HttpAction action) {
+ StringBuilder builder = new StringBuilder() ;
+ HttpServletRequest request = action.request ;
+ HttpServletResponse response = action.response ;
+
+ // Remote
+ String remote = get(request, "X-Forwarded-For", request.getRemoteAddr()) ;
+ builder.append(remote) ;
+ builder.append(" ") ;
+
+ // %l %u : User identity (unrelaible)
+ builder.append("- - ") ;
+
+ // %t
+ // Expensive?
+ builder.append("[");
+ // Better?
+ builder.append(dateFormatter.format(new Date())) ;
+ builder.append("] ");
+
+ // "%r"
+ builder.append("\"") ;
+ builder.append(request.getMethod()) ;
+ builder.append(" ") ;
+ // No query string - they are long and logged readably elsewhere
+ builder.append(request.getRequestURI()) ;
+ builder.append("\"") ;
+ //%>s -- Final request status
+ builder.append(" ") ;
+ builder.append(response.getStatus()) ;
+
+ //%b -- Size in bytes
+ builder.append(" ") ;
+ //String size = getField()
+ String size = get(response, "Content-Length", "-") ;
+ builder.append(size) ;
+
+ // "%{Referer}i"
+ builder.append(" \"") ;
+ builder.append(get(request, "Referer", "")) ;
+ builder.append("\"") ;
+ // "%{User-Agent}i"
+ builder.append(" \"") ;
+ builder.append(get(request, "User-Agent", "")) ;
+ builder.append("\"") ;
+
+ return builder.toString() ;
+ }
+
+ private static String get(HttpServletRequest request, String name, String dft) {
+ String x = get(request, name) ;
+ if ( x == null )
+ x = dft ;
+ return x ;
+ }
+
+ private static String get(HttpServletRequest request, String name) {
+ Enumeration<String> en = request.getHeaders(name) ;
+ if ( ! en.hasMoreElements() ) return null ;
+ String x = en.nextElement() ;
+ if ( en.hasMoreElements() ) {
+ Log.warn(RequestLog.class, "Multiple request header values") ;
+ }
+ return x ;
+ }
+
+ private static String get(HttpServletResponse response, String name, String dft) {
+ String x = get(response, name) ;
+ if ( x == null )
+ x = dft ;
+ return x ;
+ }
+
+
+ private static String get(HttpServletResponse response, String name) {
+ Collection<String> en = response.getHeaders(name) ;
+ if ( en.isEmpty() )return null ;
+ if ( en.size() != 1 ) Log.warn(RequestLog.class, "Multiple response header values") ;
+ return response.getHeader(name) ;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/ServerInitialConfig.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/ServerInitialConfig.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/ServerInitialConfig.java
new file mode 100644
index 0000000..67f5d26
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/ServerInitialConfig.java
@@ -0,0 +1,40 @@
+/**
+ * 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.HashMap ;
+import java.util.Map ;
+
+import com.hp.hpl.jena.sparql.core.DatasetGraph ;
+
+/** Dataset setup (command line, config file) for a dataset (or several if config file) */
+public class ServerInitialConfig {
+ // Either this ...
+ public String templateFile = null ;
+ public Map<String,String> params = new HashMap<>() ;
+ public String datasetPath = null ;
+ public boolean allowUpdate = false ;
+ // Or this ...
+ public String fusekiConfigFile = null ;
+
+ // Special case - directly pass in the dataset graphs - datasetPath must be given.
+ // This is not persistet across server restarts.
+ public DatasetGraph dsg = null ;
+
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/ServiceMXBean.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/ServiceMXBean.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/ServiceMXBean.java
new file mode 100644
index 0000000..11c7330
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/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/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/ShiroEnvironmentLoader.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/ShiroEnvironmentLoader.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/ShiroEnvironmentLoader.java
new file mode 100644
index 0000000..f33fe92
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/ShiroEnvironmentLoader.java
@@ -0,0 +1,164 @@
+/**
+ * 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.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.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 ;
+
+import com.hp.hpl.jena.util.FileUtils ;
+
+/** A place to perform Fuseki-specific initialization of Apache Shiro.
+ * Runs after listener FusekiServerEnvironmentInit and before 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) {
+ FusekiServer.init() ;
+ 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 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/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/SystemState.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/SystemState.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/SystemState.java
new file mode 100644
index 0000000..74117c9
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/SystemState.java
@@ -0,0 +1,108 @@
+/**
+ * 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.atlas.lib.FileOps ;
+import org.apache.jena.atlas.lib.StrUtils ;
+import org.apache.jena.fuseki.Fuseki ;
+
+import com.hp.hpl.jena.query.Dataset ;
+import com.hp.hpl.jena.tdb.StoreConnection ;
+import com.hp.hpl.jena.tdb.TDB ;
+import com.hp.hpl.jena.tdb.TDBFactory ;
+import com.hp.hpl.jena.tdb.base.block.FileMode ;
+import com.hp.hpl.jena.tdb.base.file.Location ;
+import com.hp.hpl.jena.tdb.setup.StoreParams ;
+import com.hp.hpl.jena.tdb.transaction.DatasetGraphTransaction ;
+
+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)
+ .blockReadCacheSize(20)
+ .blockWriteCacheSize(20)
+ .node2NodeIdCacheSize(5000)
+ .nodeId2NodeCacheSize(5000)
+ .nodeMissCacheSize(100)
+ .build() ;
+
+ public /* for testing */ static void init$() {
+ if ( initialized )
+ return ;
+ initialized = true ;
+
+ if ( location == null )
+ location = Location.create(FusekiServer.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) ;
+ }
+
+ public static String PREFIXES = StrUtils.strjoinNL
+ ("BASE <http://example/base#>",
+ "PREFIX ja: <http://jena.hpl.hp.com/2005/11/Assembler#>",
+ "PREFIX fu: <http://jena.apache.org/fuseki#>",
+ "PREFIX fuseki: <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 sdb: <http://jena.hpl.hp.com/20087/sdb#>",
+ "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#>",
+ "") ;
+}
+
http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionBase.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionBase.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionBase.java
new file mode 100644
index 0000000..3e45c78
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionBase.java
@@ -0,0 +1,265 @@
+/*
+ * 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 java.lang.String.format ;
+
+import java.io.IOException ;
+import java.util.Enumeration ;
+import java.util.Map ;
+
+import javax.servlet.ServletException ;
+import javax.servlet.http.HttpServletRequest ;
+import javax.servlet.http.HttpServletResponse ;
+
+import org.apache.jena.atlas.RuntimeIOException ;
+import org.apache.jena.fuseki.Fuseki ;
+import org.apache.jena.riot.web.HttpNames ;
+import org.apache.jena.web.HttpSC ;
+import org.slf4j.Logger ;
+
+import com.hp.hpl.jena.query.ARQ ;
+import com.hp.hpl.jena.query.QueryCancelledException ;
+import com.hp.hpl.jena.sparql.util.Context ;
+
+/** General request lifecycle */
+public abstract class ActionBase extends ServletBase
+{
+ protected final Logger log ;
+
+ protected ActionBase(Logger log) {
+ super() ;
+ this.log = log ;
+ }
+
+ @Override
+ public void init() {
+// log.info("["+Utils.className(this)+"] ServletContextName = "+getServletContext().getServletContextName()) ;
+// log.info("["+Utils.className(this)+"] ContextPath = "+getServletContext().getContextPath()) ;
+
+ //super.init() ;
+ }
+
+ /**
+ * Common framework for handling HTTP requests.
+ * @param request
+ * @param response
+ */
+ protected void doCommon(HttpServletRequest request, HttpServletResponse response)
+ {
+ try {
+ long id = allocRequestId(request, response);
+
+ // Lifecycle
+ HttpAction action = allocHttpAction(id, request, response) ;
+
+ printRequest(action) ;
+ action.setStartTime() ;
+
+ // The response may be changed to a HttpServletResponseTracker
+ response = action.response ;
+ initResponse(request, response) ;
+ Context cxt = ARQ.getContext() ;
+
+ try {
+ execCommonWorker(action) ;
+ } catch (QueryCancelledException ex) {
+ // Also need the per query info ...
+ String message = String.format("The query timed out (restricted to %s ms)", cxt.get(ARQ.queryTimeout));
+ // Possibility :: response.setHeader("Retry-after", "600") ; // 5 minutes
+ ServletOps.responseSendError(response, HttpSC.SERVICE_UNAVAILABLE_503, message);
+ } catch (ActionErrorException ex) {
+ if ( ex.exception != null )
+ ex.exception.printStackTrace(System.err) ;
+ // Log message done by printResponse in a moment.
+ if ( ex.message != null )
+ ServletOps.responseSendError(response, ex.rc, ex.message) ;
+ else
+ ServletOps.responseSendError(response, ex.rc) ;
+ } catch (RuntimeIOException ex) {
+ log.warn(format("[%d] Runtime IO Exception (client left?) RC = %d : %s", id, HttpSC.INTERNAL_SERVER_ERROR_500, ex.getMessage()), ex) ;
+ ServletOps.responseSendError(response, HttpSC.INTERNAL_SERVER_ERROR_500, ex.getMessage()) ;
+ } catch (Throwable ex) {
+ // This should not happen.
+ //ex.printStackTrace(System.err) ;
+ log.warn(format("[%d] RC = %d : %s", id, HttpSC.INTERNAL_SERVER_ERROR_500, ex.getMessage()), ex) ;
+ ServletOps.responseSendError(response, HttpSC.INTERNAL_SERVER_ERROR_500, ex.getMessage()) ;
+ }
+
+ action.setFinishTime() ;
+ printResponse(action) ;
+ archiveHttpAction(action) ;
+ } catch (Throwable th) {
+ log.error("Internal error", th) ;
+ }
+ }
+
+ // ---- Operation lifecycle
+
+ /**
+ * Returns a fresh HTTP Action for this request.
+ * @param id the Request ID
+ * @param request HTTP request
+ * @param response HTTP response
+ * @return a new HTTP Action
+ */
+ protected HttpAction allocHttpAction(long id, HttpServletRequest request, HttpServletResponse response) {
+ // Need a way to set verbose logging on a per servlet and per request basis.
+ return new HttpAction(id, log, request, response, Fuseki.verboseLogging) ;
+ }
+
+ /**
+ * Begin handling an {@link HttpAction}
+ * @param action
+ */
+ protected final void startRequest(HttpAction action) {
+ action.startRequest() ;
+ }
+
+ /**
+ * Stop handling an {@link HttpAction}
+ */
+ protected final void finishRequest(HttpAction action) {
+ action.finishRequest() ;
+ }
+
+ /**
+ * Archives the HTTP Action.
+ * @param action HTTP Action
+ * @see HttpAction#minimize()
+ */
+ private void archiveHttpAction(HttpAction action) {
+ action.minimize() ;
+ }
+
+ /**
+ * Execute this request, which maybe a admin operation or a client request.
+ * @param action HTTP Action
+ */
+ protected abstract void execCommonWorker(HttpAction action) ;
+
+ /** Extract the name after the container name (serverlet name).
+ * Returns "/name" or null
+ */
+ protected static String extractItemName(HttpAction action) {
+// action.log.info("context path = "+action.request.getContextPath()) ;
+// action.log.info("pathinfo = "+action.request.getPathInfo()) ;
+// action.log.info("servlet path = "+action.request.getServletPath()) ;
+ // if /name
+ // request.getServletPath() otherwise it's null
+ // if /*
+ // request.getPathInfo() ; otherwise it's null.
+
+ // PathInfo is after the servlet name.
+ String x1 = action.request.getServletPath() ;
+ String x2 = action.request.getPathInfo() ;
+
+ String pathInfo = action.request.getPathInfo() ;
+ if ( pathInfo == null || pathInfo.isEmpty() || pathInfo.equals("/") )
+ // Includes calling as a container.
+ return null ;
+ String name = pathInfo ;
+ // pathInfo starts with a "/"
+ int idx = pathInfo.lastIndexOf('/') ;
+ if ( idx > 0 )
+ name = name.substring(idx) ;
+ // Returns "/name"
+ return name ;
+ }
+
+ @SuppressWarnings("unused") // ServletException
+ protected void doPatch(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "HTTP PATCH not supported");
+ }
+
+ private void printRequest(HttpAction action) {
+ String url = ActionLib.wholeRequestURL(action.request) ;
+ String method = action.request.getMethod() ;
+
+ log.info(format("[%d] %s %s", action.id, method, url)) ;
+ if ( action.verbose ) {
+ Enumeration<String> en = action.request.getHeaderNames() ;
+ for (; en.hasMoreElements();) {
+ String h = en.nextElement() ;
+ Enumeration<String> vals = action.request.getHeaders(h) ;
+ if ( !vals.hasMoreElements() )
+ log.info(format("[%d] ", action.id, h)) ;
+ else {
+ for (; vals.hasMoreElements();)
+ log.info(format("[%d] %-20s %s", action.id, h, vals.nextElement())) ;
+ }
+ }
+ }
+ }
+
+ private void initResponse(HttpServletRequest request, HttpServletResponse response) {
+ setCommonHeaders(response) ;
+ String method = request.getMethod() ;
+ // All GET and HEAD operations are sensitive to conneg so ...
+ if ( HttpNames.METHOD_GET.equalsIgnoreCase(method) || HttpNames.METHOD_HEAD.equalsIgnoreCase(method) )
+ setVaryHeader(response) ;
+ }
+
+ private void printResponse(HttpAction action) {
+ long time = action.getTime() ;
+
+ HttpServletResponseTracker response = action.response ;
+ if ( action.verbose ) {
+ if ( action.contentType != null )
+ log.info(format("[%d] %-20s %s", action.id, HttpNames.hContentType, action.contentType)) ;
+ if ( action.contentLength != -1 )
+ log.info(format("[%d] %-20s %d", action.id, HttpNames.hContentLengh, action.contentLength)) ;
+ for (Map.Entry<String, String> e : action.headers.entrySet())
+ log.info(format("[%d] %-20s %s", action.id, e.getKey(), e.getValue())) ;
+ }
+
+ String timeStr = fmtMillis(time) ;
+
+ if ( action.message == null )
+ log.info(String.format("[%d] %d %s (%s) ", action.id, action.statusCode,
+ HttpSC.getMessage(action.statusCode), timeStr)) ;
+ else
+ log.info(String.format("[%d] %d %s (%s) ", action.id, action.statusCode, action.message, timeStr)) ;
+
+ // See also HttpAction.finishRequest - request logging happens there.
+ }
+
+ /**
+ * <p>Given a time point, return the time as a milli second string if it is less than 1000,
+ * otherwise return a seconds string.</p>
+ * <p>It appends a 'ms' suffix when using milli seconds,
+ * and <i>s</i> for seconds.</p>
+ * <p>For instance: </p>
+ * <ul>
+ * <li>10 emits 10 ms</li>
+ * <li>999 emits 999 ms</li>
+ * <li>1000 emits 1.000 s</li>
+ * <li>10000 emits 10.000 s</li>
+ * </ul>
+ * @param time the time in milliseconds
+ * @return the time as a display string
+ */
+
+ private static String fmtMillis(long time) {
+ // Millis only? seconds only?
+ if ( time < 1000 )
+ return String.format("%,d ms", time) ;
+ return String.format("%,.3f s", time / 1000.0) ;
+ }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionErrorException.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionErrorException.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionErrorException.java
new file mode 100644
index 0000000..c87cfe8
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/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;
+
+public class ActionErrorException extends RuntimeException
+{
+ public final Throwable exception ;
+ public final String message ;
+ public final int rc ;
+ public 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/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionLib.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionLib.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionLib.java
new file mode 100644
index 0000000..182b46e
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionLib.java
@@ -0,0 +1,180 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.servlets;
+
+import javax.servlet.http.HttpServletRequest ;
+
+import org.apache.jena.atlas.web.AcceptList ;
+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.DataAccessPoint ;
+import org.apache.jena.fuseki.server.DataAccessPointRegistry ;
+
+/** Operations related to servlets */
+
+public class ActionLib {
+ /**
+ * A possible implementation for {@link ActionSPARQL#mapRequestToDataset}
+ * that assumes the form /dataset/service.
+ * @param action the request
+ * @return the dataset
+ */ public static String mapRequestToDataset(HttpAction action) {
+ String uri = action.getActionURI() ;
+ return mapActionRequestToDataset(uri) ;
+ }
+
+ /** Map request to uri in the registry.
+ * A possible implementation for mapRequestToDataset(String)
+ * that assumes the form /dataset/service
+ * Returning null means no mapping found.
+ * The URI must be the action URI (no contact path)
+ */
+
+ public static String mapActionRequestToDataset(String uri) {
+ // Chop off trailing part - the service selector
+ // e.g. /dataset/sparql => /dataset
+ int i = uri.lastIndexOf('/') ;
+ if ( i == -1 )
+ return null ;
+ if ( i == 0 )
+ {
+ // started with '/' - leave.
+ return uri ;
+ }
+
+ return uri.substring(0, i) ;
+ }
+
+ /** Calculate the operation , given action and data access point */
+ public static String mapRequestToOperation(HttpAction action, DataAccessPoint dsRef) {
+ if ( dsRef == null )
+ return "" ;
+ String uri = action.getActionURI() ;
+ String name = dsRef.getName();
+ if ( name.length() >= uri.length() )
+ return "" ;
+ return uri.substring(name.length()+1) ; // Skip the separating "/"
+
+ }
+
+ /** Implementation of mapRequestToDataset(String) that looks for
+ * the longest match in the registry.
+ * This includes use in direct naming GSP.
+ */
+ public static String mapRequestToDatasetLongest$(String uri)
+ {
+ if ( uri == null )
+ return null ;
+
+ // This covers local, using the URI as a direct name for
+ // a graph, not just using the indirect ?graph= or ?default
+ // forms.
+
+ String ds = null ;
+ for ( String ds2 : DataAccessPointRegistry.get().keys() ) {
+ if ( ! uri.startsWith(ds2) )
+ continue ;
+
+ if ( ds == null )
+ {
+ ds = ds2 ;
+ continue ;
+ }
+ if ( ds.length() < ds2.length() )
+ {
+ ds = ds2 ;
+ continue ;
+ }
+ }
+ return ds ;
+ }
+
+ /** Calculate the fill URL including query string
+ * for the HTTP request. This may be quite long.
+ * @param request HttpServletRequest
+ * @return String The full URL, including query string.
+ */
+ public static String wholeRequestURL(HttpServletRequest request) {
+ StringBuffer sb = request.getRequestURL() ;
+ String queryString = request.getQueryString() ;
+ if ( queryString != null ) {
+ sb.append("?") ;
+ sb.append(queryString) ;
+ }
+ return sb.toString() ;
+ }
+
+ /*
+ * The context path can be:
+ * "" for the root context
+ * "/APP" for named contexts
+ * so:
+ * "/dataset/server" becomes "/dataset/server"
+ * "/APP/dataset/server" becomes "/dataset/server"
+ */
+ public static String removeContextPath(HttpAction action) {
+
+ return actionURI(action.request) ;
+ }
+
+ public static String actionURI(HttpServletRequest request) {
+// Log.info(this, "URI = '"+request.getRequestURI()) ;
+// Log.info(this, "Context path = '"+request.getContextPath()+"'") ;
+// Log.info(this, "Servlet path = '"+request.getServletPath()+"'") ;
+// ServletContext cxt = this.getServletContext() ;
+// Log.info(this, "ServletContext path = '"+cxt.getContextPath()+"'") ;
+
+ String contextPath = request.getServletContext().getContextPath() ;
+ String uri = request.getRequestURI() ;
+ if ( contextPath == null )
+ return uri ;
+ if ( contextPath.isEmpty())
+ return uri ;
+ String x = uri ;
+ if ( uri.startsWith(contextPath) )
+ x = uri.substring(contextPath.length()) ;
+ //log.info("uriWithoutContextPath: uri = "+uri+" contextPath="+contextPath+ "--> x="+x) ;
+ return x ;
+ }
+
+ /** Negotiate the content-type and set the response headers */
+ public static MediaType contentNegotation(HttpAction action, AcceptList myPrefs,
+ MediaType defaultMediaType) {
+ MediaType mt = ConNeg.chooseContentType(action.request, myPrefs, defaultMediaType) ;
+ 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 ;
+ }
+
+ /** Negotiate the content-type for an RDF triples syntax and set the response headers */
+ public static MediaType contentNegotationRDF(HttpAction action) {
+ return contentNegotation(action, DEF.rdfOffer, DEF.acceptRDFXML) ;
+ }
+
+ /** Negotiate the content-type for an RDF quads syntax and set the response headers */
+ public static MediaType contentNegotationQuads(HttpAction action) {
+ return contentNegotation(action, DEF.quadsOffer, DEF.acceptNQuads) ;
+ }
+}
+
http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionREST.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionREST.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionREST.java
new file mode 100644
index 0000000..e2c7e4a
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionREST.java
@@ -0,0 +1,161 @@
+/*
+ * 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 java.util.Locale ;
+
+import javax.servlet.ServletException ;
+import javax.servlet.http.HttpServletRequest ;
+import javax.servlet.http.HttpServletResponse ;
+
+import org.apache.jena.fuseki.server.CounterName ;
+
+/** Common point for operations that are "REST"ish (use GET/PUT etc as operations). */
+public abstract class ActionREST extends ActionSPARQL
+{
+ public ActionREST()
+ { super() ; }
+
+ @Override
+ protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ // Direct all verbs to our common framework.
+ doCommon(request, response) ;
+ }
+
+ @Override
+ protected void perform(HttpAction action) {
+ dispatch(action) ;
+ }
+
+ private void dispatch(HttpAction action) {
+ HttpServletRequest req = action.request ;
+ HttpServletResponse resp = action.response ;
+ String method = req.getMethod().toUpperCase(Locale.ROOT) ;
+
+ if (method.equals(METHOD_GET))
+ doGet$(action);
+ else if (method.equals(METHOD_HEAD))
+ doHead$(action);
+ else if (method.equals(METHOD_POST))
+ doPost$(action);
+ else if (method.equals(METHOD_PATCH))
+ doPatch$(action) ;
+ else if (method.equals(METHOD_OPTIONS))
+ doOptions$(action) ;
+ else if (method.equals(METHOD_TRACE))
+ //doTrace(action) ;
+ ServletOps.errorMethodNotAllowed("TRACE") ;
+ else if (method.equals(METHOD_PUT))
+ doPut$(action) ;
+ else if (method.equals(METHOD_DELETE))
+ doDelete$(action) ;
+ else
+ ServletOps.errorNotImplemented("Unknown method: "+method) ;
+ }
+
+ // Counter wrappers
+
+ // XXX Out of date - we now add HTTP counters to all endpoints.
+
+ private final void doGet$(HttpAction action) {
+ incCounter(action.getEndpoint(), CounterName.HTTPget) ;
+ try {
+ doGet(action) ;
+ incCounter(action.getEndpoint(), CounterName.HTTPgetGood) ;
+ } catch ( ActionErrorException ex) {
+ incCounter(action.getEndpoint(), CounterName.HTTPGetBad) ;
+ throw ex ;
+ }
+ }
+
+ private final void doHead$(HttpAction action) {
+ incCounter(action.getEndpoint(), CounterName.HTTPhead) ;
+ try {
+ doHead(action) ;
+ incCounter(action.getEndpoint(), CounterName.HTTPheadGood) ;
+ } catch ( ActionErrorException ex) {
+ incCounter(action.getEndpoint(), CounterName.HTTPheadBad) ;
+ throw ex ;
+ }
+ }
+
+ private final void doPost$(HttpAction action) {
+ incCounter(action.getEndpoint(), CounterName.HTTPpost) ;
+ try {
+ doPost(action) ;
+ incCounter(action.getEndpoint(), CounterName.HTTPpostGood) ;
+ } catch ( ActionErrorException ex) {
+ incCounter(action.getEndpoint(), CounterName.HTTPpostBad) ;
+ throw ex ;
+ }
+ }
+
+ private final void doPatch$(HttpAction action) {
+ incCounter(action.getEndpoint(), CounterName.HTTPpatch) ;
+ try {
+ doPatch(action) ;
+ incCounter(action.getEndpoint(), CounterName.HTTPpatchGood) ;
+ } catch ( ActionErrorException ex) {
+ incCounter(action.getEndpoint(), CounterName.HTTPpatchBad) ;
+ throw ex ;
+ }
+ }
+
+ private final void doDelete$(HttpAction action) {
+ incCounter(action.getEndpoint(), CounterName.HTTPdelete) ;
+ try {
+ doDelete(action) ;
+ incCounter(action.getEndpoint(), CounterName.HTTPdeleteGood) ;
+ } catch ( ActionErrorException ex) {
+ incCounter(action.getEndpoint(), CounterName.HTTPdeleteBad) ;
+ throw ex ;
+ }
+ }
+
+ private final void doPut$(HttpAction action) {
+ incCounter(action.getEndpoint(), CounterName.HTTPput) ;
+ try {
+ doPut(action) ;
+ incCounter(action.getEndpoint(), CounterName.HTTPputGood) ;
+ } catch ( ActionErrorException ex) {
+ incCounter(action.getEndpoint(), CounterName.HTTPputBad) ;
+ throw ex ;
+ }
+ }
+
+ private final void doOptions$(HttpAction action) {
+ incCounter(action.getEndpoint(), CounterName.HTTPoptions) ;
+ try {
+ doOptions(action) ;
+ incCounter(action.getEndpoint(), CounterName.HTTPoptionsGood) ;
+ } catch ( ActionErrorException ex) {
+ incCounter(action.getEndpoint(), CounterName.HTTPoptionsBad) ;
+ throw ex ;
+ }
+ }
+
+ protected abstract void doGet(HttpAction action) ;
+ protected abstract void doHead(HttpAction action) ;
+ protected abstract void doPost(HttpAction action) ;
+ protected abstract void doPatch(HttpAction action) ;
+ protected abstract void doDelete(HttpAction action) ;
+ protected abstract void doPut(HttpAction action) ;
+ protected abstract void doOptions(HttpAction action) ;
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionSPARQL.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionSPARQL.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionSPARQL.java
new file mode 100644
index 0000000..d26bc55
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionSPARQL.java
@@ -0,0 +1,207 @@
+/*
+ * 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 org.apache.jena.fuseki.server.CounterName.Requests ;
+import static org.apache.jena.fuseki.server.CounterName.RequestsBad ;
+import static org.apache.jena.fuseki.server.CounterName.RequestsGood ;
+
+import java.io.InputStream ;
+
+import org.apache.jena.atlas.RuntimeIOException ;
+import org.apache.jena.fuseki.Fuseki ;
+import org.apache.jena.fuseki.server.* ;
+import org.apache.jena.riot.Lang ;
+import org.apache.jena.riot.RDFDataMgr ;
+import org.apache.jena.riot.ReaderRIOT ;
+import org.apache.jena.riot.RiotException ;
+import org.apache.jena.riot.system.ErrorHandler ;
+import org.apache.jena.riot.system.ErrorHandlerFactory ;
+import org.apache.jena.riot.system.StreamRDF ;
+
+import com.hp.hpl.jena.query.QueryCancelledException ;
+
+/** SPARQL request lifecycle */
+public abstract class ActionSPARQL extends ActionBase
+{
+ protected ActionSPARQL() { super(Fuseki.actionLog) ; }
+
+ protected abstract void validate(HttpAction action) ;
+ protected abstract void perform(HttpAction action) ;
+
+ /**
+ * Executes common tasks, including mapping the request to the right dataset, setting the dataset into the HTTP
+ * action, and retrieving the service for the dataset requested. Finally, it calls the
+ * {@link #executeAction(HttpAction)} method, which executes the HTTP Action life cycle.
+ * @param action HTTP Action
+ */
+ @Override
+ protected void execCommonWorker(HttpAction action) {
+ DataAccessPoint dataAccessPoint ;
+ DataService dSrv ;
+
+ String datasetUri = mapRequestToDataset(action) ;
+ if ( datasetUri != null ) {
+ dataAccessPoint = DataAccessPointRegistry.get().get(datasetUri) ;
+ if ( dataAccessPoint == null ) {
+ ServletOps.errorNotFound("No dataset for URI: "+datasetUri) ;
+ return ;
+ }
+ //dataAccessPoint.
+ dSrv = dataAccessPoint.getDataService() ;
+ if ( ! dSrv.isAcceptingRequests() ) {
+ ServletOps.errorNotFound("Dataset not active: "+datasetUri) ;
+ return ;
+ }
+ } else {
+ dataAccessPoint = null ;
+ dSrv = DataService.serviceOnlyDataService() ;
+ }
+
+ String operationName = mapRequestToOperation(action, dataAccessPoint) ;
+ action.setRequest(dataAccessPoint, dSrv) ;
+
+ //operationName = ""
+
+ Endpoint op = dSrv.getOperation(operationName) ;
+ action.setEndpoint(op, operationName);
+ executeAction(action) ;
+ }
+
+ /** Execute a SPARQL request. Statistics have not been adjusted at this point.
+ *
+ * @param action
+ */
+ protected void executeAction(HttpAction action) {
+ executeLifecycle(action) ;
+ }
+
+ /**
+ * Standard execution lifecycle for a SPARQL Request.
+ * <ul>
+ * <li>{@link #startRequest(HttpAction)}</li>
+ * <li>initial statistics,</li>
+ * <li>{@link #validate(HttpAction)} request,</li>
+ * <li>{@link #perform(HttpAction)} request,</li>
+ * <li>completion/error statistics,</li>
+ * <li>{@link #finishRequest(HttpAction)}
+ * </ul>
+ *
+ * @param action
+ */
+ // This is the service request lifecycle.
+ final
+ protected void executeLifecycle(HttpAction action) {
+ startRequest(action) ;
+ // And also HTTP counter
+ CounterSet csService = action.getDataService().getCounters() ;
+ CounterSet csOperation = action.getEndpoint().getCounters() ;
+
+ incCounter(csService, Requests) ;
+ incCounter(csOperation, Requests) ;
+ try {
+ // Either exit this via "bad request" on validation
+ // or in execution in perform.
+ try {
+ validate(action) ;
+ } catch (ActionErrorException ex) {
+ incCounter(csOperation, RequestsBad) ;
+ incCounter(csService, RequestsBad) ;
+ throw ex ;
+ }
+
+ try {
+ perform(action) ;
+ // Success
+ incCounter(csOperation, RequestsGood) ;
+ incCounter(csService, RequestsGood) ;
+ } catch (ActionErrorException | QueryCancelledException | RuntimeIOException ex) {
+ incCounter(csOperation, RequestsBad) ;
+ incCounter(csService, RequestsBad) ;
+ throw ex ;
+ }
+ } finally {
+ finishRequest(action) ;
+ }
+ }
+
+ /**
+ * Map request {@link HttpAction} to uri in the registry.
+ * A return of ull means no mapping done (passthrough).
+ * @param uri the URI
+ * @return the dataset
+ */
+ protected String mapRequestToDataset(HttpAction action) {
+ return ActionLib.mapRequestToDataset(action) ;
+ }
+
+ /**
+ * Map request to uri in the registry. null means no mapping done
+ * (passthrough).
+ */
+ protected String mapRequestToOperation(HttpAction action, DataAccessPoint dataAccessPoint) {
+ return ActionLib.mapRequestToOperation(action, dataAccessPoint) ;
+ }
+
+ /** Increment counter */
+ protected static void incCounter(Counters counters, CounterName name) {
+ if ( counters == null ) return ;
+ incCounter(counters.getCounters(), name) ;
+ }
+
+ /** Decrement counter */
+ protected static void decCounter(Counters counters, CounterName name) {
+ if ( counters == null ) return ;
+ decCounter(counters.getCounters(), name) ;
+ }
+
+ protected static void incCounter(CounterSet counters, CounterName name) {
+ if ( counters == null )
+ return ;
+ try {
+ if ( counters.contains(name) )
+ counters.inc(name) ;
+ } catch (Exception ex) {
+ Fuseki.serverLog.warn("Exception on counter inc", ex) ;
+ }
+ }
+
+ protected static void decCounter(CounterSet counters, CounterName name) {
+ if ( counters == null )
+ return ;
+ try {
+ if ( counters.contains(name) )
+ counters.dec(name) ;
+ } catch (Exception ex) {
+ Fuseki.serverLog.warn("Exception on counter dec", ex) ;
+ }
+ }
+
+ public static void parse(HttpAction action, StreamRDF dest, InputStream input, Lang lang, String base) {
+ try {
+ ReaderRIOT r = RDFDataMgr.createReader(lang) ;
+ if ( r == null )
+ ServletOps.errorBadRequest("No parser for language '"+lang.getName()+"'") ;
+ ErrorHandler errorHandler = ErrorHandlerFactory.errorHandlerStd(action.log);
+ r.setErrorHandler(errorHandler);
+ r.read(input, base, null, dest, null) ;
+ }
+ catch (RiotException ex) { ServletOps.errorBadRequest("Parse error: "+ex.getMessage()) ; }
+ }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ConcurrencyPolicyMRSW.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ConcurrencyPolicyMRSW.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ConcurrencyPolicyMRSW.java
new file mode 100644
index 0000000..1f86539
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/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.actionLog ; //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/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/FusekiFilter.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/FusekiFilter.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/FusekiFilter.java
new file mode 100644
index 0000000..7a79cd3
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/FusekiFilter.java
@@ -0,0 +1,87 @@
+/**
+ * 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.* ;
+import javax.servlet.http.HttpServletRequest ;
+import javax.servlet.http.HttpServletResponse ;
+
+import org.apache.jena.fuseki.Fuseki ;
+import org.apache.jena.fuseki.server.DataAccessPointRegistry ;
+import org.slf4j.Logger ;
+
+/** Look at all requests and see if they match a registered dataset name;
+ * if they do, pass down to the uber servlet, which can dispatch any request
+ * for any service.
+ */
+public class FusekiFilter implements Filter {
+ private static Logger log = Fuseki.serverLog ;
+ private static SPARQL_UberServlet überServlet = new SPARQL_UberServlet.AccessByConfig() ;
+
+ @Override
+ public void init(FilterConfig filterConfig) throws ServletException {
+// log.info("Filter: ["+Utils.className(this)+"] ServletContextName = "+filterConfig.getServletContext().getServletContextName()) ;
+// log.info("Filter: ["+Utils.className(this)+"] ContextPath = "+filterConfig.getServletContext().getContextPath()) ;
+ }
+
+ private static final boolean LogFilter = false ; // Development debugging (can be excessive!)
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+ try {
+ HttpServletRequest req = (HttpServletRequest)request ;
+ HttpServletResponse resp = (HttpServletResponse)response ;
+
+ // Handle context path
+ String uri = ActionLib.actionURI(req) ;
+ String datasetUri = ActionLib.mapActionRequestToDataset(uri) ;
+
+ // is it a long running operation?
+ // (this could be a separate filter)
+
+ if ( LogFilter ) {
+ log.info("Filter: Request URI = "+req.getRequestURI()) ;
+ log.info("Filter: Action URI = "+uri) ;
+ log.info("Filter: Dataset URI = "+datasetUri) ;
+ }
+
+ if ( datasetUri != null ) {
+ if ( DataAccessPointRegistry.get().isRegistered(datasetUri) ) {
+ if ( LogFilter )
+ log.info("Filter: dispatch") ;
+ überServlet.doCommon(req, resp) ;
+ return ;
+ }
+ }
+ } catch (Exception ex) {}
+
+ if ( LogFilter )
+ log.info("Filter: pass to chain") ;
+ // Not found - continue.
+ chain.doFilter(request, response);
+ }
+
+ @Override
+ public void destroy() {}
+
+}
+
http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/HttpAction.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/HttpAction.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/HttpAction.java
new file mode 100644
index 0000000..245ed0f
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/HttpAction.java
@@ -0,0 +1,387 @@
+/*
+ * 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.fuseki.Fuseki ;
+import org.apache.jena.fuseki.FusekiException ;
+import org.apache.jena.fuseki.server.* ;
+import org.slf4j.Logger ;
+
+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 ActionSPARQL#executeAction(HttpAction)} method.
+ */
+public class HttpAction
+{
+ public final long id ;
+ public final boolean verbose ;
+ public final Logger log ;
+
+ // ----
+ // Worth subclassing? Given this is allocated in the general lifecycle
+ // it would mean there are downcasts to the specific type.
+
+ // -- Valid only for operational actions (e.g. SPARQL).
+
+ public String endpointName = null ; // Endpoint name srv was found under
+ public Endpoint endpoint = null ;
+ private Transactional transactional = null ;
+ private boolean isTransactional = false ;
+ private DatasetGraph activeDSG = null ; // Set when inside begin/end.
+ private ReadWrite activeMode = null ; // Set when inside begin/end.
+
+ // -- Valid only for administration actions.
+
+ // -- Shared items (but exact meaning may differ)
+ /** Handle to dataset+services being acted on (maybe null) */
+ private DataAccessPoint dataAccessPoint = null ;
+ private DataService dataService = null ;
+ private String datasetName = null ; // Dataset URI used (e.g. registry)
+ private DatasetGraph dsg = null ;
+
+ // ----
+
+ private boolean startTimeIsSet = false ;
+ private boolean finishTimeIsSet = false ;
+
+ private long startTime = -2 ;
+ private long finishTime = -2 ;
+
+ // Outcome.
+ public int statusCode = -1 ;
+ public String message = null ;
+ public int contentLength = -1 ;
+ public String contentType = null ;
+
+ // Cleared to archive:
+ public Map <String, String> headers = new HashMap<>() ;
+ public HttpServletRequest request;
+ public HttpServletResponseTracker response ;
+ private final String actionURI ;
+ private final String contextPath ;
+
+ /**
+ * Creates a new HTTP Action, using the HTTP request and response, and a given ID.
+ *
+ * @param id given ID
+ * @param log Logger for this action
+ * @param request HTTP request
+ * @param response HTTP response
+ * @param verbose verbose flag
+ */
+ public HttpAction(long id, Logger log, HttpServletRequest request, HttpServletResponse response, boolean verbose) {
+ this.id = id ;
+ this.log = log ;
+ 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 ;
+ this.contextPath = request.getServletContext().getContextPath() ;
+ this.actionURI = ActionLib.actionURI(request) ;
+ }
+
+ /** Initialization after action creation during lifecycle setup.
+ * <p>Sets the action dataset. Setting will replace any existing {@link DataAccessPoint} and {@link DataService},
+ * 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 dataAccessPoint {@link DataAccessPoint}
+ * @param dService {@link DataService}
+ * @see Transactional
+ */
+
+ public void setRequest(DataAccessPoint dataAccessPoint, DataService dService) {
+ this.dataAccessPoint = dataAccessPoint ;
+ if ( dataAccessPoint != null )
+ this.datasetName = dataAccessPoint.getName() ;
+
+ if ( this.dataService != null )
+ throw new FusekiException("Redefinition of DatasetRef in the request action") ;
+
+ this.dataService = dService ;
+ if ( dService == null || dService.getDataset() == null )
+ // Null does not happens for service requests, (it does for admin requests - call setControlRequest)
+ throw new FusekiException("Null DataService in the request action") ;
+
+ this.dsg = dService.getDataset() ;
+ 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 safesetControlRef
+ transactional = new DatasetGraphWithLock(dsg) ;
+ // No real abort.
+ isTransactional = false ;
+ }
+ }
+
+ public void setControlRequest(DataAccessPoint dataAccessPoint, String datasetUri) {
+ this.dataAccessPoint = dataAccessPoint ;
+ this.dataService = null ;
+ this.datasetName = datasetUri ;
+ }
+
+ /**
+ * 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 ;
+ }
+
+ /** This is the requestURI with the context path removed.
+ * It should be used internally for dispatch.
+ */
+ public String getActionURI() {
+ return actionURI ;
+ }
+
+ /** Get the context path.
+ */
+ public String getContextPath() {
+ return contextPath ;
+ }
+
+
+ /** Set the endpoint and endpoint name that this is an action for.
+ * @param srvRef {@link Endpoint}
+ * @param endpointName
+ */
+ public void setEndpoint(Endpoint srvRef, String endpointName) {
+ this.endpoint = srvRef ;
+ this.endpointName = endpointName ;
+ }
+
+ /** Get the endpoint for the action (may be null) . */
+ public Endpoint getEndpoint() {
+ return endpoint ;
+ }
+
+ /**
+ * Returns whether or not the underlying DatasetGraph is fully transactional (supports rollback)
+ */
+ public boolean isTransactional() {
+ return isTransactional ;
+ }
+
+ public void beginRead() {
+ activeMode = READ ;
+ transactional.begin(READ) ;
+ activeDSG = dsg ;
+ dataService.startTxn(READ) ;
+ }
+
+ public void endRead() {
+ dataService.finishTxn(READ) ;
+ activeMode = null ;
+ transactional.end() ;
+ activeDSG = null ;
+ }
+
+ public void beginWrite() {
+ transactional.begin(WRITE) ;
+ activeMode = WRITE ;
+ activeDSG = dsg ;
+ dataService.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() {
+ dataService.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 void startRequest()
+ {
+ if ( dataAccessPoint != null )
+ dataAccessPoint.startRequest(this) ;
+ }
+
+ public final void finishRequest() {
+ if ( dataAccessPoint != null )
+ dataAccessPoint.finishRequest(this) ;
+ // Standard logging goes here.
+ if ( Fuseki.requestLog != null && Fuseki.requestLog.isInfoEnabled() ) {
+ String s = RequestLog.combinedNCSA(this) ;
+ Fuseki.requestLog.info(s);
+ }
+ }
+
+ /** If inside the transaction for the action, return the active {@link DatasetGraph},
+ * otherwise return null.
+ * @return Current active {@link DatasetGraph}
+ */
+ public final DatasetGraph getActiveDSG() {
+ return activeDSG ;
+ }
+
+ public final DataAccessPoint getDataAccessPoint() {
+ return dataAccessPoint;
+ }
+
+// public void setDataAccessPoint(DataAccessPoint dataAccessPoint) {
+// this.dataAccessPoint = dataAccessPoint;
+// }
+
+ public final DataService getDataService() {
+ return dataService;
+ }
+
+// public final void setDataService(DataService dataService) {
+// this.dataService = dataService;
+// }
+
+ public final String getDatasetName() {
+ return datasetName;
+ }
+
+// public void setDatasetName(String datasetName) {
+// this.datasetName = datasetName;
+// }
+
+ /** Reduce to a size that can be kept around for sometime.
+ * Release resources like datasets that may be closed, reset etc.
+ */
+ public void minimize() {
+ this.request = null ;
+ this.response = null ;
+ this.dsg = null ;
+ this.dataService = null ;
+ this.activeDSG = null ;
+ this.endpoint = null ;
+ }
+
+ public void setStartTime() {
+ if ( startTimeIsSet )
+ Log.warn(this, "Start time reset") ;
+ startTimeIsSet = true ;
+ this.startTime = System.nanoTime() ;
+ }
+
+ /** Start time, in system nanos */
+ public long getStartTime() {
+ if ( ! startTimeIsSet )
+ Log.warn(this, "Start time is not set") ;
+ return startTime ;
+ }
+
+ /** Start time, in system nanos */
+ public long getFinishTime() {
+ if ( ! finishTimeIsSet )
+ Log.warn(this, "Finish time is not set") ;
+ return finishTime ;
+ }
+
+ 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.
+ * {@link #setStartTime} and {@link #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) ;
+ }
+}
http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/HttpServletResponseTracker.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/HttpServletResponseTracker.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/HttpServletResponseTracker.java
new file mode 100644
index 0000000..c39e728
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/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/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/NullOutputStream.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/NullOutputStream.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/NullOutputStream.java
new file mode 100644
index 0000000..63e6562
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/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
+ }
+
+}