http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/REST_Quads.java
new file mode 100644
index 0000000..06c38b7
--- /dev/null
+++ b/jena-fuseki/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/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/ResponseCallback.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/ResponseCallback.java
 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/ResponseCallback.java
new file mode 100644
index 0000000..1a78627
--- /dev/null
+++ 
b/jena-fuseki/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/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/ResponseModel.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/ResponseModel.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/ResponseModel.java
new file mode 100644
index 0000000..f2172f0
--- /dev/null
+++ 
b/jena-fuseki/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/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/ResponseOps.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/ResponseOps.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/ResponseOps.java
new file mode 100644
index 0000000..62ad6d5
--- /dev/null
+++ b/jena-fuseki/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/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/ResponseResultSet.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/ResponseResultSet.java
 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/ResponseResultSet.java
new file mode 100644
index 0000000..c42378b
--- /dev/null
+++ 
b/jena-fuseki/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/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Protocol.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Protocol.java
 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Protocol.java
new file mode 100644
index 0000000..ed57a37
--- /dev/null
+++ 
b/jena-fuseki/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 ;
+    }
+    
+
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Query.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Query.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Query.java
new file mode 100644
index 0000000..27e76ac
--- /dev/null
+++ 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Query.java
@@ -0,0 +1,386 @@
+/*
+ * 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.fuseki.HttpNames.* ;
+import static org.apache.jena.fuseki.server.CounterName.QueryExecErrors ;
+import static org.apache.jena.fuseki.server.CounterName.QueryTimeouts ;
+import static org.apache.jena.fuseki.server.CounterName.RequestsBad ;
+
+import java.io.IOException ;
+import java.io.InputStream ;
+import java.util.* ;
+
+import javax.servlet.http.HttpServletRequest ;
+import javax.servlet.http.HttpServletResponse ;
+
+import org.apache.jena.atlas.RuntimeIOException ;
+import org.apache.jena.atlas.io.IO ;
+import org.apache.jena.atlas.io.IndentedLineBuffer ;
+import org.apache.jena.atlas.web.ContentType ;
+import org.apache.jena.fuseki.FusekiException ;
+import org.apache.jena.fuseki.FusekiLib ;
+import org.apache.jena.fuseki.HttpNames ;
+import org.apache.jena.riot.WebContent ;
+import org.apache.jena.riot.web.HttpOp ;
+import org.apache.jena.web.HttpSC ;
+
+import com.hp.hpl.jena.query.* ;
+import com.hp.hpl.jena.rdf.model.Model ;
+import com.hp.hpl.jena.sparql.core.Prologue ;
+import com.hp.hpl.jena.sparql.resultset.SPARQLResult ;
+
+/**
+ * Handles SPARQL Query requests.
+ */
+public abstract class SPARQL_Query extends SPARQL_Protocol
+{
+    public SPARQL_Query()   { super() ; }
+
+    // Choose REST verbs to support.
+    
+    @Override
+    protected void doPost(HttpServletRequest request, HttpServletResponse 
response)
+    { doCommon(request, response) ; }
+    
+    @Override
+    protected void doGet(HttpServletRequest request, HttpServletResponse 
response)
+    { doCommon(request, response) ; }
+
+    // HEAD
+    
+    @Override
+    protected void doOptions(HttpServletRequest request, HttpServletResponse 
response)
+    {
+        //response.setHeader(HttpNames.hAllow, "GET,HEAD,OPTIONS,POST");
+        setCommonHeaders(response) ;
+        response.setHeader(HttpNames.hAllow, "GET,OPTIONS,POST");
+        response.setHeader(HttpNames.hContentLengh, "0") ;
+    }
+    
+    @Override
+    protected final void perform(HttpAction action)
+    {
+        // GET
+        if ( action.request.getMethod().equals(HttpNames.METHOD_GET) )
+        {
+            executeWithParameter(action) ;
+            return ;
+        }
+
+        ContentType ct = FusekiLib.getContentType(action) ;
+        String incoming = ct.getContentType() ;
+
+        // POST application/sparql-query
+        if (WebContent.contentTypeSPARQLQuery.equals(incoming))
+        {
+            executeBody(action) ;
+            return ;
+        }
+        // POST application/x-www-form-url
+        if (WebContent.contentTypeHTMLForm.equals(incoming))
+        {
+            executeWithParameter(action) ;
+            return ;
+        }
+
+        error(HttpSC.UNSUPPORTED_MEDIA_TYPE_415, "Bad content type: 
"+incoming) ;
+    }
+
+    // All the params we support
+
+    protected static List<String> allParams  = Arrays.asList(paramQuery, 
+                                                             
paramDefaultGraphURI, paramNamedGraphURI, 
+                                                             paramQueryRef,
+                                                             paramStyleSheet,
+                                                             paramAccept,
+                                                             paramOutput1, 
paramOutput2, 
+                                                             paramCallback, 
+                                                             paramForceAccept,
+                                                             paramTimeout) ;
+
+    @Override
+    protected void validate(HttpAction action)
+    {
+        String method = action.request.getMethod().toUpperCase(Locale.ROOT) ;
+        
+        if ( ! HttpNames.METHOD_POST.equals(method) && ! 
HttpNames.METHOD_GET.equals(method) )
+            errorMethodNotAllowed("Not a GET or POST request") ;
+            
+        if ( HttpNames.METHOD_GET.equals(method) && 
action.request.getQueryString() == null )
+        {
+            warning("Service Description / SPARQL Query / 
"+action.request.getRequestURI()) ;
+            errorNotFound("Service Description: 
"+action.request.getRequestURI()) ;
+        }
+        
+        // Use of the dataset describing parameters is check later.
+        try {
+            validateParams(action.request, allParams) ;
+            validateRequest(action) ; 
+        } catch (ActionErrorException ex) { 
+            throw ex ; 
+        } 
+        // Query not yet parsed.
+    }
+    
+    /**
+     * Validate the request after checking HTTP method and HTTP Parameters.
+     * @param action HTTP Action
+     */
+    protected abstract void validateRequest(HttpAction action) ;
+    
+    /**
+     * Helper method for validating request.
+     * @param request HTTP request
+     * @param params parameters in a collection of Strings
+     */
+    protected void validateParams(HttpServletRequest request, 
Collection<String> params)
+    {
+        ContentType ct = FusekiLib.getContentType(request) ;
+        boolean mustHaveQueryParam = true ;
+        if ( ct != null )
+        {
+            String incoming = ct.getContentType() ;
+            
+            if ( WebContent.contentTypeSPARQLQuery.equals(incoming) )
+            {
+                mustHaveQueryParam = false ;
+                //error(HttpSC.UNSUPPORTED_MEDIA_TYPE_415, "Unofficial 
"+WebContent.contentTypeSPARQLQuery+" not supported") ;
+            }
+            else if ( WebContent.contentTypeHTMLForm.equals(incoming) ) {}
+            else
+                error(HttpSC.UNSUPPORTED_MEDIA_TYPE_415, "Unsupported: 
"+incoming) ;
+        }
+        
+        // GET/POST of a form at this point.
+        
+        if ( mustHaveQueryParam )
+        {
+            int N = countParamOccurences(request, paramQuery) ; 
+            
+            if ( N == 0 ) errorBadRequest("SPARQL Query: No 'query=' 
parameter") ;
+            if ( N > 1 ) errorBadRequest("SPARQL Query: Multiple 'query=' 
parameters") ;
+            
+            // application/sparql-query does not use a query param.
+            String queryStr = request.getParameter(HttpNames.paramQuery) ;
+            
+            if ( queryStr == null )
+                errorBadRequest("SPARQL Query: No query specified (no 'query=' 
found)") ;
+            if ( queryStr.isEmpty() )
+                errorBadRequest("SPARQL Query: Empty query string") ;
+        }
+
+        if ( params != null )
+        {
+            Enumeration<String> en = request.getParameterNames() ;
+            for ( ; en.hasMoreElements() ; )
+            {
+                String name = en.nextElement() ;
+                if ( ! params.contains(name) )
+                    warning("SPARQL Query: Unrecognize request parameter 
(ignored): "+name) ;
+            }
+        }
+    }
+
+    private void executeWithParameter(HttpAction action)
+    {
+        String queryString = action.request.getParameter(paramQuery) ;
+        execute(queryString, action) ;
+    }
+
+    private void executeBody(HttpAction action)
+    {
+        String queryString = null ;
+        try { 
+            InputStream input = action.request.getInputStream() ; 
+            queryString = IO.readWholeFileAsUTF8(input) ;
+        }
+        catch (IOException ex) { errorOccurred(ex) ; }
+        execute(queryString, action) ;
+    }
+
+    private void execute(String queryString, HttpAction action)
+    {
+        String queryStringLog = formatForLog(queryString) ;
+        if ( action.verbose )
+            log.info(format("[%d] Query = \n%s", action.id, queryString));
+        else
+            log.info(format("[%d] Query = %s", action.id, queryStringLog));
+
+        Query query = null ;
+        try {
+            // NB syntax is ARQ (a superset of SPARQL)
+            query = QueryFactory.create(queryString, 
"http://example/query-base";, Syntax.syntaxARQ) ;
+            queryStringLog = formatForLog(query) ;
+            validateQuery(action, query) ;
+        } catch (ActionErrorException ex) {
+            incCounter(action.srvRef, RequestsBad) ;
+            throw ex ;
+        } catch (QueryParseException ex) {
+            incCounter(action.srvRef, RequestsBad) ;
+            errorBadRequest("Parse error: \n" + queryString + "\n\r" + 
messageForQPE(ex)) ;
+        } catch (RuntimeIOException ex) {
+            errorBadRequest("Runtime IO Exception: \n" + queryString + "\n\r" 
+ ex.getMessage()) ;
+        }
+        // Should not happen.
+        catch (QueryException ex) {
+            errorBadRequest("Error: \n" + queryString + "\n\r" + 
ex.getMessage()) ;
+        }
+        
+        // Assumes finished whole thing by end of sendResult. 
+        action.beginRead() ;
+        try {
+            Dataset dataset = decideDataset(action, query, queryStringLog) ; 
+            try(QueryExecution qExec = createQueryExecution(query, dataset)) {
+                SPARQLResult result = executeQuery(action, qExec, query, 
queryStringLog) ;
+                // Deals with exceptions itself.
+                sendResults(action, result, query.getPrologue()) ;
+            }
+        } catch (QueryCancelledException ex) {
+            // Additional counter information.
+            incCounter(action.srvRef, QueryTimeouts) ; 
+            throw ex ; 
+        } catch (RuntimeIOException ex) {
+            incCounter(action.srvRef, QueryExecErrors) ;
+            throw ex ;
+        } catch (QueryExecException ex) { 
+            // Additional counter information.
+            incCounter(action.srvRef, QueryExecErrors) ; 
+            throw ex ;
+        } finally { 
+            action.endRead() ;
+        }
+    }
+
+    /**
+     * Check the query, throwing ActionErrorException when not valid, or 
calling super#error.
+     * @param action HTTP Action
+     * @param query the Query
+     */
+    protected abstract void validateQuery(HttpAction action, Query query) ;
+
+    protected QueryExecution createQueryExecution(Query query, Dataset dataset)
+    {
+        return QueryExecutionFactory.create(query, dataset) ;
+    }
+
+    protected SPARQLResult executeQuery(HttpAction action, QueryExecution 
qExec, Query query, String queryStringLog)
+    {
+        setAnyTimeouts(qExec, action);
+
+        if ( query.isSelectType() )
+        {
+            ResultSet rs = qExec.execSelect() ;
+            
+            // Force some query execution now.
+            //
+            // If the timeout-first-row goes off, the output stream has not 
+            // been started so the HTTP error code is sent. 
+            
+            rs.hasNext() ;
+
+            // If we wanted perfect query time cancellation, we could consume 
the result now
+            // to see if the timeout-end-of-query goes off.  
+            
+            //rs = ResultSetFactory.copyResults(rs) ;
+
+            log.info(format("[%d] exec/select", action.id)) ;
+            return new SPARQLResult(rs) ;
+        }
+
+        if ( query.isConstructType() )
+        {
+            Model model = qExec.execConstruct() ;
+            log.info(format("[%d] exec/construct", action.id)) ;
+            return new SPARQLResult(model) ;
+        }
+
+        if ( query.isDescribeType() )
+        {
+            Model model = qExec.execDescribe() ;
+            log.info(format("[%d] exec/describe",action.id)) ;
+            return new SPARQLResult(model) ;
+        }
+
+        if ( query.isAskType() )
+        {
+            boolean b = qExec.execAsk() ;
+            log.info(format("[%d] exec/ask",action.id)) ;
+            return new SPARQLResult(b) ;
+        }
+
+        errorBadRequest("Unknown query type - "+queryStringLog) ;
+        return null ;
+    }
+
+    private void setAnyTimeouts(QueryExecution qexec, HttpAction action) {
+        if (!(action.getDatasetRef().allowTimeoutOverride))
+            return;
+
+        long desiredTimeout = Long.MAX_VALUE;
+        String timeoutHeader = action.request.getHeader("Timeout");
+        String timeoutParameter = action.request.getParameter("timeout");
+        if (timeoutHeader != null) {
+            try {
+                desiredTimeout = (int) Float.parseFloat(timeoutHeader) * 1000;
+            } catch (NumberFormatException e) {
+                throw new FusekiException("Timeout header must be a number", 
e);
+            }
+        } else if (timeoutParameter != null) {
+            try {
+                desiredTimeout = (int) Float.parseFloat(timeoutParameter) * 
1000;
+            } catch (NumberFormatException e) {
+                throw new FusekiException("timeout parameter must be a 
number", e);
+            }
+        }
+
+        desiredTimeout = 
Math.min(action.getDatasetRef().maximumTimeoutOverride, desiredTimeout);
+        if (desiredTimeout != Long.MAX_VALUE)
+            qexec.setTimeout(desiredTimeout);
+    }
+
+    protected abstract Dataset decideDataset(HttpAction action, Query query, 
String queryStringLog) ;
+
+    protected void sendResults(HttpAction action, SPARQLResult result, 
Prologue qPrologue)
+    {
+        if ( result.isResultSet() )
+            ResponseResultSet.doResponseResultSet(action, 
result.getResultSet(), qPrologue) ;
+        else if ( result.isGraph() )
+            ResponseModel.doResponseModel(action, result.getModel()) ;
+        else if ( result.isBoolean() )
+            ResponseResultSet.doResponseResultSet(action, 
result.getBooleanResult()) ;
+        else
+            errorOccurred("Unknown or invalid result type") ;
+    }
+    
+    private String formatForLog(Query query)
+    {
+        IndentedLineBuffer out = new IndentedLineBuffer() ;
+        out.setFlatMode(true) ;
+        query.serialize(out) ;
+        return out.asString() ;
+    }
+        
+    private String getRemoteString(String queryURI)
+    {
+        return HttpOp.execHttpGetString(queryURI) ;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_QueryDataset.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_QueryDataset.java
 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_QueryDataset.java
new file mode 100644
index 0000000..9e9df36
--- /dev/null
+++ 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_QueryDataset.java
@@ -0,0 +1,60 @@
+/*
+ * 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 com.hp.hpl.jena.query.Dataset ;
+import com.hp.hpl.jena.query.DatasetFactory ;
+import com.hp.hpl.jena.query.Query ;
+import com.hp.hpl.jena.sparql.core.DatasetDescription ;
+import com.hp.hpl.jena.sparql.core.DatasetGraph ;
+import com.hp.hpl.jena.sparql.core.DynamicDatasets ;
+
+public class SPARQL_QueryDataset extends SPARQL_Query
+{
+    public SPARQL_QueryDataset(boolean verbose)     { super() ; }
+
+    public SPARQL_QueryDataset()
+    { this(false) ; }
+    
+    @Override
+    protected void validateRequest(HttpAction action) 
+    { }
+
+    @Override
+    protected void validateQuery(HttpAction action, Query query) 
+    { }
+   
+    @Override
+    protected Dataset decideDataset(HttpAction action, Query query, String 
queryStringLog) 
+    { 
+        DatasetGraph dsg = action.getActiveDSG() ;
+        
+        // query.getDatasetDescription() ;
+        
+        // Protocol.
+        DatasetDescription dsDesc = getDatasetDescription(action) ;
+        if (dsDesc != null )
+        {
+            //errorBadRequest("SPARQL Query: Dataset description in the 
protocol request") ;
+            dsg = DynamicDatasets.dynamicDataset(dsDesc, dsg, false) ;
+        }
+        
+        return DatasetFactory.create(dsg) ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_QueryGeneral.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_QueryGeneral.java
 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_QueryGeneral.java
new file mode 100644
index 0000000..a022e96
--- /dev/null
+++ 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_QueryGeneral.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 java.lang.String.format ;
+
+import java.util.List ;
+
+import org.apache.jena.atlas.lib.InternalErrorException ;
+import org.apache.jena.fuseki.migrate.GraphLoadUtils ;
+import org.apache.jena.riot.RiotException ;
+
+import com.hp.hpl.jena.query.Dataset ;
+import com.hp.hpl.jena.query.DatasetFactory ;
+import com.hp.hpl.jena.query.Query ;
+import com.hp.hpl.jena.rdf.model.Model ;
+import com.hp.hpl.jena.rdf.model.ModelFactory ;
+import com.hp.hpl.jena.sparql.core.DatasetDescription ;
+
+public class SPARQL_QueryGeneral extends SPARQL_Query
+{
+    final static int MaxTriples = 100*1000 ; 
+    
+    public SPARQL_QueryGeneral()    { super() ; }
+
+    @Override
+    protected void validateRequest(HttpAction action) {}
+
+    @Override
+    protected void validateQuery(HttpAction action, Query query) {}
+    
+    @Override
+    protected String mapRequestToDataset(String uri)
+    { return null ; }
+    
+    @Override
+    protected Dataset decideDataset(HttpAction action, Query query, String 
queryStringLog) 
+    {
+        DatasetDescription datasetDesc = getDatasetDescription(action) ;
+        if ( datasetDesc == null )
+            datasetDesc = getDatasetDescription(query) ;
+        if ( datasetDesc == null )
+            errorBadRequest("No dataset description in protocol request or in 
the query string") ;
+
+        return datasetFromDescription(action, datasetDesc) ;
+    }
+
+    /**
+     * Construct a Dataset based on a dataset description.
+     */
+    
+    protected static Dataset datasetFromDescription(HttpAction action, 
DatasetDescription datasetDesc)
+    {
+        try {
+            if ( datasetDesc == null )
+                return null ;
+            if ( datasetDesc.isEmpty() )
+                return null ;
+            
+            List<String> graphURLs = datasetDesc.getDefaultGraphURIs() ;
+            List<String> namedGraphs = datasetDesc.getNamedGraphURIs() ;
+            
+            if ( graphURLs.size() == 0 && namedGraphs.size() == 0 )
+                    return null ;
+            
+            Dataset dataset = DatasetFactory.createMem() ;
+            // Look in cache for loaded graphs!!
+
+            // ---- Default graph
+            {
+                Model model = ModelFactory.createDefaultModel() ;
+                for ( String uri : graphURLs )
+                {
+                    if ( uri == null || uri.equals("") )
+                        throw new InternalErrorException("Default graph URI is 
null or the empty string")  ;
+
+                    try {
+                        //TODO Clearup - RIOT integration.
+                        GraphLoadUtils.loadModel(model, uri, MaxTriples) ;
+                        log.info(format("[%d] Load (default graph) %s", 
action.id, uri)) ;
+                    } catch (RiotException ex) {
+                        log.info(format("[%d] Parsing error loading %s: %s", 
action.id, uri, ex.getMessage())) ;
+                        errorBadRequest("Failed to load URL (parse error) 
"+uri+" : "+ex.getMessage()) ;
+                    } catch (Exception ex)
+                    {
+                        log.info(format("[%d] Failed to load (default) %s: 
%s", action.id, uri, ex.getMessage())) ;
+                        errorBadRequest("Failed to load URL "+uri) ;
+                    }
+                }
+                dataset.setDefaultModel(model) ;
+            }
+            // ---- Named graphs
+            if ( namedGraphs != null )
+            {
+                for ( String uri : namedGraphs )
+                {
+                    if ( uri == null || uri.equals("") )
+                        throw new InternalErrorException("Named graph URI is 
null or the empty string")  ;
+
+                    try {
+                        Model model = ModelFactory.createDefaultModel() ;
+                        GraphLoadUtils.loadModel(model, uri, MaxTriples) ;
+                        log.info(format("[%d] Load (named graph) %s", 
action.id, uri)) ;
+                        dataset.addNamedModel(uri, model) ;
+                    } catch (RiotException ex) {
+                        log.info(format("[%d] Parsing error loading %s: %s", 
action.id, uri, ex.getMessage())) ;
+                        errorBadRequest("Failed to load URL (parse error) 
"+uri+" : "+ex.getMessage()) ;
+                    } catch (Exception ex)
+                    {
+                        log.info(format("[%d] Failed to load (named graph) %s: 
%s", action.id, uri, ex.getMessage())) ;
+                        errorBadRequest("Failed to load URL "+uri) ;
+                    }
+                }
+            }
+            
+            return dataset ;
+            
+        } 
+        catch (ActionErrorException ex) { throw ex ; }
+        catch (Exception ex)
+        {
+            log.info(format("[%d] SPARQL parameter error: 
"+ex.getMessage(),action.id, ex)) ;
+            errorBadRequest("Parameter error: "+ex.getMessage());
+            return null ;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_REST.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_REST.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_REST.java
new file mode 100644
index 0000000..4ab386b
--- /dev/null
+++ b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_REST.java
@@ -0,0 +1,354 @@
+/*
+ * 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.* ;
+
+import java.io.IOException ;
+import java.io.InputStream ;
+import java.util.Enumeration ;
+import java.util.Locale ;
+
+import javax.servlet.ServletException ;
+import javax.servlet.http.HttpServletRequest ;
+import javax.servlet.http.HttpServletResponse ;
+
+import org.apache.jena.fuseki.HttpNames ;
+import org.apache.jena.fuseki.server.CounterName ;
+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.IRIResolver ;
+import org.apache.jena.riot.system.StreamRDF ;
+import org.slf4j.Logger ;
+import org.slf4j.LoggerFactory ;
+
+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 ;
+
+public abstract class SPARQL_REST extends SPARQL_ServletBase
+{
+    protected static Logger classLog = 
LoggerFactory.getLogger(SPARQL_REST.class) ;
+    
+    protected static ErrorHandler errorHandler = 
ErrorHandlerFactory.errorHandlerStd(log) ;
+
+    protected final static Target determineTarget(HttpAction action) {
+        // Delayed until inside a transaction.
+        if ( action.getActiveDSG() == null )
+            errorOccurred("Internal error : No action graph (not in a 
transaction?)") ;
+        
+        boolean dftGraph = getOneOnly(action.request, 
HttpNames.paramGraphDefault) != null ;
+        String uri = getOneOnly(action.request, HttpNames.paramGraph) ;
+        
+        if ( !dftGraph && uri == null ) {
+            // Direct naming or error.
+            uri = action.request.getRequestURL().toString() ;
+            if ( 
action.request.getRequestURI().equals(action.getDatasetRef().name) )
+                // No name 
+                errorBadRequest("Neither default graph nor named graph 
specified; no direct name") ;
+        }
+        
+        if ( dftGraph )
+            return Target.createDefault(action.getActiveDSG()) ;
+        
+        // Named graph
+        if ( uri.equals(HttpNames.valueDefault ) )
+            // But "named" default
+            return Target.createDefault(action.getActiveDSG()) ;
+        
+        // Strictly, a bit naughty on the URI resolution.  But more sensible. 
+        // Base is dataset.
+        String base = action.request.getRequestURL().toString() ; 
//wholeRequestURL(request) ;
+        // Make sure it ends in "/", ie. dataset as container.
+        if ( action.request.getQueryString() != null && ! base.endsWith("/") )
+            base = base + "/" ;
+        
+        String absUri = IRIResolver.resolveString(uri, base) ;
+        Node gn = NodeFactory.createURI(absUri) ;
+        return Target.createNamed(action.getActiveDSG(), absUri, gn) ;
+    }
+    
+
+    // struct for target
+    protected static final class Target
+    {
+        final boolean isDefault ;
+        final DatasetGraph dsg ;
+        private Graph _graph ;
+        final String name ;
+        final Node graphName ;
+
+        static Target createNamed(DatasetGraph dsg, String name, Node 
graphName) {
+            return new Target(false, dsg, name, graphName) ;
+        }
+
+        static Target createDefault(DatasetGraph dsg) {
+            return new Target(true, dsg, null, null) ;
+        }
+
+        private Target(boolean isDefault, DatasetGraph dsg, String name, Node 
graphName) {
+            this.isDefault = isDefault ;
+            this.dsg = dsg ;
+            this._graph = null ;
+            this.name  = name ;
+            this.graphName = graphName ;
+
+            if ( isDefault )
+            {
+                if ( name != null || graphName != null )
+                    throw new IllegalArgumentException("Inconsistent: default 
and a graph name/node") ;       
+            }
+            else
+            {
+                if ( name == null || graphName == null )
+                    throw new IllegalArgumentException("Inconsistent: not 
default and/or no graph name/node") ;
+            }                
+        }
+
+        /** Get a graph for the action - this may create a graph in the 
dataset - this is not a test for graph existence */
+        public Graph graph() {
+            if ( ! isGraphSet() )
+            {
+                if ( isDefault ) 
+                    _graph = dsg.getDefaultGraph() ;
+                else
+                    _graph = dsg.getGraph(graphName) ;
+            }
+            return _graph ;
+        }
+
+        public boolean exists()
+        {
+            if ( isDefault ) return true ;
+            return dsg.containsGraph(graphName) ;
+        }
+
+        public boolean isGraphSet()
+        {
+            return _graph != null ;
+        }
+
+        @Override
+        public String toString()
+        {
+            if ( isDefault ) return "default" ;
+            return name ;
+        }
+    }
+
+    public SPARQL_REST()
+    { super() ; }
+
+    @Override
+    protected void service(HttpServletRequest request, HttpServletResponse 
response) throws ServletException, IOException {
+        // Direct all verbs to our common framework.
+        doCommon(request, response) ;
+    }
+    
+    private void maybeSetLastModified(HttpServletResponse resp, long 
lastModified) {
+        if (resp.containsHeader(HEADER_LASTMOD)) return ;
+        if (lastModified >= 0) resp.setDateHeader(HEADER_LASTMOD, 
lastModified);
+    }
+    
+    @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) ;
+            errorMethodNotAllowed("TRACE") ;
+        else if (method.equals(METHOD_PUT))
+            doPut$(action) ;   
+        else if (method.equals(METHOD_DELETE))
+            doDelete$(action) ;
+        else
+            errorNotImplemented("Unknown method: "+method) ;
+    }
+
+    // Counter wrappers
+    
+    protected void doGet$(HttpAction action) {
+        incCounter(action.srvRef, CounterName.GSPget) ;
+        try {
+            doGet(action) ;
+            incCounter(action.srvRef, CounterName.GSPgetGood) ;
+        } catch ( ActionErrorException ex) {
+            incCounter(action.srvRef, CounterName.GSPgetBad) ;
+            throw ex ;
+        }
+    }
+
+    protected void doHead$(HttpAction action) {
+        incCounter(action.srvRef, CounterName.GSPhead) ;
+        try {
+            doHead(action) ;
+            incCounter(action.srvRef, CounterName.GSPheadGood) ;
+        } catch ( ActionErrorException ex) {
+            incCounter(action.srvRef, CounterName.GSPheadBad) ;
+            throw ex ;
+        }
+    }
+
+    protected void doPost$(HttpAction action) {
+        incCounter(action.srvRef, CounterName.GSPpost) ;
+        try {
+            doPost(action) ;
+            incCounter(action.srvRef, CounterName.GSPpostGood) ;
+        } catch ( ActionErrorException ex) {
+            incCounter(action.srvRef, CounterName.GSPpostBad) ;
+            throw ex ;
+        }
+    }
+
+    protected void doPatch$(HttpAction action) {
+        incCounter(action.srvRef, CounterName.GSPpatch) ;
+        try {
+            doPatch(action) ;
+            incCounter(action.srvRef, CounterName.GSPpatchGood) ;
+        } catch ( ActionErrorException ex) {
+            incCounter(action.srvRef, CounterName.GSPpatchBad) ;
+            throw ex ;
+        }
+    }
+
+    protected void doDelete$(HttpAction action) {
+        incCounter(action.srvRef, CounterName.GSPdelete) ;
+        try {
+            doDelete(action) ;
+            incCounter(action.srvRef, CounterName.GSPdeleteGood) ;
+        } catch ( ActionErrorException ex) {
+            incCounter(action.srvRef, CounterName.GSPdeleteBad) ;
+            throw ex ;
+        }
+    }
+
+    protected void doPut$(HttpAction action) {
+        incCounter(action.srvRef, CounterName.GSPput) ;
+        try {
+            doPut(action) ;
+            incCounter(action.srvRef, CounterName.GSPputGood) ;
+        } catch ( ActionErrorException ex) {
+            incCounter(action.srvRef, CounterName.GSPputBad) ;
+            throw ex ;
+        }
+    }
+
+    protected void doOptions$(HttpAction action) {
+        incCounter(action.srvRef, CounterName.GSPoptions) ;
+        try {
+            doOptions(action) ;
+            incCounter(action.srvRef, CounterName.GSPoptionsGood) ;
+        } catch ( ActionErrorException ex) {
+            incCounter(action.srvRef, CounterName.GSPoptionsBad) ;
+            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) ;
+    
+    // @@ Move to SPARQL_ServletBase
+    // Check for all RiotReader
+    public static void parse(HttpAction action, StreamRDF dest, InputStream 
input, Lang lang, String base) {
+        try {
+            ReaderRIOT r = RDFDataMgr.createReader(lang) ;
+            if ( r == null )
+                errorBadRequest("No parser for language '"+lang.getName()+"'") 
;
+            r.setErrorHandler(errorHandler); 
+            r.read(input, base, null, dest, null) ; 
+        } 
+        catch (RiotException ex) { errorBadRequest("Parse error: 
"+ex.getMessage()) ; }
+    }
+
+    @Override
+    protected void validate(HttpAction action)
+    {
+        HttpServletRequest request = action.request ;
+        // Direct naming.
+        if ( request.getQueryString() == null )
+            //errorBadRequest("No query string") ;
+            return ;
+        
+        String g = request.getParameter(HttpNames.paramGraph) ;
+        String d = request.getParameter(HttpNames.paramGraphDefault) ;
+        
+        if ( g != null && d !=null )
+            errorBadRequest("Both ?default and ?graph in the query string of 
the request") ;
+        
+        if ( g == null && d == null )
+            errorBadRequest("Neither ?default nor ?graph in the query string 
of the request") ;
+        
+        int x1 = SPARQL_Protocol.countParamOccurences(request, 
HttpNames.paramGraph) ;
+        int x2 = SPARQL_Protocol.countParamOccurences(request, 
HttpNames.paramGraphDefault) ;
+        
+        if ( x1 > 1 )
+            errorBadRequest("Multiple ?default in the query string of the 
request") ;
+        if ( x2 > 1 )
+            errorBadRequest("Multiple ?graph in the query string of the 
request") ;
+        
+        Enumeration<String> en = request.getParameterNames() ;
+        for ( ; en.hasMoreElements() ; )
+        {
+            String h = en.nextElement() ;
+            if ( ! HttpNames.paramGraph.equals(h) && ! 
HttpNames.paramGraphDefault.equals(h) )
+                errorBadRequest("Unknown parameter '"+h+"'") ;
+            // one of ?default and &graph
+            if ( request.getParameterValues(h).length != 1 )
+                errorBadRequest("Multiple parameters '"+h+"'") ;
+        }
+    }
+
+    protected static String getOneOnly(HttpServletRequest request, String name)
+    {
+        String[] values = request.getParameterValues(name) ;
+        if ( values == null )
+            return null ;
+        if ( values.length == 0 )
+            return null ;
+        if ( values.length > 1 )
+            errorBadRequest("Multiple occurrences of '"+name+"'") ;
+        return values[0] ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/36855e1b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_REST_R.java
----------------------------------------------------------------------
diff --git 
a/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_REST_R.java 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_REST_R.java
new file mode 100644
index 0000000..3c5c926
--- /dev/null
+++ 
b/jena-fuseki/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_REST_R.java
@@ -0,0 +1,125 @@
+/*
+ * 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.HttpNames ;
+import org.apache.jena.riot.* ;
+
+import com.hp.hpl.jena.graph.Graph ;
+
+/** Only the READ operations */
+public class SPARQL_REST_R extends SPARQL_REST
+{
+    public SPARQL_REST_R()
+    { super() ; }
+    
+    
+    @Override
+    protected String mapRequestToDataset(String uri) { return 
mapRequestToDatasetLongest$(uri) ; } 
+
+    @Override
+    protected void doGet(HttpAction action)
+    {
+        // Assume success - do the set up before grabbing the lock.
+        // Sets content type.
+        MediaType mediaType = HttpAction.contentNegotationRDF(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 ( action.verbose )
+            log.info(format("[%d]   Get: Content-Type=%s, Charset=%s => %s", 
+                            action.id, mediaType.getContentType(), 
mediaType.getCharset(), lang.getName())) ;
+
+        action.beginRead() ;
+
+        try {
+            Target target = determineTarget(action) ;
+            if ( log.isDebugEnabled() )
+                log.debug("GET->"+target) ;
+            boolean exists = target.exists() ;
+            if ( ! exists )
+                errorNotFound("No such graph: <"+target.name+">") ;
+            // If we want to set the Content-Length, we need to buffer.
+            //response.setContentLength(??) ;
+            String ct = lang.getContentType().toHeaderString() ;
+            action.response.setContentType(ct) ;
+            Graph g = target.graph() ;
+            //Special case RDF/XML to be the plain (faster, less readable) form
+            RDFFormat fmt = 
+                ( lang == Lang.RDFXML ) ? RDFFormat.RDFXML_PLAIN : 
RDFWriterRegistry.defaultSerialization(lang) ;  
+            RDFDataMgr.write(out, g, fmt) ;
+            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 { 
+            Target target = determineTarget(action) ;
+            if ( log.isDebugEnabled() )
+                log.debug("HEAD->"+target) ;
+            if ( ! target.exists() )
+            {
+                successNotFound(action) ;
+                return ;
+            }
+            MediaType mediaType = HttpAction.contentNegotationRDF(action) ;
+            success(action) ;
+        } finally { action.endRead() ; }
+    }
+
+    @Override
+    protected void doPost(HttpAction action)
+    { errorMethodNotAllowed("POST") ; }
+
+    @Override
+    protected void doDelete(HttpAction action)
+    { errorMethodNotAllowed("DELETE") ; }
+
+    @Override
+    protected void doPut(HttpAction action)
+    { errorMethodNotAllowed("PUT") ; }
+
+    @Override
+    protected void doPatch(HttpAction action)
+    { errorMethodNotAllowed("PATCH") ; }
+}

Reply via email to