You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by an...@apache.org on 2015/03/05 16:36:43 UTC

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

http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/ServiceMXBean.java
----------------------------------------------------------------------
diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/ServiceMXBean.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/ServiceMXBean.java
new file mode 100644
index 0000000..11c7330
--- /dev/null
+++ b/jena-fuseki1/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/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/ServiceRef.java
----------------------------------------------------------------------
diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/ServiceRef.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/server/ServiceRef.java
new file mode 100644
index 0000000..6236050
--- /dev/null
+++ b/jena-fuseki1/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/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/ActionErrorException.java
----------------------------------------------------------------------
diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/ActionErrorException.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/ActionErrorException.java
new file mode 100644
index 0000000..6c5ebe9
--- /dev/null
+++ b/jena-fuseki1/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/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/ConcurrencyPolicyMRSW.java
----------------------------------------------------------------------
diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/ConcurrencyPolicyMRSW.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/ConcurrencyPolicyMRSW.java
new file mode 100644
index 0000000..259453d
--- /dev/null
+++ b/jena-fuseki1/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/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/DumpServlet.java
----------------------------------------------------------------------
diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/DumpServlet.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/DumpServlet.java
new file mode 100644
index 0000000..7ece249
--- /dev/null
+++ b/jena-fuseki1/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("&", "&amp;") ;
+            x = x.replaceAll("<", "&lt;") ;
+            x = x.replaceAll(">", "&gt;") ;
+            pw.println(x) ;
+        }
+        try { in.reset() ;} catch (IOException e) { System.out.println("DumpServlet: Reset of content failed: "+e) ; }
+    }
+    
+    /**
+     * <code>dumpEnvironment</code>
+     * @return String that is the HTML of the System properties as name/value pairs.
+     * The values are with single quotes independent of whether or not the value has
+     * single quotes in it.
+     */
+    static public String dumpEnvironment()
+    {
+        Properties properties = System.getProperties();
+
+        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/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/HttpAction.java
----------------------------------------------------------------------
diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/HttpAction.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/HttpAction.java
new file mode 100644
index 0000000..39663dd
--- /dev/null
+++ b/jena-fuseki1/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/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/HttpServletResponseTracker.java
----------------------------------------------------------------------
diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/HttpServletResponseTracker.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/HttpServletResponseTracker.java
new file mode 100644
index 0000000..c39e728
--- /dev/null
+++ b/jena-fuseki1/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/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/NullOutputStream.java
----------------------------------------------------------------------
diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/NullOutputStream.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/NullOutputStream.java
new file mode 100644
index 0000000..63e6562
--- /dev/null
+++ b/jena-fuseki1/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
+	}
+	
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads.java
----------------------------------------------------------------------
diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads.java
new file mode 100644
index 0000000..06c38b7
--- /dev/null
+++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads.java
@@ -0,0 +1,211 @@
+/**
+ * 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 javax.servlet.ServletOutputStream ;
+
+import org.apache.jena.atlas.web.MediaType ;
+import org.apache.jena.atlas.web.TypedOutputStream ;
+import org.apache.jena.fuseki.Fuseki ;
+import org.apache.jena.fuseki.HttpNames ;
+import org.apache.jena.riot.Lang ;
+import org.apache.jena.riot.RDFDataMgr ;
+import org.apache.jena.riot.RDFLanguages ;
+import org.apache.jena.riot.ReaderRIOT ;
+import org.apache.jena.riot.system.StreamRDF ;
+import org.apache.jena.riot.system.StreamRDFLib ;
+
+import com.hp.hpl.jena.graph.Graph ;
+import com.hp.hpl.jena.graph.Node ;
+import com.hp.hpl.jena.graph.NodeFactory ;
+import com.hp.hpl.jena.sparql.core.DatasetGraph ;
+
+/** 
+ * Servlet that serves up quads for a dataset.
+ */
+
+public class REST_Quads extends SPARQL_REST
+{
+    public REST_Quads()     { super(); }
+    
+    @Override
+    protected void validate(HttpAction action)
+    {
+        // already checked?
+    }
+    
+    @Override
+    protected void doGet(HttpAction action)
+    {
+        MediaType mediaType = HttpAction.contentNegotationQuads(action) ;
+        ServletOutputStream output ;
+        try { output = action.response.getOutputStream() ; }
+        catch (IOException ex) { errorOccurred(ex) ; output = null ; }
+        
+        TypedOutputStream out = new TypedOutputStream(output, mediaType) ;
+        Lang lang = RDFLanguages.contentTypeToLang(mediaType.getContentType()) ;
+        if ( lang == null )
+            lang = RDFLanguages.TRIG ;
+
+        if ( action.verbose )
+            log.info(format("[%d]   Get: Content-Type=%s, Charset=%s => %s", 
+                                  action.id, mediaType.getContentType(), mediaType.getCharset(), lang.getName())) ;
+        if ( ! RDFLanguages.isQuads(lang) )
+            errorBadRequest("Not a quads format: "+mediaType) ;
+        
+        action.beginRead() ;
+        try {
+            DatasetGraph dsg = action.getActiveDSG() ;
+            RDFDataMgr.write(out, dsg, lang) ;
+            success(action) ;
+        } finally { action.endRead() ; }
+    }
+    
+    @Override
+    protected void doOptions(HttpAction action)
+    {
+        action.response.setHeader(HttpNames.hAllow, "GET, HEAD, OPTIONS") ;
+        action.response.setHeader(HttpNames.hContentLengh, "0") ;
+        success(action) ;
+    }
+
+    @Override
+    protected void doHead(HttpAction action)
+    {
+        action.beginRead() ;
+        try { 
+            MediaType mediaType = HttpAction.contentNegotationQuads(action) ;
+            success(action) ;
+        } finally { action.endRead() ; }
+    }
+
+    static int counter = 0 ;
+    @Override
+    protected void doPost(HttpAction action)
+    { 
+        if ( ! action.getDatasetRef().allowDatasetUpdate )
+            errorMethodNotAllowed("POST") ;
+
+        // Graph Store Protocol mode - POST triples to dataset causes
+        // a new graph to be created and the new URI returned via Location.
+        // Normally off.  
+        // When off, POST of triples goes to default graph.
+        boolean gspMode = Fuseki.graphStoreProtocolPostCreate ;
+        
+        // Code to pass the GSP test suite.
+        // Not necessarily good code.
+        String x = action.request.getContentType() ;
+        if ( x == null )
+            errorBadRequest("Content-type required for data format") ;
+        
+        MediaType mediaType = MediaType.create(x) ;
+        Lang lang = RDFLanguages.contentTypeToLang(mediaType.getContentType()) ;
+        if ( lang == null )
+            lang = RDFLanguages.TRIG ;
+
+        if ( action.verbose )
+            log.info(format("[%d]   Post: Content-Type=%s, Charset=%s => %s", 
+                                  action.id, mediaType.getContentType(), mediaType.getCharset(), lang.getName())) ;
+        
+        if ( RDFLanguages.isQuads(lang) )
+            doPostQuads(action, lang) ;
+        else if ( gspMode && RDFLanguages.isTriples(lang) )
+            doPostTriplesGSP(action, lang) ;
+        else if ( RDFLanguages.isTriples(lang) )
+            doPostTriples(action, lang) ;
+        else
+            errorBadRequest("Not a triples or quads format: "+mediaType) ;
+    }
+        
+    protected void doPostQuads(HttpAction action, Lang lang)
+    {
+        action.beginWrite() ;
+        try {
+            String name = action.request.getRequestURL().toString() ;
+            DatasetGraph dsg = action.getActiveDSG() ;
+            StreamRDF dest = StreamRDFLib.dataset(dsg) ;
+            ReaderRIOT reader = RDFDataMgr.createReader(lang) ;
+            reader.read(action.request.getInputStream(), name, null, dest, null);
+            action.commit();
+            success(action) ;
+        } catch (IOException ex) { action.abort() ; } 
+        finally { action.endWrite() ; }
+    }
+    
+  
+    // POST triples to dataset -- send to default graph.  
+    protected void doPostTriples(HttpAction action, Lang lang) 
+    {
+        action.beginWrite() ;
+        try {
+            DatasetGraph dsg = action.getActiveDSG() ;
+            // This should not be anythign other than the datasets name via this route.  
+            String name = action.request.getRequestURL().toString() ;
+            //log.info(format("[%d] ** Content-length: %d", action.id, action.request.getContentLength())) ;  
+            Graph g = dsg.getDefaultGraph() ;
+            StreamRDF dest = StreamRDFLib.graph(g) ;
+            ReaderRIOT reader = RDFDataMgr.createReader(lang) ;
+            reader.read(action.request.getInputStream(), name, null, dest, null);
+            action.commit();
+            success(action) ;
+        } catch (IOException ex) { action.abort() ; } 
+        finally { action.endWrite() ; }
+    }
+    
+    protected void doPostTriplesGSP(HttpAction action, Lang lang) 
+    {
+        action.beginWrite() ;
+        try {
+            DatasetGraph dsg = action.getActiveDSG() ;
+            //log.info(format("[%d] ** Content-length: %d", action.id, action.request.getContentLength())) ;  
+            
+            String name = action.request.getRequestURL().toString() ;
+            if ( ! name.endsWith("/") )
+                name = name+ "/"  ;
+            name = name+(++counter) ;
+            Node gn = NodeFactory.createURI(name) ;
+            Graph g = dsg.getGraph(gn) ;
+            StreamRDF dest = StreamRDFLib.graph(g) ;
+            ReaderRIOT reader = RDFDataMgr.createReader(lang) ;
+            reader.read(action.request.getInputStream(), name, null, dest, null);
+            log.info(format("[%d] Location: %s", action.id, name)) ;
+            action.response.setHeader("Location",  name) ;
+            action.commit();
+            successCreated(action) ;
+        } catch (IOException ex) { action.abort() ; } 
+        finally { action.endWrite() ; }
+    }
+
+    @Override
+    protected void doDelete(HttpAction action)
+    { errorMethodNotAllowed("DELETE") ; }
+
+    @Override
+    protected void doPut(HttpAction action)
+    { errorMethodNotAllowed("PUT") ; }
+
+    @Override
+    protected void doPatch(HttpAction action)
+    { errorMethodNotAllowed("PATCH") ; }
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/ResponseCallback.java
----------------------------------------------------------------------
diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/ResponseCallback.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/ResponseCallback.java
new file mode 100644
index 0000000..1a78627
--- /dev/null
+++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/ResponseCallback.java
@@ -0,0 +1,24 @@
+/*
+ * 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 interface ResponseCallback
+{
+    public void callback(boolean successfulOperation) ;
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/ResponseModel.java
----------------------------------------------------------------------
diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/ResponseModel.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/ResponseModel.java
new file mode 100644
index 0000000..f2172f0
--- /dev/null
+++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/ResponseModel.java
@@ -0,0 +1,143 @@
+/*
+ * 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.servlets.ServletBase.error ;
+import static org.apache.jena.fuseki.servlets.ServletBase.errorBadRequest ;
+import static org.apache.jena.fuseki.servlets.ServletBase.errorOccurred ;
+
+import java.util.HashMap ;
+import java.util.Map ;
+
+import javax.servlet.ServletOutputStream ;
+import javax.servlet.http.HttpServletRequest ;
+import javax.servlet.http.HttpServletResponse ;
+
+import org.apache.jena.atlas.web.MediaType ;
+import org.apache.jena.fuseki.DEF ;
+import org.apache.jena.fuseki.Fuseki ;
+import org.apache.jena.fuseki.conneg.ConNeg ;
+import org.apache.jena.fuseki.conneg.WebLib ;
+import org.apache.jena.riot.Lang ;
+import org.apache.jena.riot.RDFDataMgr ;
+import org.apache.jena.riot.RDFLanguages ;
+import org.apache.jena.riot.WebContent ;
+import org.apache.jena.web.HttpSC ;
+import org.slf4j.Logger ;
+
+import com.hp.hpl.jena.rdf.model.Model ;
+
+public class ResponseModel
+{
+    private static Logger slog = ServletBase.log ;
+
+    // Short names for "output="
+    private static final String contentOutputJSONLD        = "json-ld" ;
+    private static final String contentOutputJSONRDF       = "json-rdf" ;
+    private static final String contentOutputJSON          = "json" ;
+    private static final String contentOutputXML           = "xml" ;
+    private static final String contentOutputText          = "text" ;
+    private static final String contentOutputTTL           = "ttl" ;
+    private static final String contentOutputNT            = "nt" ;
+
+    public static Map<String,String> shortNamesModel = new HashMap<String, String>() ;
+    static {
+
+        // Some short names.  keys are lowercase.
+        ResponseOps.put(shortNamesModel, contentOutputJSONLD, WebContent.contentTypeJSONLD) ;
+        ResponseOps.put(shortNamesModel, contentOutputJSONRDF, WebContent.contentTypeRDFJSON) ;
+        ResponseOps.put(shortNamesModel, contentOutputJSON, WebContent.contentTypeJSONLD) ;
+        ResponseOps.put(shortNamesModel, contentOutputXML,  WebContent.contentTypeRDFXML) ;
+        ResponseOps.put(shortNamesModel, contentOutputText, WebContent.contentTypeTurtle) ;
+        ResponseOps.put(shortNamesModel, contentOutputTTL,  WebContent.contentTypeTurtle) ;
+        ResponseOps.put(shortNamesModel, contentOutputNT,   WebContent.contentTypeNTriples) ;
+    }
+
+    public static void doResponseModel(HttpAction action, Model model) 
+    {
+        HttpServletRequest request = action.request ;
+        HttpServletResponse response = action.response ;
+        
+        String mimeType = null ;        // Header request type 
+
+        // TODO Use MediaType throughout.
+        MediaType i = ConNeg.chooseContentType(request, DEF.rdfOffer, DEF.acceptRDFXML) ;
+        if ( i != null )
+            mimeType = i.getContentType() ;
+
+        String outputField = ResponseOps.paramOutput(request, shortNamesModel) ;
+        if ( outputField != null )
+            mimeType = outputField ;
+
+        String writerMimeType = mimeType ;
+
+        if ( mimeType == null )
+        {
+            Fuseki.requestLog.warn("Can't find MIME type for response") ;
+            String x = WebLib.getAccept(request) ;
+            String msg ;
+            if ( x == null )
+                msg = "No Accept: header" ;
+            else
+                msg = "Accept: "+x+" : Not understood" ;
+            error(HttpSC.NOT_ACCEPTABLE_406, msg) ;
+        }
+
+        String contentType = mimeType ;
+        String charset =     WebContent.charsetUTF8 ;
+
+        String forceAccept = ResponseOps.paramForceAccept(request) ;
+        if ( forceAccept != null )
+        {
+            contentType = forceAccept ;
+            charset = WebContent.charsetUTF8 ;
+        }
+
+        Lang lang = RDFLanguages.contentTypeToLang(contentType) ;
+        if ( lang == null )
+            errorBadRequest("Can't determine output content type: "+contentType) ;
+        
+//        if ( rdfw instanceof RDFXMLWriterI )
+//            rdfw.setProperty("showXmlDeclaration", "true") ;
+
+    //        // Write locally to check it's possible.
+    //        // Time/space tradeoff.
+    //        try {
+    //            OutputStream out = new NullOutputStream() ;
+    //            RDFDataMgr.write(out, model, lang) ;
+    //            IO.flush(out) ;
+    //        } catch (JenaException ex)
+    //        {
+    //            SPARQL_ServletBase.errorOccurred(ex) ;
+    //        }
+
+        try {
+            ResponseResultSet.setHttpResponse(request, response, contentType, charset) ; 
+            response.setStatus(HttpSC.OK_200) ;
+            ServletOutputStream out = response.getOutputStream() ;
+            RDFDataMgr.write(out, model, lang) ;
+            out.flush() ;
+        }
+        catch (Exception ex) { 
+            slog.info("Exception while writing the response model: "+ex.getMessage(), ex) ;
+            errorOccurred("Exception while writing the response model: "+ex.getMessage(), ex) ;
+        }
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/ResponseOps.java
----------------------------------------------------------------------
diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/ResponseOps.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/ResponseOps.java
new file mode 100644
index 0000000..62ad6d5
--- /dev/null
+++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/ResponseOps.java
@@ -0,0 +1,94 @@
+/*
+ * 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 java.util.Map ;
+
+import javax.servlet.http.HttpServletRequest ;
+
+import org.apache.jena.fuseki.HttpNames ;
+
+public class ResponseOps
+{
+    // Helpers
+    public static void put(Map<String, String> map, String key, String value)
+    {
+        map.put(key.toLowerCase(Locale.ROOT), value) ;
+    }
+    
+    public static boolean isEOFexception(IOException ioEx)
+    {
+        if ( ioEx.getClass().getName().equals("org.mortbay.jetty.EofException eofEx") )
+            return true ;
+        if ( ioEx instanceof java.io.EOFException )
+            return true ;
+        return false ;
+    }
+
+    public static String paramForceAccept(HttpServletRequest request)
+    {
+        String x = fetchParam(request, HttpNames.paramForceAccept) ;
+        return x ; 
+    }
+
+    public static String paramStylesheet(HttpServletRequest request)
+    { return fetchParam(request, HttpNames.paramStyleSheet) ; }
+
+    public static String paramOutput(HttpServletRequest request, Map<String,String> map)
+    {
+        // Two names.
+        String x = fetchParam(request, HttpNames.paramOutput1) ;
+        if ( x == null )
+            x = fetchParam(request, HttpNames.paramOutput2) ;
+        return expandShortName(x, map) ;
+    }
+
+    public static String expandShortName(String str, Map<String,String> map)
+    {
+        if ( str == null )
+            return null ;
+        // Force keys to lower case. See put() above.
+        String key = str.toLowerCase(Locale.ROOT) ;
+        String str2 = map.get(key) ;
+        if ( str2 == null )
+            return str ;
+        return str2 ;
+    }
+
+    public static String paramCallback(HttpServletRequest request)
+    { 
+        return fetchParam(request, HttpNames.paramCallback) ;
+    }
+
+    public static String fetchParam(HttpServletRequest request, String parameterName)
+    {
+        String value = request.getParameter(parameterName) ;
+        if ( value != null )
+        {
+            value = value.trim() ;
+            if ( value.length() == 0 )
+                value = null ;
+        }
+        return value ;
+    }
+
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/ResponseResultSet.java
----------------------------------------------------------------------
diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/ResponseResultSet.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/ResponseResultSet.java
new file mode 100644
index 0000000..c42378b
--- /dev/null
+++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/ResponseResultSet.java
@@ -0,0 +1,320 @@
+/*
+ * 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 static org.apache.jena.atlas.lib.Lib.equal ;
+import static org.apache.jena.fuseki.servlets.ServletBase.errorBadRequest ;
+import static org.apache.jena.fuseki.servlets.ServletBase.errorOccurred ;
+import static org.apache.jena.fuseki.servlets.ServletBase.log ;
+
+import java.io.IOException ;
+import java.util.HashMap ;
+import java.util.Map ;
+
+import javax.servlet.ServletOutputStream ;
+import javax.servlet.http.HttpServletRequest ;
+import javax.servlet.http.HttpServletResponse ;
+
+import org.apache.commons.lang.StringUtils ;
+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.FusekiException ;
+import org.apache.jena.fuseki.conneg.ConNeg ;
+import org.apache.jena.riot.ResultSetMgr ;
+import org.apache.jena.riot.WebContent ;
+import org.apache.jena.riot.resultset.ResultSetLang ;
+import org.apache.jena.web.HttpSC ;
+import org.slf4j.Logger ;
+import org.slf4j.LoggerFactory ;
+
+import com.hp.hpl.jena.query.QueryCancelledException ;
+import com.hp.hpl.jena.query.ResultSet ;
+import com.hp.hpl.jena.query.ResultSetFormatter ;
+import com.hp.hpl.jena.sparql.core.Prologue ;
+
+/** This is the content negotiation for each kind of SPARQL query result */ 
+public class ResponseResultSet
+{
+    private static Logger xlog = LoggerFactory.getLogger(ResponseResultSet.class) ;
+    private static Logger slog = ServletBase.log ;
+
+    // Short names for "output="
+    private static final String contentOutputJSON          = "json" ;
+    private static final String contentOutputXML           = "xml" ;
+    private static final String contentOutputSPARQL        = "sparql" ;
+    private static final String contentOutputText          = "text" ;
+    private static final String contentOutputCSV           = "csv" ;
+    private static final String contentOutputTSV           = "tsv" ;
+    private static final String contentOutputThrift        = "thrift" ;
+    
+    public static Map<String,String> shortNamesResultSet = new HashMap<String, String>() ;
+    static {
+        // Some short names.  keys are lowercase.
+        ResponseOps.put(shortNamesResultSet, contentOutputJSON,   WebContent.contentTypeResultsJSON) ;
+        ResponseOps.put(shortNamesResultSet, contentOutputSPARQL, WebContent.contentTypeResultsXML) ;
+        ResponseOps.put(shortNamesResultSet, contentOutputXML,    WebContent.contentTypeResultsXML) ;
+        ResponseOps.put(shortNamesResultSet, contentOutputText,   WebContent.contentTypeTextPlain) ;
+        ResponseOps.put(shortNamesResultSet, contentOutputCSV,    WebContent.contentTypeTextCSV) ;
+        ResponseOps.put(shortNamesResultSet, contentOutputTSV,    WebContent.contentTypeTextTSV) ;
+        ResponseOps.put(shortNamesResultSet, contentOutputThrift, WebContent.contentTypeResultsThrift) ;
+    }
+    
+    interface OutputContent { void output(ServletOutputStream out) ; }
+
+    public static void doResponseResultSet(HttpAction action, Boolean booleanResult)
+    {
+        doResponseResultSet$(action, null, booleanResult, null, DEF.rsOfferTable) ;
+    }
+
+    public static void doResponseResultSet(HttpAction action, ResultSet resultSet, Prologue qPrologue)
+    {
+        doResponseResultSet$(action, resultSet, null, qPrologue, DEF.rsOfferTable) ;
+    }
+    
+    // If we refactor the conneg into a single function, we can split boolean and result set handling. 
+    
+    // One or the other argument must be null
+    private static void doResponseResultSet$(HttpAction action,
+                                             ResultSet resultSet, Boolean booleanResult, 
+                                             Prologue qPrologue, 
+                                             AcceptList contentTypeOffer) 
+    {
+        HttpServletRequest request = action.request ;
+        HttpServletResponse response = action.response ;
+        long id = action.id ;
+        
+        if ( resultSet == null && booleanResult == null )
+        {
+            xlog.warn("doResponseResult: Both result set and boolean result are null") ; 
+            throw new FusekiException("Both result set and boolean result are null") ;
+        }
+        
+        if ( resultSet != null && booleanResult != null )
+        {
+            xlog.warn("doResponseResult: Both result set and boolean result are set") ; 
+            throw new FusekiException("Both result set and boolean result are set") ;
+        }
+
+        String mimeType = null ; 
+        MediaType i = ConNeg.chooseContentType(request, contentTypeOffer, DEF.acceptRSXML) ;
+        if ( i != null )
+            mimeType = i.getContentType() ;
+        
+        // Override content type
+        // Does &output= override?
+        // Requested output type by the web form or &output= in the request.
+        String outputField = ResponseOps.paramOutput(request, shortNamesResultSet) ;    // Expands short names
+        if ( outputField != null )
+            mimeType = outputField ;
+        
+        String serializationType = mimeType ;           // Choose the serializer based on this.
+        String contentType = mimeType ;                 // Set the HTTP respose header to this.
+             
+        // Stylesheet - change to application/xml.
+        final String stylesheetURL = ResponseOps.paramStylesheet(request) ;
+        if ( stylesheetURL != null && equal(serializationType,WebContent.contentTypeResultsXML) )
+            contentType = WebContent.contentTypeXML ;
+        
+        // Force to text/plain?
+        String forceAccept = ResponseOps.paramForceAccept(request) ;
+        if ( forceAccept != null )
+            contentType = WebContent.contentTypeTextPlain ;
+
+        // Better : dispatch on MediaType
+        // Fuseki2 uses the SPARQL parser/write registry.
+        if ( equal(serializationType, WebContent.contentTypeResultsXML) )
+            sparqlXMLOutput(action, contentType, resultSet, stylesheetURL, booleanResult) ;
+        else if ( equal(serializationType, WebContent.contentTypeResultsJSON) )
+            jsonOutput(action, contentType, resultSet, booleanResult) ;
+        else if ( equal(serializationType, WebContent.contentTypeTextPlain) )
+            textOutput(action, contentType, resultSet, qPrologue, booleanResult) ;
+        else if ( equal(serializationType, WebContent.contentTypeTextCSV) ) 
+            csvOutput(action, contentType, resultSet, booleanResult) ;
+        else if (equal(serializationType, WebContent.contentTypeTextTSV) )
+            tsvOutput(action, contentType, resultSet, booleanResult) ;
+        else if (equal(serializationType, WebContent.contentTypeResultsThrift) )
+            thriftOutput(action, contentType, resultSet, booleanResult) ;
+        else
+            errorBadRequest("Can't determine output serialization: "+serializationType) ;
+    }
+    
+    
+    public static void setHttpResponse(HttpServletRequest httpRequest,
+                                       HttpServletResponse httpResponse,
+                                       String contentType, String charset) 
+    {
+        // ---- Set up HTTP Response
+        // Stop caching (not that ?queryString URLs are cached anyway)
+        if ( true )
+        {
+            httpResponse.setHeader("Cache-Control", "no-cache") ;
+            httpResponse.setHeader("Pragma", "no-cache") ;
+        }
+        // See: http://www.w3.org/International/O-HTTP-charset.html
+        if ( contentType != null )
+        {
+            if ( charset != null && ! isXML(contentType) )
+                contentType = contentType+"; charset="+charset ;
+            log.trace("Content-Type for response: "+contentType) ;
+            httpResponse.setContentType(contentType) ;
+        }
+    }
+
+    private static boolean isXML(String contentType)
+    {
+        return contentType.equals(WebContent.contentTypeRDFXML)
+            || contentType.equals(WebContent.contentTypeResultsXML)
+            || contentType.equals(WebContent.contentTypeXML) ; 
+    }
+
+    private static void sparqlXMLOutput(HttpAction action, String contentType, final ResultSet resultSet, final String stylesheetURL, final Boolean booleanResult)
+    {
+        OutputContent proc = 
+            new OutputContent(){
+            @Override
+            public void output(ServletOutputStream out)
+            {
+                if ( resultSet != null )
+                    ResultSetFormatter.outputAsXML(out, resultSet, stylesheetURL) ;
+                if ( booleanResult != null )
+                    ResultSetFormatter.outputAsXML(out, booleanResult, stylesheetURL) ;
+            }} ;
+            output(action, contentType, null, proc) ;
+        }
+    
+    private static void jsonOutput(HttpAction action, String contentType, final ResultSet resultSet, final Boolean booleanResult)
+    {
+        OutputContent proc = new OutputContent(){
+            @Override
+            public void output(ServletOutputStream out)
+            {
+                if ( resultSet != null )
+                    ResultSetFormatter.outputAsJSON(out, resultSet) ;
+                if (  booleanResult != null )
+                    ResultSetFormatter.outputAsJSON(out, booleanResult ) ;
+            }
+        } ;
+        
+        try {
+            String callback = ResponseOps.paramCallback(action.request) ;
+            ServletOutputStream out = action.response.getOutputStream() ;
+
+            if ( callback != null )
+            {
+                callback = StringUtils.replaceChars(callback, "\r", "") ;
+                callback = StringUtils.replaceChars(callback, "\n", "") ;
+                out.print(callback) ;
+                out.println("(") ;
+            }
+
+            output(action, contentType, WebContent.charsetUTF8, proc) ;
+
+            if ( callback != null )
+                out.println(")") ;
+        } catch (IOException ex) { errorOccurred(ex) ; }
+    }
+    
+    private static void textOutput(HttpAction action, String contentType, final ResultSet resultSet, final Prologue qPrologue, final Boolean booleanResult)
+    {
+        // Text is not streaming.
+        OutputContent proc =  new OutputContent(){
+            @Override
+            public void output(ServletOutputStream out)
+            {
+                if ( resultSet != null )
+                    ResultSetFormatter.out(out, resultSet, qPrologue) ;
+                if (  booleanResult != null )
+                    ResultSetFormatter.out(out, booleanResult ) ;
+            }
+        };
+
+        output(action, contentType, WebContent.charsetUTF8, proc) ;
+    }
+
+    private static void csvOutput(HttpAction action, String contentType, final ResultSet resultSet, final Boolean booleanResult) {
+        OutputContent proc = new OutputContent(){
+            @Override
+            public void output(ServletOutputStream out)
+            {
+                if ( resultSet != null )
+                    ResultSetFormatter.outputAsCSV(out, resultSet) ;
+                if (  booleanResult != null )
+                    ResultSetFormatter.outputAsCSV(out, booleanResult ) ;
+            }
+        } ;
+        output(action, contentType, WebContent.charsetUTF8, proc) ; 
+    }
+
+    private static void tsvOutput(HttpAction action, String contentType, final ResultSet resultSet, final Boolean booleanResult) {
+        OutputContent proc = new OutputContent(){
+            @Override
+            public void output(ServletOutputStream out)
+            {
+                if ( resultSet != null )
+                    ResultSetFormatter.outputAsTSV(out, resultSet) ;
+                if (  booleanResult != null )
+                    ResultSetFormatter.outputAsTSV(out, booleanResult ) ;
+            }
+        } ;
+        output(action, contentType, WebContent.charsetUTF8, proc) ; 
+    }
+
+    private static void thriftOutput(HttpAction action, String contentType, final ResultSet resultSet, final Boolean booleanResult) {
+        OutputContent proc = new OutputContent(){
+            @Override
+            public void output(ServletOutputStream out)
+            {
+                if ( resultSet != null )
+                    ResultSetMgr.write(out, resultSet, ResultSetLang.SPARQLResultSetThrift) ;
+                if ( booleanResult != null )
+                    slog.error("Can't write boolen result in thrift") ;
+            }
+        } ;
+        output(action, contentType, WebContent.charsetUTF8, proc) ; 
+    }
+
+    private static void output(HttpAction action, String contentType, String charset, OutputContent proc) 
+    {
+        try {
+            setHttpResponse(action.request, action.response, contentType, charset) ; 
+            action.response.setStatus(HttpSC.OK_200) ;
+            ServletOutputStream out = action.response.getOutputStream() ;
+            try
+            {
+                proc.output(out) ;
+                out.flush() ;
+            } catch (QueryCancelledException ex) {
+                // Bother.  Status code 200 already sent.
+                slog.info(format("[%d] Query Cancelled - results truncated (but 200 already sent)", action.id)) ;
+                out.println() ;
+                out.println("##  Query cancelled due to timeout during execution   ##") ;
+                out.println("##  ****          Incomplete results           ****   ##") ;
+                out.flush() ;
+                // No point raising an exception - 200 was sent already.  
+                //errorOccurred(ex) ;
+            }
+        // Includes client gone.
+        } catch (IOException ex) 
+        { errorOccurred(ex) ; }
+        // Do not call httpResponse.flushBuffer(); here - Jetty closes the stream if it is a gzip stream
+        // then the JSON callback closing details can't be added. 
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Protocol.java
----------------------------------------------------------------------
diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Protocol.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Protocol.java
new file mode 100644
index 0000000..ed57a37
--- /dev/null
+++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Protocol.java
@@ -0,0 +1,101 @@
+/**
+ * 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.HttpNames.paramDefaultGraphURI ;
+import static org.apache.jena.fuseki.HttpNames.paramNamedGraphURI ;
+
+import java.util.Arrays ;
+import java.util.Collections ;
+import java.util.List ;
+
+import javax.servlet.http.HttpServletRequest ;
+
+import org.apache.jena.atlas.iterator.Filter ;
+import org.apache.jena.atlas.iterator.Iter ;
+import org.apache.jena.atlas.lib.Lib ;
+
+import com.hp.hpl.jena.query.Query ;
+import com.hp.hpl.jena.query.QueryParseException ;
+import com.hp.hpl.jena.sparql.core.DatasetDescription ;
+
+/** Support for the SPARQL protocol (SPARQL Query, SPARQL Update)
+ */
+public  abstract class SPARQL_Protocol extends SPARQL_ServletBase
+{
+    protected SPARQL_Protocol() { super() ; }
+
+    protected static String messageForQPE(QueryParseException ex)
+    {
+        if ( ex.getMessage() != null )
+            return ex.getMessage() ;
+        if ( ex.getCause() != null )
+            return Lib.classShortName(ex.getCause().getClass()) ;
+        return null ;
+    }
+    
+    protected static DatasetDescription getDatasetDescription(HttpAction action)
+    {
+        List<String> graphURLs = toStrList(action.request.getParameterValues(paramDefaultGraphURI)) ;
+        List<String> namedGraphs = toStrList(action.request.getParameterValues(paramNamedGraphURI)) ;
+        
+        graphURLs = removeEmptyValues(graphURLs) ;
+        namedGraphs = removeEmptyValues(namedGraphs) ;
+        
+        if ( graphURLs.size() == 0 && namedGraphs.size() == 0 )
+            return null ;
+        return DatasetDescription.create(graphURLs, namedGraphs) ;
+    }
+    
+    protected static DatasetDescription getDatasetDescription(Query query)
+    {
+        return DatasetDescription.create(query) ;
+    }
+   
+    private static List<String> toStrList(String[] array)
+    {
+        if ( array == null )
+            return Collections.emptyList() ;
+        return Arrays.asList(array) ;
+    }
+
+    private static List<String> removeEmptyValues(List<String> list)
+    {
+        return Iter.iter(list).filter(acceptNonEmpty).toList() ;
+    }
+    
+    private static Filter<String> acceptNonEmpty = new Filter<String>(){ 
+        @Override
+        public boolean accept(String item)
+        {
+            return item != null && item.length() != 0 ;
+        }
+    } ;
+    
+    protected static int countParamOccurences(HttpServletRequest request, String param)
+    {
+        String[] x = request.getParameterValues(param) ;
+        if ( x == null )
+            return 0 ;
+        return x.length ;
+    }
+    
+
+}
+