You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by rv...@apache.org on 2015/01/14 11:31:44 UTC
[42/93] [abbrv] [partial] jena git commit: Maven modules for Fuseki2
http://git-wip-us.apache.org/repos/asf/jena/blob/470ee4d7/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/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) {}
+//}