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/01/05 18:34:21 UTC

[42/51] [abbrv] [partial] jena git commit: Maven modules for Fuseki2

http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_QueryGeneral.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_QueryGeneral.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_QueryGeneral.java
new file mode 100644
index 0000000..f1c9a9a
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_QueryGeneral.java
@@ -0,0 +1,142 @@
+/*
+ * 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(HttpAction action)
+    { 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 )
+            ServletOps.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 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 {
+                        GraphLoadUtils.loadModel(model, uri, MaxTriples) ;
+                        action.log.info(format("[%d] Load (default graph) %s", action.id, uri)) ;
+                    } catch (RiotException ex) {
+                        action.log.info(format("[%d] Parsing error loading %s: %s", action.id, uri, ex.getMessage())) ;
+                        ServletOps.errorBadRequest("Failed to load URL (parse error) "+uri+" : "+ex.getMessage()) ;
+                    } catch (Exception ex)
+                    {
+                        action.log.info(format("[%d] Failed to load (default) %s: %s", action.id, uri, ex.getMessage())) ;
+                        ServletOps.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) ;
+                        action.log.info(format("[%d] Load (named graph) %s", action.id, uri)) ;
+                        dataset.addNamedModel(uri, model) ;
+                    } catch (RiotException ex) {
+                        action.log.info(format("[%d] Parsing error loading %s: %s", action.id, uri, ex.getMessage())) ;
+                        ServletOps.errorBadRequest("Failed to load URL (parse error) "+uri+" : "+ex.getMessage()) ;
+                    } catch (Exception ex)
+                    {
+                        action.log.info(format("[%d] Failed to load (named graph) %s: %s", action.id, uri, ex.getMessage())) ;
+                        ServletOps.errorBadRequest("Failed to load URL "+uri) ;
+                    }
+                }
+            }
+            
+            return dataset ;
+            
+        } 
+        catch (ActionErrorException ex) { throw ex ; }
+        catch (Exception ex)
+        {
+            action.log.info(format("[%d] SPARQL parameter error: "+ex.getMessage(),action.id, ex)) ;
+            ServletOps.errorBadRequest("Parameter error: "+ex.getMessage());
+            return null ;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_UberServlet.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_UberServlet.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_UberServlet.java
new file mode 100644
index 0000000..efc7222
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_UberServlet.java
@@ -0,0 +1,359 @@
+/**
+ * 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.riot.WebContent.contentTypeSPARQLQuery ;
+import static org.apache.jena.riot.WebContent.contentTypeSPARQLUpdate ;
+
+import java.util.List ;
+
+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.FusekiException ;
+import org.apache.jena.fuseki.conneg.ConNeg ;
+import org.apache.jena.fuseki.server.DataAccessPoint ;
+import org.apache.jena.fuseki.server.DataService ;
+import org.apache.jena.fuseki.server.Endpoint ;
+import org.apache.jena.fuseki.server.OperationName ;
+import org.apache.jena.riot.web.HttpNames ;
+
+/** This servlet can be attached to a dataset location
+ *  and acts as a router for all SPARQL operations
+ *  (query, update, graph store, both direct and 
+ *  indirect naming, quads operations on a dataset and
+ *  ?query and ?update directly on a dataset.) 
+ */
+public abstract class SPARQL_UberServlet extends ActionSPARQL
+{
+    protected abstract boolean allowQuery(HttpAction action) ;
+    protected abstract boolean allowUpdate(HttpAction action) ;
+    protected abstract boolean allowREST_R(HttpAction action) ;
+    protected abstract boolean allowREST_W(HttpAction action) ;
+    protected abstract boolean allowQuadsR(HttpAction action) ;
+    protected abstract boolean allowQuadsW(HttpAction action) ;
+    
+    public static class ReadOnly extends SPARQL_UberServlet
+    {
+        public ReadOnly()    { super() ; }
+        @Override protected boolean allowQuery(HttpAction action)    { return true ; }
+        @Override protected boolean allowUpdate(HttpAction action)   { return false ; }
+        @Override protected boolean allowREST_R(HttpAction action)   { return true ; }
+        @Override protected boolean allowREST_W(HttpAction action)   { return false ; }
+        @Override protected boolean allowQuadsR(HttpAction action)   { return true ; }
+        @Override protected boolean allowQuadsW(HttpAction action)   { return false ; }
+    }
+
+    public static class ReadWrite extends SPARQL_UberServlet
+    {
+        public ReadWrite()    { super() ; }
+        @Override protected boolean allowQuery(HttpAction action)    { return true ; }
+        @Override protected boolean allowUpdate(HttpAction action)   { return true ; }
+        @Override protected boolean allowREST_R(HttpAction action)   { return true ; }
+        @Override protected boolean allowREST_W(HttpAction action)   { return true ; }
+        @Override protected boolean allowQuadsR(HttpAction action)   { return true ; }
+        @Override protected boolean allowQuadsW(HttpAction action)   { return true ; }
+    }
+
+    public static class AccessByConfig extends SPARQL_UberServlet
+    {
+        public AccessByConfig()    { super() ; }
+        @Override protected boolean allowQuery(HttpAction action)    { return isEnabled(action, OperationName.Query) ; }
+        @Override protected boolean allowUpdate(HttpAction action)   { return isEnabled(action, OperationName.Update) ; }
+        @Override protected boolean allowREST_R(HttpAction action)   { return isEnabled(action, OperationName.GSP_R) || isEnabled(action, OperationName.GSP) ; }
+        @Override protected boolean allowREST_W(HttpAction action)   { return isEnabled(action, OperationName.GSP) ; }
+        // Quad operations tied to presence/absence of GSP.
+        @Override protected boolean allowQuadsR(HttpAction action)   { return isEnabled(action, OperationName.GSP_R) ; }
+        @Override protected boolean allowQuadsW(HttpAction action)   { return isEnabled(action, OperationName.GSP) ; }
+
+        // Test whether there is a configuration that allows this action as the operation given.
+        // Ignores the operation in the action (set due to parsing - it might be "quads"
+        // which is the generic operation when just the dataset is specificed.  
+        private boolean isEnabled(HttpAction action, OperationName opName) {
+            // Disregard the operation name of the action
+            DataService dSrv = action.getDataService() ;
+            if ( dSrv == null )
+                return false;
+            return ! dSrv.getOperation(opName).isEmpty() ;
+        }
+    }
+    
+    /*  This can be used for a single servlet for everything (über-servlet)
+     *  
+     *  It can check for a request that looks like a service request and passes it on.
+     * This takes precedence over direct naming.
+     */
+    
+    // Refactor? Extract the direct naming handling.
+    // To test: enable in SPARQLServer.configureOneDataset
+    
+    private final ActionSPARQL queryServlet    = new SPARQL_QueryDataset() ;
+    private final ActionSPARQL updateServlet   = new SPARQL_Update() ;
+    private final ActionSPARQL uploadServlet   = new SPARQL_Upload() ;
+    private final ActionSPARQL gspServlet_R    = new SPARQL_GSP_R() ;
+    private final ActionSPARQL gspServlet_RW   = new SPARQL_GSP_RW() ;
+    private final ActionSPARQL restQuads_R     = new REST_Quads_R() ;
+    private final ActionSPARQL restQuads_RW    = new REST_Quads_RW() ;
+    
+    public SPARQL_UberServlet() { super(); }
+
+    private String getEPName(String dsname, List<String> endpoints) {
+        if (endpoints == null || endpoints.size() == 0) 
+            return null ;
+        String x = endpoints.get(0) ;
+        if ( ! dsname.endsWith("/") )
+            x = dsname+"/"+x ;
+        else
+            x = dsname+x ;
+        return x ;
+    }
+    
+    // These calls should not happen because we hook in at executeAction
+    @Override protected void validate(HttpAction action) { throw new FusekiException("Call to SPARQL_UberServlet.validate") ; }
+    @Override protected void perform(HttpAction action)  { throw new FusekiException("Call to SPARQL_UberServlet.perform") ; }
+
+    /** Map request to uri in the registry.
+     *  null means no mapping done 
+     */
+    @Override
+    protected String mapRequestToDataset(HttpAction action) {
+        String uri = ActionLib.removeContextPath(action) ;
+        return ActionLib.mapRequestToDatasetLongest$(uri) ;
+    }
+    
+    /** Intercept the processing cycle at the point where the action has been set up,
+     *  the dataset target decided but no validation or execution has been done, 
+     *  nor any stats have been done.
+     */
+    @Override
+    protected void executeAction(HttpAction action) {
+        long id = action.id ;
+        HttpServletRequest request = action.request ;
+        HttpServletResponse response = action.response ;
+        String actionURI = action.getActionURI() ;            // No context path
+        String method = request.getMethod() ;
+        
+        DataAccessPoint desc = action.getDataAccessPoint() ;
+        DataService dSrv = action.getDataService() ;
+
+//        if ( ! dSrv.isActive() )
+//            ServletOps.error(HttpSC.SERVICE_UNAVAILABLE_503, "Dataset not currently active");
+        
+        // Part after the DataAccessPoint (dataset) name.
+        String trailing = findTrailing(actionURI, desc.getName()) ;
+        String qs = request.getQueryString() ;
+
+        boolean hasParams = request.getParameterMap().size() > 0 ;
+        
+        // Test for parameters - includes HTML forms.
+        boolean hasParamQuery           = request.getParameter(HttpNames.paramQuery) != null ;
+        // Include old name "request="
+        boolean hasParamUpdate          = request.getParameter(HttpNames.paramUpdate) != null || request.getParameter(HttpNames.paramRequest) != null ;
+        boolean hasParamGraph           = request.getParameter(HttpNames.paramGraph) != null ;
+        boolean hasParamGraphDefault    = request.getParameter(HttpNames.paramGraphDefault) != null ;
+
+        String ct = request.getContentType() ;
+        String charset = request.getCharacterEncoding() ;
+        
+        MediaType mt = null ;
+        if ( ct != null )
+            mt = MediaType.create(ct, charset) ;
+        
+        if (action.log.isInfoEnabled() ) {
+            //String cxt = action.getContextPath() ;
+            action.log.info(format("[%d] %s %s :: '%s' :: %s ? %s", id, method, desc.getName(), trailing, (mt==null?"<none>":mt), (qs==null?"":qs))) ;
+        }
+                       
+        boolean hasTrailing = ( trailing.length() != 0 ) ;
+        
+        if ( !hasTrailing && !hasParams ) {
+            // Check enabled.  But no trailing here.
+            // if ( serviceDispatch(action, desc.readWriteGraphStore, trailing, restQuads_RW) ) return ;
+            // if ( serviceDispatch(action, desc.readGraphStore, trailing, restQuads_R) ) return ;
+            restQuads_RW.executeLifecycle(action) ;
+            return ;
+        }
+        
+        if ( !hasTrailing ) {
+            boolean isPOST = action.getRequest().getMethod().equals(HttpNames.METHOD_POST) ;
+            // Nothing after the DataAccessPoint i.e Dataset by name.
+            // e.g.  http://localhost:3030/ds?query=
+            // Query - GET or POST.
+            // Query - ?query= or body of application/sparql-query
+            if ( hasParamQuery || ( isPOST && contentTypeSPARQLQuery.equalsIgnoreCase(ct) ) ) {
+                // SPARQL Query
+                if ( !allowQuery(action) )
+                    ServletOps.errorForbidden("Forbidden: SPARQL query") ;
+                executeRequest(action, queryServlet) ;
+                return ;
+            }
+
+            // Insist on POST for update.
+            // Update - ?update= or body of application/sparql-update
+            if ( isPOST && ( hasParamUpdate || contentTypeSPARQLUpdate.equalsIgnoreCase(ct) ) ) {
+                // SPARQL Update
+                if ( !allowUpdate(action) )
+                    ServletOps.errorForbidden("Forbidden: SPARQL update") ;
+                executeRequest(action, updateServlet) ;
+                return ;
+            }
+            
+            // ?graph=, ?default
+            if ( hasParamGraph || hasParamGraphDefault ) {
+                doGraphStoreProtocol(action) ;
+                return ;
+            }
+            
+            ServletOps.errorBadRequest("Malformed request") ;
+            ServletOps.errorForbidden("Forbidden: SPARQL Graph Store Protocol : Read operation : "+method) ;
+        }
+        
+        final boolean checkForPossibleService = true ;
+        if ( checkForPossibleService && action.getEndpoint() != null ) {
+            // There is a trailing part.
+            // Check it's not the same name as a registered service.
+            // If so, dispatch to that service.
+            if ( serviceDispatch(action, OperationName.Query, queryServlet) ) return ; 
+            if ( serviceDispatch(action, OperationName.Update, updateServlet) ) return ; 
+            if ( serviceDispatch(action, OperationName.Upload, uploadServlet) ) return ;
+            if ( hasParams ) {
+                if ( serviceDispatch(action, OperationName.GSP_R, gspServlet_R) ) return ; 
+                if ( serviceDispatch(action, OperationName.GSP, gspServlet_RW) ) return ;
+            } else {
+                // No parameters - do as a quads operation on the dataset.
+                if ( serviceDispatch(action, OperationName.GSP_R, restQuads_R) ) return ;
+                if ( serviceDispatch(action, OperationName.GSP, restQuads_RW) ) return ;
+            }
+        }
+        // There is a trailing part - params are illegal by this point.
+        if ( hasParams )
+            // ?? Revisit to include query-on-one-graph 
+            //errorBadRequest("Can't invoke a query-string service on a direct named graph") ;
+            ServletOps.errorNotFound("Not found: dataset='"+printName(desc.getName())+"' service='"+printName(trailing)+"'");
+
+        // There is a trailing part - not a service, no params ==> GSP direct naming.
+        doGraphStoreProtocol(action) ;
+    }
+    
+    private String printName(String x) {
+        if ( x.startsWith("/") )
+            return x.substring(1) ;
+        return x ;
+    }
+    
+    private void doGraphStoreProtocol(HttpAction action) {
+        // The GSP servlets handle direct and indirect naming. 
+        Endpoint operation = action.getEndpoint() ;
+        String method = action.request.getMethod() ;
+
+        // Try to route to read service.
+
+        if ( HttpNames.METHOD_GET.equalsIgnoreCase(method) ||
+            HttpNames.METHOD_HEAD.equalsIgnoreCase(method) ) 
+        {
+            // Graphs Store Protocol, indirect naming, read operations
+            // Try to send to the R service, else drop through to RW service dispatch.
+            if ( allowREST_R(action)) 
+                ServletOps.errorForbidden("Forbidden: SPARQL Graph Store Protocol : Read operation : "+method) ;
+            executeRequest(action, gspServlet_R) ;
+            return ;
+        }
+
+        // Graphs Store Protocol, indirect naming, write (or read, though actually handled above)
+        // operations on the RW service.
+        if ( ! allowREST_W(action))
+            ServletOps.errorForbidden("Forbidden: SPARQL Graph Store Protocol : "+method) ;
+        executeRequest(action, gspServlet_RW) ;
+        return ;
+    }
+
+    private void executeRequest(HttpAction action, ActionSPARQL servlet) {
+        servlet.executeLifecycle(action) ;
+//      // Forwarded dispatch.
+//      try
+//      {
+//          String target = getEPName(desc.name, endpointList) ;
+//          if ( target == null )
+//              errorMethodNotAllowed(request.getMethod()) ;
+//          // ** relative servlet forward
+//          request.getRequestDispatcher(target).forward(request, response) ;    
+//          // ** absolute srvlet forward
+//          // getServletContext().getRequestDispatcher(target) ;
+//      } catch (Exception e) { errorOccurred(e) ; }        
+    }
+
+    protected static MediaType contentNegotationQuads(HttpAction action) {
+        MediaType mt = ConNeg.chooseContentType(action.request, DEF.quadsOffer, DEF.acceptNQuads) ;
+        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 ;
+    }
+
+    /** return true if dispatched 
+     * @param opName 
+     */
+    private boolean serviceDispatch(HttpAction action, OperationName opName, ActionSPARQL servlet) {
+        Endpoint operation = action.getEndpoint() ;
+        if ( operation == null )
+            return false ;
+        if ( ! operation.isType(opName) ) 
+            return false ;
+        servlet.executeLifecycle(action) ;
+        return true ;
+    }
+
+    /** Find part after the dataset name: service name or the graph (direct naming) */ 
+    protected String findTrailing(String uri, String dsname) {
+        if ( dsname.length() >= uri.length() )
+            return "" ;
+        return uri.substring(dsname.length()+1) ;   // Skip the separating "/"
+    }
+
+    // Route everything to "doCommon"
+    @Override
+    protected void doHead(HttpServletRequest request, HttpServletResponse response)
+    { doCommon(request, response) ; }
+    
+    @Override
+    protected void doGet(HttpServletRequest request, HttpServletResponse response)
+    { doCommon(request, response) ; }
+
+    @Override
+    protected void doPost(HttpServletRequest request, HttpServletResponse response)
+    { doCommon(request, response) ; }
+
+    @Override
+    protected void doOptions(HttpServletRequest request, HttpServletResponse response)
+    { doCommon(request, response) ; }
+    
+    @Override
+    protected void doPut(HttpServletRequest request, HttpServletResponse response)
+    { doCommon(request, response) ; }
+
+    @Override
+    protected void doDelete(HttpServletRequest request, HttpServletResponse response)
+    { doCommon(request, response) ; }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Update.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Update.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Update.java
new file mode 100644
index 0000000..ae87de0
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Update.java
@@ -0,0 +1,286 @@
+/*
+ * 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.server.CounterName.UpdateExecErrors ;
+import static org.apache.jena.riot.WebContent.charsetUTF8 ;
+import static org.apache.jena.riot.WebContent.contentTypeHTMLForm ;
+import static org.apache.jena.riot.WebContent.contentTypeSPARQLUpdate ;
+import static org.apache.jena.riot.WebContent.ctSPARQLUpdate ;
+import static org.apache.jena.riot.WebContent.isHtmlForm ;
+import static org.apache.jena.riot.WebContent.matchContentType ;
+import static org.apache.jena.riot.web.HttpNames.paramRequest ;
+import static org.apache.jena.riot.web.HttpNames.paramUpdate ;
+import static org.apache.jena.riot.web.HttpNames.paramUsingGraphURI ;
+import static org.apache.jena.riot.web.HttpNames.paramUsingNamedGraphURI ;
+
+import java.io.ByteArrayInputStream ;
+import java.io.IOException ;
+import java.io.InputStream ;
+import java.util.Arrays ;
+import java.util.Collection ;
+import java.util.Enumeration ;
+import java.util.List ;
+
+import javax.servlet.ServletException ;
+import javax.servlet.http.HttpServletRequest ;
+import javax.servlet.http.HttpServletResponse ;
+
+import org.apache.jena.atlas.io.IO ;
+import org.apache.jena.atlas.lib.StrUtils ;
+import org.apache.jena.atlas.web.ContentType ;
+import org.apache.jena.fuseki.Fuseki ;
+import org.apache.jena.fuseki.FusekiLib ;
+import org.apache.jena.iri.IRI ;
+import org.apache.jena.riot.system.IRIResolver ;
+import org.apache.jena.riot.web.HttpNames ;
+import org.apache.jena.web.HttpSC ;
+
+import com.hp.hpl.jena.graph.Node ;
+import com.hp.hpl.jena.graph.NodeFactory ;
+import com.hp.hpl.jena.query.QueryParseException ;
+import com.hp.hpl.jena.query.Syntax ;
+import com.hp.hpl.jena.sparql.modify.UsingList ;
+import com.hp.hpl.jena.update.UpdateAction ;
+import com.hp.hpl.jena.update.UpdateException ;
+import com.hp.hpl.jena.update.UpdateFactory ;
+import com.hp.hpl.jena.update.UpdateRequest ;
+
+public class SPARQL_Update extends SPARQL_Protocol 
+{
+    // Base URI used to isolate parsing from the current directory of the server. 
+    private static final String UpdateParseBase = Fuseki.BaseParserSPARQL ;
+    private static final IRIResolver resolver = IRIResolver.create(UpdateParseBase) ;
+    
+    public SPARQL_Update()
+    { super() ; }
+
+    @Override
+    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
+            throws ServletException, IOException {
+        response.sendError(HttpSC.BAD_REQUEST_400, "Attempt to perform SPARQL update by GET.  Use POST") ;
+    }
+
+    @Override
+    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
+            throws ServletException, IOException {
+        doCommon(request, response) ;
+    }
+
+    @Override
+    protected void doOptions(HttpServletRequest request, HttpServletResponse response) {
+        setCommonHeadersForOptions(response) ;
+        response.setHeader(HttpNames.hAllow, "OPTIONS,POST") ;
+        response.setHeader(HttpNames.hContentLengh, "0") ;
+    }
+
+    @Override
+    protected void perform(HttpAction action) {
+        ContentType ct = FusekiLib.getContentType(action) ;
+        if ( ct == null )
+            ct = ctSPARQLUpdate ;
+
+        if ( matchContentType(ctSPARQLUpdate, ct) ) {
+            executeBody(action) ;
+            return ;
+        }
+        if ( isHtmlForm(ct) ) {
+            executeForm(action) ;
+            return ;
+        }
+        ServletOps.error(HttpSC.UNSUPPORTED_MEDIA_TYPE_415, "Bad content type: " + action.request.getContentType()) ;
+    }
+
+    protected static List<String> paramsForm = Arrays.asList(paramRequest, paramUpdate, 
+                                                             paramUsingGraphURI, paramUsingNamedGraphURI) ;
+    protected static List<String> paramsPOST = Arrays.asList(paramUsingGraphURI, paramUsingNamedGraphURI) ;
+    
+    @Override
+    protected void validate(HttpAction action) {
+        HttpServletRequest request = action.request ;
+
+        if ( !HttpNames.METHOD_POST.equalsIgnoreCase(request.getMethod()) )
+            ServletOps.errorMethodNotAllowed("SPARQL Update : use POST") ;
+
+        ContentType ct = FusekiLib.getContentType(action) ;
+        if ( ct == null )
+            ct = ctSPARQLUpdate ;
+        // ----
+
+        if ( matchContentType(ctSPARQLUpdate, ct) ) {
+            String charset = request.getCharacterEncoding() ;
+            if ( charset != null && !charset.equalsIgnoreCase(charsetUTF8) )
+                ServletOps.errorBadRequest("Bad charset: " + charset) ;
+            validate(action, paramsPOST) ;
+            return ;
+        }
+
+        if ( isHtmlForm(ct) ) {
+            int x = countParamOccurences(request, paramUpdate) + countParamOccurences(request, paramRequest) ;
+            if ( x == 0 )
+                ServletOps.errorBadRequest("SPARQL Update: No 'update=' parameter") ;
+            if ( x != 1 )
+                ServletOps.errorBadRequest("SPARQL Update: Multiple 'update=' parameters") ;
+
+            String requestStr = request.getParameter(paramUpdate) ;
+            if ( requestStr == null )
+                requestStr = request.getParameter(paramRequest) ;
+            if ( requestStr == null )
+                ServletOps.errorBadRequest("SPARQL Update: No update= in HTML form") ;
+            validate(action, paramsForm) ;
+            return ;
+        }
+
+        ServletOps.error(HttpSC.UNSUPPORTED_MEDIA_TYPE_415, "Must be "+contentTypeSPARQLUpdate+" or "+contentTypeHTMLForm+" (got "+ct.getContentType()+")") ;
+    }
+    
+    protected void validate(HttpAction action, Collection<String> params) {
+        if ( params != null ) {
+            Enumeration<String> en = action.request.getParameterNames() ;
+            for ( ; en.hasMoreElements() ; ) {
+                String name = en.nextElement() ;
+                if ( !params.contains(name) )
+                    ServletOps.warning(action, "SPARQL Update: Unrecognized request parameter (ignored): "+name) ;
+            }
+        }
+    }
+
+    private void executeBody(HttpAction action) {
+        InputStream input = null ;
+        try { input = action.request.getInputStream() ; }
+        catch (IOException ex) { ServletOps.errorOccurred(ex) ; }
+
+        if ( action.verbose ) {
+            // Verbose mode only .... capture request for logging (does not scale). 
+            String requestStr = null ;
+            try { requestStr = IO.readWholeFileAsUTF8(input) ; }
+            catch (IOException ex) { IO.exception(ex) ; }
+            action.log.info(format("[%d] Update = %s", action.id, ServletOps.formatForLog(requestStr))) ;
+            
+            input = new ByteArrayInputStream(requestStr.getBytes());
+            requestStr = null;
+        }
+        
+        execute(action, input) ;
+        ServletOps.successNoContent(action) ;
+    }
+
+    private void executeForm(HttpAction action) {
+        String requestStr = action.request.getParameter(paramUpdate) ;
+        if ( requestStr == null )
+            requestStr = action.request.getParameter(paramRequest) ;
+        
+        if ( action.verbose )
+            action.log.info(format("[%d] Form update = \n%s", action.id, requestStr)) ;
+        // A little ugly because we are taking a copy of the string, but hopefully shouldn't be too big if we are in this code-path
+        // If we didn't want this additional copy, we could make the parser take a Reader in addition to an InputStream
+        byte[] b = StrUtils.asUTF8bytes(requestStr) ;
+        ByteArrayInputStream input = new ByteArrayInputStream(b);
+        requestStr = null;  // free it early at least
+        execute(action, input);
+        ServletOps.successPage(action,"Update succeeded") ;
+    }
+    
+    private void execute(HttpAction action, InputStream input) {
+        UsingList usingList = processProtocol(action.request) ;
+        
+        // If the dsg is transactional, then we can parse and execute the update in a streaming fashion.
+        // If it isn't, we need to read the entire update request before performing any updates, because
+        // we have to attempt to make the request atomic in the face of malformed queries
+        UpdateRequest req = null ;
+        if (!action.isTransactional()) { 
+            try {
+                // TODO implement a spill-to-disk version of this
+                req = UpdateFactory.read(usingList, input, UpdateParseBase, Syntax.syntaxARQ);
+            }
+            catch (UpdateException ex) { ServletOps.errorBadRequest(ex.getMessage()) ; return ; }
+            catch (QueryParseException ex) { ServletOps.errorBadRequest(messageForQPE(ex)) ; return ; }
+        }
+        
+        action.beginWrite() ;
+        try {
+            if (req == null )
+                UpdateAction.parseExecute(usingList, action.getActiveDSG(), input, UpdateParseBase, Syntax.syntaxARQ);
+            else
+                UpdateAction.execute(req, action.getActiveDSG()) ;
+            action.commit() ;
+        } catch (UpdateException ex) {
+            action.abort() ;
+            incCounter(action.getEndpoint().getCounters(), UpdateExecErrors) ;
+            ServletOps.errorOccurred(ex.getMessage()) ;
+        } catch (QueryParseException ex) {
+            action.abort() ;
+            // Counter inc'ed further out.
+            ServletOps.errorBadRequest(messageForQPE(ex)) ;
+        } catch (Throwable ex) {
+            if ( ! ( ex instanceof ActionErrorException ) )
+            {
+                try { action.abort() ; } catch (Exception ex2) {}
+                ServletOps.errorOccurred(ex.getMessage(), ex) ;
+            }
+        } finally { action.endWrite(); }
+    }
+
+    /* [It is an error to supply the using-graph-uri or using-named-graph-uri parameters 
+     * when using this protocol to convey a SPARQL 1.1 Update request that contains an 
+     * operation that uses the USING, USING NAMED, or WITH clause.]
+     * 
+     * We will simply capture any using parameters here and pass them to the parser, which will be
+     * responsible for throwing an UpdateException if the query violates the above requirement,
+     * and will also be responsible for adding the using parameters to update queries that can
+     * accept them.
+     */
+    private UsingList processProtocol(HttpServletRequest request) {
+        UsingList toReturn = new UsingList();
+        
+        String[] usingArgs = request.getParameterValues(paramUsingGraphURI) ;
+        String[] usingNamedArgs = request.getParameterValues(paramUsingNamedGraphURI) ;
+        if ( usingArgs == null && usingNamedArgs == null )
+            return toReturn;
+        if ( usingArgs == null )
+            usingArgs = new String[0] ;
+        if ( usingNamedArgs == null )
+            usingNamedArgs = new String[0] ;
+        // Impossible.
+//        if ( usingArgs.length == 0 && usingNamedArgs.length == 0 )
+//            return ;
+        
+        for ( String nodeUri : usingArgs ) {
+            toReturn.addUsing(createNode(nodeUri)) ;
+        }
+        for ( String nodeUri : usingNamedArgs ) {
+            toReturn.addUsingNamed(createNode(nodeUri)) ;
+        }
+
+        return toReturn ;
+    }
+    
+    private static Node createNode(String x) {
+        try {
+            IRI iri = resolver.resolve(x) ;
+            return NodeFactory.createURI(iri.toString()) ;
+        } catch (Exception ex)
+        {
+            ServletOps.errorBadRequest("SPARQL Update: bad IRI: "+x) ;
+            return null ;
+        }
+        
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Upload.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Upload.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Upload.java
new file mode 100644
index 0000000..458c924
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Upload.java
@@ -0,0 +1,291 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.servlets;
+
+import static java.lang.String.format ;
+
+import java.io.IOException ;
+import java.io.InputStream ;
+import java.io.PrintWriter ;
+import java.util.zip.GZIPInputStream ;
+
+import javax.servlet.ServletException ;
+import javax.servlet.http.HttpServletRequest ;
+import javax.servlet.http.HttpServletResponse ;
+
+import org.apache.commons.fileupload.FileItemIterator ;
+import org.apache.commons.fileupload.FileItemStream ;
+import org.apache.commons.fileupload.servlet.ServletFileUpload ;
+import org.apache.commons.fileupload.util.Streams ;
+import org.apache.jena.atlas.web.ContentType ;
+import org.apache.jena.fuseki.Fuseki ;
+import org.apache.jena.fuseki.FusekiLib ;
+import org.apache.jena.iri.IRI ;
+import org.apache.jena.riot.Lang ;
+import org.apache.jena.riot.RDFLanguages ;
+import org.apache.jena.riot.lang.StreamRDFCounting ;
+import org.apache.jena.riot.system.IRIResolver ;
+import org.apache.jena.riot.system.StreamRDF ;
+import org.apache.jena.riot.system.StreamRDFLib ;
+import org.apache.jena.riot.web.HttpNames ;
+import org.apache.jena.web.HttpSC ;
+
+import com.hp.hpl.jena.graph.Node ;
+import com.hp.hpl.jena.graph.NodeFactory ;
+import com.hp.hpl.jena.sparql.core.DatasetGraph ;
+import com.hp.hpl.jena.sparql.core.DatasetGraphFactory ;
+import com.hp.hpl.jena.sparql.core.Quad ;
+
+public class SPARQL_Upload extends ActionSPARQL 
+{
+    public SPARQL_Upload() {
+        super() ;
+    }
+
+    // Methods to respond to.
+    @Override
+    protected void doPost(HttpServletRequest request, HttpServletResponse response)
+    throws ServletException, IOException
+    {
+        doCommon(request, response) ;
+    }
+    
+    @Override
+    protected void doOptions(HttpServletRequest request, HttpServletResponse response)
+    {
+        setCommonHeaders(response);
+        setCommonHeadersForOptions(response) ;
+        response.setHeader(HttpNames.hAllow, "OPTIONS,POST");
+        response.setHeader(HttpNames.hContentLengh, "0") ;
+    }
+    
+    @Override
+    protected void perform(HttpAction action)
+    {
+        // Only allows one file in the upload.
+        boolean isMultipart = ServletFileUpload.isMultipartContent(action.request);
+        if ( ! isMultipart )
+            ServletOps.error(HttpSC.BAD_REQUEST_400 , "Not a file upload") ;
+        
+        long count = upload(action, Fuseki.BaseUpload) ;
+        ServletOps.success(action) ;
+        try {
+            action.response.setContentType("text/html") ;
+            action.response.setStatus(HttpSC.OK_200);
+            PrintWriter out = action.response.getWriter() ;
+            out.println("<html>") ;
+            out.println("<head>") ;
+            out.println("</head>") ;
+            out.println("<body>") ;
+            out.println("<h1>Success</h1>");
+            out.println("<p>") ;
+            out.println("Triples = "+count + "\n");
+            out.println("<p>") ;
+            out.println("</p>") ;
+            out.println("<button onclick=\"timeFunction()\">Back to Fuseki</button>");
+            out.println("</p>") ;
+            out.println("<script type=\"text/javascript\">");
+            out.println("function timeFunction(){");
+            out.println("window.location.href = \"/fuseki.html\";}");
+            out.println("</script>");
+            out.println("</body>") ;
+            out.println("</html>") ;
+            out.flush() ;
+            ServletOps.success(action) ;
+        }
+        catch (Exception ex) { ServletOps.errorOccurred(ex) ; }
+    }
+    
+    // Also used by SPARQL_REST
+    static public long upload(HttpAction action, String base)
+    {
+        if ( action.isTransactional() )
+            return uploadTxn(action, base) ;
+        else
+            return uploadNonTxn(action, base) ;
+    }
+
+    /** Non-transaction - buffer to a temporary graph so that parse errors
+     * are caught before inserting any data. 
+     */
+     private static long uploadNonTxn(HttpAction action, String base) {
+         UploadDetails upload = uploadWorker(action, base) ;
+         String graphName = upload.graphName ;
+         DatasetGraph dataTmp = upload.data ;
+         long count = upload.count ;
+         
+         if ( graphName == null )
+             action.log.info(format("[%d] Upload: %d Quads(s)",action.id, count)) ;
+         else
+             action.log.info(format("[%d] Upload: Graph: %s, %d triple(s)", action.id, graphName,  count)) ;
+
+         Node gn = null ;
+         if ( graphName != null ) {
+             gn = graphName.equals(HttpNames.valueDefault)
+                 ? Quad.defaultGraphNodeGenerated 
+                 : NodeFactory.createURI(graphName) ;
+         }
+
+         action.beginWrite() ;
+         try {
+             if ( gn != null )
+                 FusekiLib.addDataInto(dataTmp.getDefaultGraph(), action.getActiveDSG(), gn) ;
+             else
+                 FusekiLib.addDataInto(dataTmp, action.getActiveDSG()) ;
+
+             action.commit() ;
+             return count ;
+         } catch (RuntimeException ex)
+        {
+            // If anything went wrong, try to backout.
+            try { action.abort() ; } catch (Exception ex2) {}
+            ServletOps.errorOccurred(ex.getMessage()) ;
+            return -1 ;
+        } 
+        finally { action.endWrite() ; }
+    }
+
+     /** Transactional - we'd like data to go straight to the destination, with an abort on parse error.
+      * But file upload with a name means that the name can be after the data
+      * (it is in the Fuseki default pages).
+      * Use Graph Store protocol for bulk uploads.
+      * (It would be possible to process the incoming stream and see the graph name first.)
+      */
+      private static long uploadTxn(HttpAction action, String base) {
+          // We can't do better than the non-transaction approach.
+          return uploadNonTxn(action, base) ;
+      }
+     
+    static class UploadDetails {
+        final String graphName  ;
+        final DatasetGraph data ;
+        final long count ;
+        UploadDetails(String gn, DatasetGraph dsg, long parserCount) {
+            this.graphName = gn ;
+            this.data = dsg ;
+            this.count = parserCount ;
+        }
+    }
+      
+    /** Process an HTTP file upload of RDF with additiona name field for the graph name.
+     *  We can't stream straight into a dataset because the graph name can be after the data. 
+     *  @return graph name and count
+     */
+    
+    // ?? Combine with Upload.fileUploadWorker
+    // Difference is the handling of names for graphs.  
+    static private UploadDetails uploadWorker(HttpAction action, String base)
+    {
+        DatasetGraph dsgTmp = DatasetGraphFactory.createMem() ;
+        ServletFileUpload upload = new ServletFileUpload();
+        String graphName = null ;
+        boolean isQuads = false ;
+        long count = -1 ;
+        
+        String name = null ;  
+        ContentType ct = null ;
+        Lang lang = null ;
+
+        try {
+            FileItemIterator iter = upload.getItemIterator(action.request);
+            while (iter.hasNext()) {
+                FileItemStream item = iter.next();
+                String fieldName = item.getFieldName();
+                InputStream stream = item.openStream();
+                if (item.isFormField())
+                {
+                    // Graph name.
+                    String value = Streams.asString(stream, "UTF-8") ;
+                    if ( fieldName.equals(HttpNames.paramGraph) )
+                    {
+                        graphName = value ;
+                        if ( graphName != null && ! graphName.equals("") && ! graphName.equals(HttpNames.valueDefault) )
+                        {
+                            IRI iri = IRIResolver.parseIRI(value) ;
+                            if ( iri.hasViolation(false) )
+                                ServletOps.errorBadRequest("Bad IRI: "+graphName) ;
+                            if ( iri.getScheme() == null )
+                                ServletOps.errorBadRequest("Bad IRI: no IRI scheme name: "+graphName) ;
+                            if ( iri.getScheme().equalsIgnoreCase("http") || iri.getScheme().equalsIgnoreCase("https")) 
+                            {
+                                // Redundant??
+                                if ( iri.getRawHost() == null ) 
+                                    ServletOps.errorBadRequest("Bad IRI: no host name: "+graphName) ;
+                                if ( iri.getRawPath() == null || iri.getRawPath().length() == 0 )
+                                    ServletOps.errorBadRequest("Bad IRI: no path: "+graphName) ;
+                                if ( iri.getRawPath().charAt(0) != '/' )
+                                    ServletOps.errorBadRequest("Bad IRI: Path does not start '/': "+graphName) ;
+                            } 
+                        }
+                    }
+                    else if ( fieldName.equals(HttpNames.paramDefaultGraphURI) )
+                        graphName = null ;
+                    else
+                        // Add file type?
+                        action.log.info(format("[%d] Upload: Field=%s ignored", action.id, fieldName)) ;
+                } else {
+                    // Process the input stream
+                    name = item.getName() ; 
+                    if ( name == null || name.equals("") || name.equals("UNSET FILE NAME") ) 
+                        ServletOps.errorBadRequest("No name for content - can't determine RDF syntax") ;
+
+                    String contentTypeHeader = item.getContentType() ;
+                    ct = ContentType.create(contentTypeHeader) ;
+
+                    lang = RDFLanguages.contentTypeToLang(ct.getContentType()) ;
+                    if ( lang == null ) {
+                        lang = RDFLanguages.filenameToLang(name) ;
+
+                        //JENA-600 filenameToLang() strips off certain extensions such as .gz and 
+                        //we need to ensure that if there was a .gz extension present we wrap the stream accordingly
+                        if (name.endsWith(".gz") )
+                            stream = new GZIPInputStream(stream);
+                    }
+                    
+                    
+                    if ( lang == null )
+                        // Desperate.
+                        lang = RDFLanguages.RDFXML ;
+                    
+                    isQuads = RDFLanguages.isQuads(lang) ; 
+
+                    action.log.info(format("[%d] Upload: Filename: %s, Content-Type=%s, Charset=%s => %s", 
+                                    action.id, name,  ct.getContentType(), ct.getCharset(), lang.getName())) ;
+                    
+                    StreamRDF x = StreamRDFLib.dataset(dsgTmp) ;
+                    StreamRDFCounting dest =  StreamRDFLib.count(x) ;
+                    ActionSPARQL.parse(action, dest, stream, lang, base);
+                    count = dest.count() ;
+                }
+            }    
+
+            if ( graphName == null || graphName.equals("") ) 
+                graphName = HttpNames.valueDefault ;
+            if ( isQuads )
+                graphName = null ;
+            return new UploadDetails(graphName, dsgTmp, count) ;
+        }
+        catch (ActionErrorException ex) { throw ex ; }
+        catch (Exception ex)            { ServletOps.errorOccurred(ex) ; return null ; }
+    }            
+
+    @Override
+    protected void validate(HttpAction action)
+    {}
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ServletBase.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ServletBase.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ServletBase.java
new file mode 100644
index 0000000..a39cdd1
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ServletBase.java
@@ -0,0 +1,98 @@
+/*
+ * 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.concurrent.atomic.AtomicLong ;
+
+import javax.servlet.http.HttpServlet ;
+import javax.servlet.http.HttpServletRequest ;
+import javax.servlet.http.HttpServletResponse ;
+
+import org.apache.jena.atlas.lib.StrUtils ;
+import org.apache.jena.fuseki.Fuseki ;
+import org.apache.jena.riot.web.HttpNames ;
+
+/**
+ * Addition HTTP Servlet operations. 
+ */
+public abstract class ServletBase extends HttpServlet {
+    public static final String METHOD_DELETE    = "DELETE" ;
+    public static final String METHOD_HEAD      = "HEAD" ;
+    public static final String METHOD_GET       = "GET" ;
+    public static final String METHOD_OPTIONS   = "OPTIONS" ;
+    public static final String METHOD_POST      = "POST" ;
+    public static final String METHOD_PUT       = "PUT" ;
+    public static final String METHOD_TRACE     = "TRACE" ;
+    public static final String METHOD_PATCH     = "PATCH" ;
+    
+    private static AtomicLong     requestIdAlloc = new AtomicLong(0) ;
+
+    protected ServletBase() {}
+    
+    /**
+     * Helper method which gets a unique request ID and appends it as a header
+     * to the response
+     * 
+     * @param request
+     *            HTTP Request
+     * @param response
+     *            HTTP Response
+     * @return Request ID
+     */
+    protected static long allocRequestId(HttpServletRequest request, HttpServletResponse response) {
+        long id = requestIdAlloc.incrementAndGet() ;
+        addRequestId(response, id) ;
+        return id ;
+    }
+
+    /**
+     * Helper method for attaching a request ID to a response as a header
+     * 
+     * @param response
+     *            Response
+     * @param id
+     *            Request ID
+     */
+    protected static void addRequestId(HttpServletResponse response, long id) {
+        response.addHeader("Fuseki-Request-ID", Long.toString(id)) ;
+    }
+
+    static final String varyHeaderSetting = StrUtils.strjoin(",",
+         HttpNames.hAccept,
+         HttpNames.hAcceptEncoding,
+         HttpNames.hAcceptCharset) ;
+
+    public static void setVaryHeader(HttpServletResponse httpResponse) {
+        httpResponse.setHeader(HttpNames.hVary, varyHeaderSetting) ;
+    }
+
+    public static boolean CORS_ENABLED = false ;
+    
+    public static void setCommonHeadersForOptions(HttpServletResponse httpResponse) {
+        if ( CORS_ENABLED )
+            httpResponse.setHeader(HttpNames.hAccessControlAllowHeaders, "X-Requested-With, Content-Type, Authorization") ;
+        setCommonHeaders(httpResponse) ;
+    }
+
+    public static void setCommonHeaders(HttpServletResponse httpResponse) {
+        if ( CORS_ENABLED )
+            httpResponse.setHeader(HttpNames.hAccessControlAllowOrigin, "*") ;
+        httpResponse.setHeader(HttpNames.hServer, Fuseki.serverHttpName) ;
+    }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ServletOps.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ServletOps.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ServletOps.java
new file mode 100644
index 0000000..277bf47
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ServletOps.java
@@ -0,0 +1,209 @@
+/**
+ * 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.io.PrintWriter ;
+
+import javax.servlet.ServletOutputStream ;
+import javax.servlet.http.HttpServletResponse ;
+
+import org.apache.jena.atlas.json.JSON ;
+import org.apache.jena.atlas.json.JsonValue ;
+import org.apache.jena.fuseki.servlets.UploadDetails.PreState ;
+import org.apache.jena.riot.WebContent ;
+import org.apache.jena.riot.web.HttpNames ;
+import org.apache.jena.web.HttpSC ;
+
+public class ServletOps {
+
+    public static void responseSendError(HttpServletResponse response, int statusCode, String message) {
+        try {
+            response.sendError(statusCode, message) ;
+        } catch (IOException ex) {
+            errorOccurred(ex) ;
+        } catch (IllegalStateException ex) {}
+    }
+
+    public static void responseSendError(HttpServletResponse response, int statusCode) {
+        try {
+            response.sendError(statusCode) ;
+        } catch (IOException ex) {
+            errorOccurred(ex) ;
+        }
+    }
+
+    public static void successNoContent(HttpAction action) {
+        success(action, HttpSC.NO_CONTENT_204) ;
+    }
+
+    public static void success(HttpAction action) {
+        success(action, HttpSC.OK_200) ;
+    }
+
+    public static void successCreated(HttpAction action) {
+        success(action, HttpSC.CREATED_201) ;
+    }
+
+    // When 404 is no big deal e.g. HEAD
+    public static void successNotFound(HttpAction action) {
+        success(action, HttpSC.NOT_FOUND_404) ;
+    }
+
+    //
+    public static void success(HttpAction action, int httpStatusCode) {
+        action.response.setStatus(httpStatusCode) ;
+    }
+
+    public static void successPage(HttpAction action, String message) {
+        try {
+            action.response.setContentType("text/html") ;
+            action.response.setStatus(HttpSC.OK_200) ;
+            PrintWriter out = action.response.getWriter() ;
+            out.println("<html>") ;
+            out.println("<head>") ;
+            out.println("</head>") ;
+            out.println("<body>") ;
+            out.println("<h1>Success</h1>") ;
+            if ( message != null ) {
+                out.println("<p>") ;
+                out.println(message) ;
+                out.println("</p>") ;
+            }
+            out.println("</body>") ;
+            out.println("</html>") ;
+            out.flush() ;
+        } catch (IOException ex) {
+            errorOccurred(ex) ;
+        }
+    }
+
+    public static void warning(HttpAction action, String string) {
+        action.log.warn(string) ;
+    }
+
+    public static void warning(HttpAction action, String string, Throwable thorwable) {
+        action.log.warn(string, thorwable) ;
+    }
+
+    public static void errorBadRequest(String string) {
+        error(HttpSC.BAD_REQUEST_400, string) ;
+    }
+
+    public static void errorNotFound(String string) {
+        error(HttpSC.NOT_FOUND_404, string) ;
+    }
+
+    public static void errorNotImplemented(String msg) {
+        error(HttpSC.NOT_IMPLEMENTED_501, msg) ;
+    }
+
+    public static void errorMethodNotAllowed(String method) {
+        errorMethodNotAllowed(method, "HTTP method not allowed: " + method) ;
+    }
+
+    public static void errorMethodNotAllowed(String method, String msg) {
+        error(HttpSC.METHOD_NOT_ALLOWED_405, msg) ;
+    }
+
+    public static void errorForbidden() {
+        error(HttpSC.FORBIDDEN_403, "Forbidden") ;
+    }
+    
+    public static void errorForbidden(String msg) {
+        if ( msg != null )
+            error(HttpSC.FORBIDDEN_403, msg) ;
+        else
+            errorForbidden() ;
+    }
+
+    public static void error(int statusCode) {
+        throw new ActionErrorException(null, null, statusCode) ;
+    }
+
+    public static void error(int statusCode, String string) {
+        throw new ActionErrorException(null, string, statusCode) ;
+    }
+
+    public static void errorOccurred(String message) {
+        errorOccurred(message, null) ;
+    }
+
+    public static void errorOccurred(Throwable ex) {
+        errorOccurred(null, ex) ;
+    }
+
+    public static void errorOccurred(String message, Throwable ex) {
+        throw new ActionErrorException(ex, message, HttpSC.INTERNAL_SERVER_ERROR_500) ;
+    }
+
+    public static String formatForLog(String string) {
+        string = string.replace('\n', ' ') ;
+        string = string.replace('\r', ' ') ;
+        return string ;
+    }
+
+    public static void setNoCache(HttpAction action) {
+        setNoCache(action.response) ;
+    }
+
+    public static void setNoCache(HttpServletResponse response) {
+        response.setHeader(HttpNames.hCacheControl, "must-revalidate,no-cache,no-store");
+        response.setHeader(HttpNames.hPragma, "no-cache");
+    }
+    
+    /** Send a JSON value as a 200 response.  Null object means no response body and no content-type headers. */
+    public static void sendJsonReponse(HttpAction action, JsonValue v) {
+        if ( v == null ) {
+            ServletOps.success(action);
+            //ServletOps.successNoContent(action);
+            return ;
+        }
+        
+        ServletOps.success(action);
+        sendJson(action, v) ;
+    }
+
+    /** Send a JSON value as a 200 response.  Null object means no response body and no content-type headers. */
+    public static void sendJson(HttpAction action, JsonValue v) {
+        if ( v == null )
+            return ;
+        
+        try {
+            HttpServletResponse response = action.response ;
+            ServletOutputStream out = response.getOutputStream() ;
+            response.setContentType(WebContent.contentTypeJSON);
+            response.setCharacterEncoding(WebContent.charsetUTF8) ;
+            JSON.write(out, v) ;
+            out.println() ; 
+            out.flush() ;
+        } catch (IOException ex) { ServletOps.errorOccurred(ex) ; }
+    }
+
+    /** response to a upload operation of some kind. */ 
+    public static void uploadResponse(HttpAction action, UploadDetails details) {
+        if ( details.getExistedBefore().equals(PreState.ABSENT) )
+            ServletOps.successCreated(action) ; 
+        else
+            ServletOps.success(action) ; // successNoContent if empty body.
+        JsonValue v = details.detailsJson() ;
+        ServletOps.sendJson(action, v) ;
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/Upload.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/Upload.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/Upload.java
new file mode 100644
index 0000000..0db264d
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/Upload.java
@@ -0,0 +1,164 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.servlets;
+
+import static java.lang.String.format ;
+import static org.apache.jena.riot.WebContent.ctMultipartFormData ;
+import static org.apache.jena.riot.WebContent.ctTextPlain ;
+import static org.apache.jena.riot.WebContent.matchContentType ;
+
+import java.io.IOException ;
+import java.io.InputStream ;
+import java.util.zip.GZIPInputStream ;
+
+import org.apache.commons.fileupload.FileItemIterator ;
+import org.apache.commons.fileupload.FileItemStream ;
+import org.apache.commons.fileupload.servlet.ServletFileUpload ;
+import org.apache.commons.fileupload.util.Streams ;
+import org.apache.jena.atlas.io.IO ;
+import org.apache.jena.atlas.web.ContentType ;
+import org.apache.jena.fuseki.FusekiLib ;
+import org.apache.jena.riot.Lang ;
+import org.apache.jena.riot.RDFLanguages ;
+import org.apache.jena.riot.RiotParseException ;
+import org.apache.jena.riot.lang.StreamRDFCounting ;
+import org.apache.jena.riot.system.StreamRDF ;
+import org.apache.jena.riot.system.StreamRDFLib ;
+
+public class Upload {
+    public static UploadDetails incomingData(HttpAction action, StreamRDF dest) {
+        ContentType ct = FusekiLib.getContentType(action) ;
+        
+        if ( ct == null ) {
+            ServletOps.errorBadRequest("No content type") ;
+            return null ;
+        }
+         
+        if ( matchContentType(ctMultipartFormData, ct) ) {
+            return fileUploadWorker(action, dest) ;
+        }
+        // Single graph (or quads) in body.
+        
+        String base = ActionLib.wholeRequestURL(action.request) ;
+        Lang lang = RDFLanguages.contentTypeToLang(ct.getContentType()) ;
+        if ( lang == null ) {
+            ServletOps.errorBadRequest("Unknown content type for triples: " + ct) ;
+            return null ;
+        }
+        InputStream input = null ;
+        try { input = action.request.getInputStream() ; } 
+        catch (IOException ex) { IO.exception(ex) ; }
+    
+        int len = action.request.getContentLength() ;
+
+        StreamRDFCounting countingDest = StreamRDFLib.count(dest) ;
+        try {
+            ActionSPARQL.parse(action, countingDest, input, lang, base) ;
+            UploadDetails details = new UploadDetails(countingDest.count(), countingDest.countTriples(),countingDest.countQuads()) ;
+            action.log.info(format("[%d] Body: Content-Length=%d, Content-Type=%s, Charset=%s => %s : %s", 
+                                   action.id, len, ct.getContentType(), ct.getCharset(), lang.getName(),
+                                   details.detailsStr())) ;
+            return details ;
+        } catch (RiotParseException ex) {
+            action.log.info(format("[%d] Body: Content-Length=%d, Content-Type=%s, Charset=%s => %s : %s",
+                                   action.id, len, ct.getContentType(), ct.getCharset(), lang.getName(),
+                                   ex.getMessage())) ;
+            throw ex ;
+        }
+    }
+    
+    /**  Process an HTTP upload of RDF files (triples or quads)
+     *   Stream straight into a graph or dataset -- unlike SPARQL_Upload the destination
+     *   is known at the start of the multipart file body
+     */
+    
+    public static UploadDetails fileUploadWorker(HttpAction action, StreamRDF dest) {
+        String base = ActionLib.wholeRequestURL(action.request) ;
+        ServletFileUpload upload = new ServletFileUpload();
+        //log.info(format("[%d] Upload: Field=%s ignored", action.id, fieldName)) ;
+        
+        // Overall counting.
+        StreamRDFCounting countingDest =  StreamRDFLib.count(dest) ;
+        
+        try {
+            FileItemIterator iter = upload.getItemIterator(action.request);
+            while (iter.hasNext()) {
+                FileItemStream fileStream = iter.next();
+                if (fileStream.isFormField()) {
+                    // Ignore?
+                    String fieldName = fileStream.getFieldName() ;
+                    InputStream stream = fileStream.openStream();
+                    String value = Streams.asString(stream, "UTF-8") ;
+                    ServletOps.errorBadRequest(format("Only files accepted in multipart file upload (got %s=%s)",fieldName, value)) ;
+                }
+                //Ignore the field name.
+                //String fieldName = fileStream.getFieldName();
+    
+                InputStream stream = fileStream.openStream();
+                // Process the input stream
+                String contentTypeHeader = fileStream.getContentType() ;
+                ContentType ct = ContentType.create(contentTypeHeader) ;
+                Lang lang = null ;
+                if ( ! matchContentType(ctTextPlain, ct) )
+                    lang = RDFLanguages.contentTypeToLang(ct.getContentType()) ;
+    
+                if ( lang == null ) {
+                    String name = fileStream.getName() ; 
+                    if ( name == null || name.equals("") ) 
+                        ServletOps.errorBadRequest("No name for content - can't determine RDF syntax") ;
+                    lang = RDFLanguages.filenameToLang(name) ;
+                    if (name.endsWith(".gz"))
+                        stream = new GZIPInputStream(stream);
+                }
+                if ( lang == null )
+                    // Desperate.
+                    lang = RDFLanguages.RDFXML ;
+    
+                String printfilename = fileStream.getName() ; 
+                if ( printfilename == null  || printfilename.equals("") )
+                    printfilename = "<none>" ; 
+    
+                // Before
+                // action.log.info(format("[%d] Filename: %s, Content-Type=%s, Charset=%s => %s", 
+                //                        action.id, printfilename,  ct.getContentType(), ct.getCharset(), lang.getName())) ;
+                
+                // count just this step
+                StreamRDFCounting countingDest2 =  StreamRDFLib.count(countingDest) ;
+                try {
+                    ActionSPARQL.parse(action, countingDest2, stream, lang, base);
+                    UploadDetails details1 = new UploadDetails(countingDest2.count(), countingDest2.countTriples(),countingDest2.countQuads()) ;
+                    action.log.info(format("[%d] Filename: %s, Content-Type=%s, Charset=%s => %s : %s", 
+                                           action.id, printfilename,  ct.getContentType(), ct.getCharset(), lang.getName(),
+                                           details1.detailsStr())) ;
+                } catch (RiotParseException ex) {
+                    action.log.info(format("[%d] Filename: %s, Content-Type=%s, Charset=%s => %s : %s",
+                                           action.id, printfilename,  ct.getContentType(), ct.getCharset(), lang.getName(),
+                                           ex.getMessage())) ;
+                    throw ex ;
+                }
+            }
+        }
+        catch (ActionErrorException ex) { throw ex ; }
+        catch (Exception ex)            { ServletOps.errorOccurred(ex.getMessage()) ; }
+        // Overall results.
+        UploadDetails details = new UploadDetails(countingDest.count(), countingDest.countTriples(),countingDest.countQuads()) ;
+        return details ;
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/UploadDetails.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/UploadDetails.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/UploadDetails.java
new file mode 100644
index 0000000..a5c144b
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/UploadDetails.java
@@ -0,0 +1,86 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.servlets;
+
+import org.apache.jena.atlas.json.JsonBuilder ;
+import org.apache.jena.atlas.json.JsonValue ;
+
+/** Record of an upload */
+public class UploadDetails {
+    public enum PreState { EXISTED, ABSENT, UNKNOWN } 
+    
+    private final long count ;
+    private final long tripleCount ;
+    private final long quadCount ;
+    private PreState state = PreState.UNKNOWN ;
+    
+    /*package*/ UploadDetails(long parserCount, long parserTripleCount, long parserQuadCount) {
+        this.count = parserCount ;
+        this.tripleCount = parserTripleCount ;
+        this.quadCount = parserQuadCount ;
+    }
+    
+    public static String detailsStr(long count, long tripleCount, long quadCount) {
+        return String.format("Count=%d Triples=%d Quads=%d", count, tripleCount, quadCount) ;
+    }
+    
+    public String detailsStr() {
+        return detailsStr(count, tripleCount, quadCount) ;
+    }
+    
+    public static String jCount = "count" ; 
+    public static String jTriplesCount = "tripleCount" ; 
+    public static String jQuadsCount = "quadCount" ; 
+    
+    public static JsonValue detailsJson(long count, long tripleCount, long quadCount) {
+        JsonBuilder b = new JsonBuilder() ;
+        b.startObject("details") ;
+        b.key(jCount).value(count) ;
+        b.key(jTriplesCount).value(tripleCount) ;
+        b.key(jQuadsCount).value(quadCount) ;
+        b.finishObject("details") ;
+        return b.build() ;
+    }
+
+    public JsonValue detailsJson() {
+        return detailsJson(count, tripleCount, quadCount) ;
+    }
+
+    public long getCount() {
+        return count ;
+    }
+
+    public long getTripleCount() {
+        return tripleCount ;
+    }
+
+    public long getQuadCount() {
+        return quadCount ;
+    }
+
+    public void setExistedBefore(boolean existedBefore) {
+        if ( existedBefore )
+            setExistedBefore(PreState.EXISTED) ;
+        else
+            setExistedBefore(PreState.ABSENT) ;
+    }
+    public void setExistedBefore(PreState state) { this.state = state ; }
+    
+    public PreState getExistedBefore() { return state ; }
+}

http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/validation/DataValidator.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/validation/DataValidator.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/validation/DataValidator.java
new file mode 100644
index 0000000..055d798
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/validation/DataValidator.java
@@ -0,0 +1,131 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.validation;
+
+import static org.apache.jena.riot.SysRIOT.fmtMessage ;
+
+import java.io.StringReader ;
+import java.util.ArrayList ;
+import java.util.List ;
+
+import org.apache.jena.atlas.json.JsonBuilder ;
+import org.apache.jena.atlas.json.JsonObject ;
+import org.apache.jena.fuseki.servlets.ServletOps ;
+import org.apache.jena.riot.* ;
+import org.apache.jena.riot.system.ErrorHandler ;
+import org.apache.jena.riot.system.StreamRDF ;
+import org.apache.jena.riot.system.StreamRDFLib ;
+
+public class DataValidator extends ValidatorBaseJson {
+    public DataValidator() { }
+  
+    static final String jInput           = "input" ;
+
+    static final String paramFormat           = "outputFormat" ;
+    static final String paramIndirection      = "url" ;
+    static final String paramData             = "data" ;
+    static final String paramSyntax           = "languageSyntax" ;
+    @Override
+    protected JsonObject execute(ValidationAction action) {
+        JsonBuilder obj = new JsonBuilder() ;
+        obj.startObject() ;
+        
+        
+        String syntax = getArgOrNull(action, paramSyntax) ;
+        if ( syntax == null || syntax.equals("") )
+            syntax = RDFLanguages.NQUADS.getName() ;
+
+        Lang language = RDFLanguages.shortnameToLang(syntax) ;
+        if ( language == null ) {
+            ServletOps.errorBadRequest("Unknown syntax: " + syntax) ;
+            return null ;
+        }
+
+        String string = getArg(action, paramData) ;
+        StringReader sr = new StringReader(string) ;
+        obj.key(jInput).value(string) ;
+        StreamRDF dest = StreamRDFLib.sinkNull() ;
+        
+        try {
+            // Set error handler!
+            RDFDataMgr.parse(dest, sr, null, language) ;
+        } catch (RiotParseException ex) {
+            obj.key(jErrors) ;
+
+            obj.startArray() ;      // Errors array
+            obj.startObject() ;
+            obj.key(jParseError).value(ex.getMessage()) ;
+            obj.key(jParseErrorLine).value(ex.getLine()) ;
+            obj.key(jParseErrorCol).value(ex.getCol()) ;
+            obj.finishObject() ;
+            obj.finishArray() ;
+            
+            obj.finishObject() ; // Outer object
+            return obj.build().getAsObject() ;
+        } catch (RiotException ex) {
+            obj.key(jErrors) ;
+
+            obj.startArray() ;      // Errors array
+            obj.startObject() ;
+            obj.key(jParseError).value(ex.getMessage()) ;
+            obj.finishObject() ;
+            obj.finishArray() ;
+            
+            obj.finishObject() ; // Outer object
+            return obj.build().getAsObject() ;
+        }
+
+        
+        obj.finishObject() ;
+        return obj.build().getAsObject() ;
+    }
+    @Override
+    protected String validatorName() {
+        return "RDF Data" ;
+    }
+    
+    // Error handler that records messages
+    private static class ErrorHandlerMsg implements ErrorHandler
+    {
+        private List<String> messages = new ArrayList<>() ;
+
+        ErrorHandlerMsg(List<String> messages) { this.messages = messages; }
+        
+        @Override
+        public void warning(String message, long line, long col)
+        { output(message, line, col, "Warning", "warning") ; }
+    
+        // Attempt to continue.
+        @Override
+        public void error(String message, long line, long col)
+        { output(message, line, col, "Error", "error") ; }
+    
+        @Override
+        public void fatal(String message, long line, long col)
+        { output(message, line, col, "Fatal", "error") ; throw new RiotException(fmtMessage(message, line, col)) ; }
+        
+        private void output(String message, long line, long col, String typeName, String className)
+        {
+            String str = fmtMessage(message, line, col) ;
+            messages.add(str) ;
+        }
+    }
+
+}
+

http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/validation/IRIValidator.java
----------------------------------------------------------------------
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/validation/IRIValidator.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/validation/IRIValidator.java
new file mode 100644
index 0000000..3942115
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/validation/IRIValidator.java
@@ -0,0 +1,168 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.validation;
+
+import java.util.ArrayList ;
+import java.util.Iterator ;
+import java.util.List ;
+
+import org.apache.jena.atlas.json.JsonBuilder ;
+import org.apache.jena.atlas.json.JsonObject ;
+import org.apache.jena.fuseki.servlets.ServletOps ;
+import org.apache.jena.iri.IRI ;
+import org.apache.jena.iri.IRIFactory ;
+import org.apache.jena.iri.Violation ;
+import org.apache.jena.riot.system.IRIResolver ;
+
+public class IRIValidator extends ValidatorBaseJson {
+    public IRIValidator() { }
+    
+    static IRIFactory iriFactory = IRIResolver.iriFactory ;
+    
+    static final String paramIRI           = "iri" ;
+
+    // Output is an object  { "iris" : [ ] }
+    // { "iri": "" , "error": [], "warnings": [] }
+    static final String jIRIs    = "iris" ;
+    static final String jIRI     = "iri" ;
+
+    @Override
+    protected JsonObject execute(ValidationAction action) {
+        JsonBuilder obj = new JsonBuilder() ;
+        obj.startObject() ;
+        
+        String args[] = getArgs(action, paramIRI) ;
+        if ( args.length == 0 )
+            ServletOps.errorBadRequest("No IRIs supplied");
+        
+        obj.key(jIRIs) ;
+        obj.startArray() ;
+        
+        for ( String iriStr : args )
+        {
+            obj.startObject() ;
+            obj.key(jIRI).value(iriStr) ;
+
+            IRI iri = iriFactory.create(iriStr) ;
+
+
+            List<String> errors = new ArrayList<>() ;
+            List<String> warnings = new ArrayList<>() ;
+
+            if ( iri.isRelative() )
+                warnings.add("Relative IRI: "+iriStr) ;
+
+            Iterator<Violation> vIter = iri.violations(true) ;
+            for ( ; vIter.hasNext() ; )
+            {
+                Violation v = vIter.next() ;
+                String str = v.getShortMessage() ;
+                if ( v.isError() )
+                    errors.add(str) ;
+                else
+                    warnings.add(str) ;
+            }
+            
+            obj.key(jErrors) ;
+            obj.startArray() ;
+            for ( String msg : errors )
+                obj.value(msg) ;
+            obj.finishArray() ;
+                
+            obj.key(jWarnings) ;
+            obj.startArray() ;
+            for ( String msg : warnings )
+                obj.value(msg) ;
+            obj.finishArray() ;
+
+            obj.finishObject() ;
+        }
+          
+        
+       obj.finishArray() ;
+        
+        obj.finishObject() ;
+        return obj.build().getAsObject() ;
+    }
+    @Override
+    protected String validatorName() {
+        return "RDF Data" ;
+    }
+}
+
+//static final String paramIRI      = "iri" ;
+////static IRIFactory iriFactory = IRIFactory.iriImplementation() ;
+//static IRIFactory iriFactory = IRIResolver.iriFactory ;
+//
+//@Override
+//protected void execute(HttpServletRequest httpRequest, HttpServletResponse httpResponse)
+//{
+//    try {
+//        String[] args = httpRequest.getParameterValues(paramIRI) ;
+//        ServletOutputStream outStream = httpResponse.getOutputStream() ;
+//        PrintStream stdout = System.out ;
+//        PrintStream stderr = System.err ;
+//        System.setOut(new PrintStream(outStream)) ;
+//        System.setErr(new PrintStream(outStream)) ;
+//
+//        setHeaders(httpResponse) ;
+//
+//        outStream.println("<html>") ;
+//        printHead(outStream, "Jena IRI Validator Report") ;
+//        outStream.println("<body>") ;
+//
+//        outStream.println("<h1>IRI Report</h1>") ;
+//
+//        startFixed(outStream) ;
+//
+//        try {
+//            boolean first = true ;
+//            for ( String iriStr : args )
+//            {
+//                if ( ! first )
+//                    System.out.println() ;
+//                first = false ;
+//
+//                IRI iri = iriFactory.create(iriStr) ;
+//                System.out.println(iriStr + " ==> "+iri) ;
+//                if ( iri.isRelative() )
+//                    System.out.println("Relative IRI: "+iriStr) ;
+//
+//                Iterator<Violation> vIter = iri.violations(true) ;
+//                for ( ; vIter.hasNext() ; )
+//                {
+//                    String str = vIter.next().getShortMessage() ;
+//                    str = htmlQuote(str) ;
+//                    
+//                    System.out.println(str) ;
+//                }
+//            }
+//        } finally 
+//        {
+//            finishFixed(outStream) ;
+//            System.out.flush() ;
+//            System.err.flush() ;
+//            System.setOut(stdout) ;
+//            System.setErr(stdout) ;
+//        }
+//
+//        outStream.println("</body>") ;
+//        outStream.println("</html>") ;
+//    } catch (IOException ex) {}
+//}