http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Update.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Update.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Update.java new file mode 100644 index 0000000..f16f634 --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Update.java @@ -0,0 +1,308 @@ +/* + * 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.Fuseki.requestLog ; +import static org.apache.jena.fuseki.HttpNames.paramRequest ; +import static org.apache.jena.fuseki.HttpNames.paramUpdate ; +import static org.apache.jena.fuseki.HttpNames.paramUsingGraphURI ; +import static org.apache.jena.fuseki.HttpNames.paramUsingNamedGraphURI ; +import static org.apache.jena.fuseki.server.CounterName.UpdateExecErrors ; + +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.FusekiLib ; +import org.apache.jena.fuseki.HttpNames ; +import org.apache.jena.iri.IRI ; +import org.apache.jena.riot.WebContent ; +import org.apache.jena.riot.system.IRIResolver ; +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 = "http://example/update-base/" ; + 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) + { + // WebContent needs to migrate to using ContentType. + String ctStr ; + { + ContentType ct = FusekiLib.getContentType(action) ; + if ( ct == null ) + ctStr = WebContent.contentTypeSPARQLUpdate ; + else + ctStr = ct.getContentType() ; + } + + if (WebContent.contentTypeSPARQLUpdate.equals(ctStr)) + { + executeBody(action) ; + return ; + } + if (WebContent.contentTypeHTMLForm.equals(ctStr)) + { + executeForm(action) ; + return ; + } + 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()) ) + errorMethodNotAllowed("SPARQL Update : use POST") ; + + ContentType incoming = FusekiLib.getContentType(action) ; + String ctStr = ( incoming == null ) ? WebContent.contentTypeSPARQLUpdate : incoming.getContentType() ; + // ---- + + if ( WebContent.contentTypeSPARQLUpdate.equals(ctStr) ) + { + String charset = request.getCharacterEncoding() ; + if ( charset != null && ! charset.equalsIgnoreCase(WebContent.charsetUTF8) ) + errorBadRequest("Bad charset: "+charset) ; + validate(request, paramsPOST) ; + return ; + } + + if ( WebContent.contentTypeHTMLForm.equals(ctStr) ) + { + int x = countParamOccurences(request, paramUpdate) + countParamOccurences(request, paramRequest) ; + if ( x == 0 ) + errorBadRequest("SPARQL Update: No 'update=' parameter") ; + if ( x != 1 ) + errorBadRequest("SPARQL Update: Multiple 'update=' parameters") ; + + String requestStr = request.getParameter(paramUpdate) ; + if ( requestStr == null ) + requestStr = request.getParameter(paramRequest) ; + if ( requestStr == null ) + errorBadRequest("SPARQL Update: No update= in HTML form") ; + validate(request, paramsForm) ; + return ; + } + + error(HttpSC.UNSUPPORTED_MEDIA_TYPE_415, "Must be "+WebContent.contentTypeSPARQLUpdate+" or "+WebContent.contentTypeHTMLForm+" (got "+ctStr+")") ; + } + + protected void validate(HttpServletRequest request, Collection<String> params) + { + if ( params != null ) + { + Enumeration<String> en = request.getParameterNames() ; + for ( ; en.hasMoreElements() ; ) + { + String name = en.nextElement() ; + if ( ! params.contains(name) ) + warning("SPARQL Update: Unrecognize request parameter (ignored): "+name) ; + } + } + } + + private void executeBody(HttpAction action) + { + InputStream input = null ; + try { input = action.request.getInputStream() ; } + catch (IOException ex) { 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) ; } + requestLog.info(format("[%d] Update = %s", action.id, formatForLog(requestStr))) ; + + input = new ByteArrayInputStream(requestStr.getBytes()); + requestStr = null; + } + + execute(action, input) ; + successNoContent(action) ; + } + + private void executeForm(HttpAction action) + { + String requestStr = action.request.getParameter(paramUpdate) ; + if ( requestStr == null ) + requestStr = action.request.getParameter(paramRequest) ; + + if ( action.verbose ) + //requestLog.info(format("[%d] Form update = %s", action.id, formatForLog(requestStr))) ; + requestLog.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); + 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) { errorBadRequest(ex.getMessage()) ; return ; } + catch (QueryParseException ex) { 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.srvRef, UpdateExecErrors) ; + errorOccurred(ex.getMessage()) ; + } catch (QueryParseException ex) { + action.abort() ; + // Counter inc'ed further out. + errorBadRequest(messageForQPE(ex)) ; + } catch (Throwable ex) { + if ( ! ( ex instanceof ActionErrorException ) ) + { + try { action.abort() ; } catch (Exception ex2) {} + 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) + { + errorBadRequest("SPARQL Update: bad IRI: "+x) ; + return null ; + } + + } +}
http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Upload.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Upload.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Upload.java new file mode 100644 index 0000000..d21855f --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Upload.java @@ -0,0 +1,260 @@ +/* + * 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.lib.Pair ; +import org.apache.jena.atlas.web.ContentType ; +import org.apache.jena.fuseki.FusekiLib ; +import org.apache.jena.fuseki.HttpNames ; +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.* ; +import org.apache.jena.web.HttpSC ; + +import com.hp.hpl.jena.graph.Graph ; +import com.hp.hpl.jena.graph.Node ; +import com.hp.hpl.jena.graph.NodeFactory ; +import com.hp.hpl.jena.sparql.core.Quad ; +import com.hp.hpl.jena.sparql.graph.GraphFactory ; + +public class SPARQL_Upload extends SPARQL_ServletBase +{ + private static ErrorHandler errorHandler = ErrorHandlerFactory.errorHandlerStd(log) ; + + 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) + { + 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 ) + error(HttpSC.BAD_REQUEST_400 , "Not a file upload") ; + long count = upload(action, "http://example/upload-base/") ; + 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() ; + success(action) ; + } + catch (Exception ex) { 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) { + Pair<String, Graph> p = uploadWorker(action, base) ; + String graphName = p.getLeft() ; + Graph graphTmp = p.getRight() ; + long tripleCount = graphTmp.size() ; + + log.info(format("[%d] Upload: Graph: %s (%d triple(s))", + action.id, graphName, tripleCount)) ; + + Node gn = graphName.equals(HttpNames.valueDefault) + ? Quad.defaultGraphNodeGenerated + : NodeFactory.createURI(graphName) ; + + action.beginWrite() ; + try { + FusekiLib.addDataInto(graphTmp, action.getActiveDSG(), gn) ; + action.commit() ; + return tripleCount ; + } catch (RuntimeException ex) + { + // If anything went wrong, try to backout. + try { action.abort() ; } catch (Exception ex2) {} + 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) ; + } + + /** process an HTTP upload of RDF. + * We can't stream straight into a dataset because the graph name can be after the data. + * @return graph name and count + */ + + static private Pair<String, Graph> uploadWorker(HttpAction action, String base) + { + Graph graphTmp = GraphFactory.createDefaultGraph() ; + ServletFileUpload upload = new ServletFileUpload(); + String graphName = null ; + 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) ) + errorBadRequest("Bad IRI: "+graphName) ; + if ( iri.getScheme() == null ) + errorBadRequest("Bad IRI: no IRI scheme name: "+graphName) ; + if ( iri.getScheme().equalsIgnoreCase("http") || iri.getScheme().equalsIgnoreCase("https")) + { + // Redundant?? + if ( iri.getRawHost() == null ) + errorBadRequest("Bad IRI: no host name: "+graphName) ; + if ( iri.getRawPath() == null || iri.getRawPath().length() == 0 ) + errorBadRequest("Bad IRI: no path: "+graphName) ; + if ( iri.getRawPath().charAt(0) != '/' ) + errorBadRequest("Bad IRI: Path does not start '/': "+graphName) ; + } + } + } + else if ( fieldName.equals(HttpNames.paramDefaultGraphURI) ) + graphName = null ; + else + // Add file type? + 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") ) + 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 ; + + 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.graph(graphTmp) ; + StreamRDFCounting dest = StreamRDFLib.count(x) ; + SPARQL_REST.parse(action, dest, stream, lang, base); + count = dest.count() ; + } + } + + if ( graphName == null || graphName.equals("") ) + graphName = HttpNames.valueDefault ; + return Pair.create(graphName, graphTmp) ; + } + catch (ActionErrorException ex) { throw ex ; } + catch (Exception ex) { errorOccurred(ex) ; return null ; } + } + + @Override + protected void validate(HttpAction action) + {} +} http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/ServletBase.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/ServletBase.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/ServletBase.java new file mode 100644 index 0000000..7a74fa9 --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/ServletBase.java @@ -0,0 +1,242 @@ +/* + * 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 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.fuseki.HttpNames ; +import org.apache.jena.web.HttpSC ; +import org.slf4j.Logger ; + +/** + * An abstract HTTP Servlet. Contains implementation methods for setting the request status in a HTTP Action, + * and a mechanism to allocate unique ID's to new requests. + */ +public abstract class ServletBase extends HttpServlet +{ + protected static final Logger log = Fuseki.requestLog ; + public final boolean verboseLogging = Fuseki.verboseLogging ; + 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 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 void addRequestId(HttpServletResponse response, long id) { + response.addHeader("Fuseki-Request-ID", Long.toString(id)); + } + + protected void responseSendError(HttpServletResponse response, int statusCode, String message) + { + try { response.sendError(statusCode, message) ; } + catch (IOException ex) { errorOccurred(ex) ; } + catch (IllegalStateException ex) { } + } + + protected void responseSendError(HttpServletResponse response, int statusCode) + { + try { response.sendError(statusCode) ; } + catch (IOException ex) { errorOccurred(ex) ; } + } + + /** + * Returns the HTTP request URL, appended with any additional URL parameters used. + * + * @param request HTTP request + * @return complete request URL + */ + protected static String wholeRequestURL(HttpServletRequest request) + { + StringBuffer sb = request.getRequestURL() ; + String queryString = request.getQueryString() ; + if ( queryString != null ) + { + sb.append("?") ; + sb.append(queryString) ; + } + return sb.toString() ; + } + + protected static void successNoContent(HttpAction action) + { + success(action, HttpSC.NO_CONTENT_204); + } + + protected static void success(HttpAction action) + { + success(action, HttpSC.OK_200); + } + + protected static void successCreated(HttpAction action) + { + success(action, HttpSC.CREATED_201); + } + + // When 404 is no big deal e.g. HEAD + protected static void successNotFound(HttpAction action) + { + success(action, HttpSC.NOT_FOUND_404) ; + } + + // + protected static void success(HttpAction action, int httpStatusCode) + { + action.response.setStatus(httpStatusCode); + } + + protected 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) ; } + } + + protected static void warning(String string) + { + log.warn(string) ; + } + + protected static void warning(String string, Throwable thorwable) + { + log.warn(string, thorwable) ; + } + + protected static void errorBadRequest(String string) + { + error(HttpSC.BAD_REQUEST_400, string) ; + } + + protected static void errorNotFound(String string) + { + error(HttpSC.NOT_FOUND_404, string) ; + } + + protected static void errorNotImplemented(String msg) + { + error(HttpSC.NOT_IMPLEMENTED_501, msg) ; + } + + protected static void errorMethodNotAllowed(String method) + { + error(HttpSC.METHOD_NOT_ALLOWED_405, "HTTP method not allowed: "+method) ; + } + + protected static void errorForbidden(String msg) + { + if ( msg != null ) + error(HttpSC.FORBIDDEN_403, msg) ; + else + error(HttpSC.FORBIDDEN_403, "Forbidden") ; + } + + protected static void error(int statusCode) + { + throw new ActionErrorException(null, null, statusCode) ; + } + + + protected static void error(int statusCode, String string) + { + throw new ActionErrorException(null, string, statusCode) ; + } + + protected static void errorOccurred(String message) + { + errorOccurred(message, null) ; + } + + protected static void errorOccurred(Throwable ex) + { + errorOccurred(null, ex) ; + } + + protected static void errorOccurred(String message, Throwable ex) + { + throw new ActionErrorException(ex, message, HttpSC.INTERNAL_SERVER_ERROR_500) ; + } + + protected static String formatForLog(String string) + { + string = string.replace('\n', ' ') ; + string = string.replace('\r', ' ') ; + return string ; + } + + static String varyHeaderSetting = + StrUtils.strjoin(",", + HttpNames.hAccept, + HttpNames.hAcceptEncoding, + HttpNames.hAcceptCharset ) ; + + public static void setVaryHeader(HttpServletResponse httpResponse) + { + httpResponse.setHeader(HttpNames.hVary, varyHeaderSetting) ; + } + + public static void setCommonHeadersForOptions(HttpServletResponse httpResponse) + { + httpResponse.setHeader(HttpNames.hAccessControlAllowHeaders, "X-Requested-With, Content-Type, Authorization") ; + setCommonHeaders(httpResponse) ; + } + + public static void setCommonHeaders(HttpServletResponse httpResponse) + { + httpResponse.setHeader(HttpNames.hAccessControlAllowOrigin, "*") ; + httpResponse.setHeader(HttpNames.hServer, Fuseki.serverHttpName) ; + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/SimpleVelocity.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/SimpleVelocity.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/SimpleVelocity.java new file mode 100644 index 0000000..7f5cad9 --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/SimpleVelocity.java @@ -0,0 +1,78 @@ +/** + * 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.Writer ; +import java.util.Map ; + +import org.apache.velocity.Template ; +import org.apache.velocity.VelocityContext ; +import org.apache.velocity.app.VelocityEngine ; +import org.apache.velocity.exception.MethodInvocationException ; +import org.apache.velocity.exception.ParseErrorException ; +import org.apache.velocity.exception.ResourceNotFoundException ; +import org.apache.velocity.runtime.RuntimeConstants ; +import org.apache.velocity.runtime.log.LogChute ; +import org.apache.velocity.runtime.log.NullLogChute ; +import org.slf4j.Logger ; +import org.slf4j.LoggerFactory ; + +public class SimpleVelocity +{ + private static LogChute velocityLogChute = new NullLogChute() ; + private static Logger velocityLog = LoggerFactory.getLogger("Velocity"); + + /** Process a template */ + public static void process(String base, String path, Writer out, Map<String, Object> params) + { + process(base, path, out, createContext(params)) ; + } + + /** Process a template */ + public static void process(String base, String path, Writer out, VelocityContext context) + { + VelocityEngine velocity = new VelocityEngine() ; + // Turn off logging - catch exceptions and log ourselves + velocity.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, velocityLogChute) ; + velocity.setProperty(RuntimeConstants.INPUT_ENCODING, "UTF-8") ; + velocity.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, base) ; + velocity.init() ; + try { + Template temp = velocity.getTemplate(path) ; + temp.merge(context, out) ; + out.flush(); + } + catch (ResourceNotFoundException ex) { velocityLog.error("Resource not found: "+ex.getMessage()) ; } + catch (ParseErrorException ex) { velocityLog.error("Parse error ("+path+") : "+ex.getMessage()) ; } + catch (MethodInvocationException ex) { velocityLog.error("Method invocation exception ("+path+") : "+ex.getMessage()) ; } + catch (IOException ex) { velocityLog.warn("IOException", ex) ; } + } + + public static VelocityContext createContext(Map<String, Object> params) + { + // Velocity requires a mutable map. + // Scala leads to immutable maps ... be safe and copy. + VelocityContext context = new VelocityContext() ; + for ( Map.Entry<String, Object> e : params.entrySet() ) + context.put(e.getKey(), e.getValue()) ; + return context ; + } + +} http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/SimpleVelocityServlet.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/SimpleVelocityServlet.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/SimpleVelocityServlet.java new file mode 100644 index 0000000..8773eba --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/servlets/SimpleVelocityServlet.java @@ -0,0 +1,178 @@ +/** + * 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.Writer ; +import java.util.Map ; + +import javax.servlet.http.HttpServlet ; +import javax.servlet.http.HttpServletRequest ; +import javax.servlet.http.HttpServletResponse ; + +import org.apache.velocity.VelocityContext ; +import org.apache.velocity.app.VelocityEngine ; +import org.apache.velocity.runtime.RuntimeConstants ; +import org.apache.velocity.runtime.RuntimeServices ; +import org.apache.velocity.runtime.log.LogChute ; +import org.apache.velocity.runtime.log.NullLogChute ; +import org.slf4j.Logger ; +import org.slf4j.LoggerFactory ; + + +/** Simple servlet that uses <a href="http://velocity.apache.org/">Velocity</a> + * to format pages. It isolates the use of velocity by taking a configuration map. + * Use with a servlet mapping of "*.vm" or some such extension. + */ +public class SimpleVelocityServlet extends HttpServlet +{ + //private static Logger log = LoggerFactory.getLogger(SimpleVelocityServlet.class) ; + /* Velocity logging + * Instead of internal velocity logging, we catch the exceptions, + * log the message ourselves. This gives a celaner log file without + * loosing information that the application could use. + */ + + private static Logger vlog = LoggerFactory.getLogger("Velocity") ; + private static LogChute velocityLog = new NullLogChute() ; + //private static LogChute velocityLog = new SimpleSLF4JLogChute(vlog) ; + + private String docbase ; + private VelocityEngine velocity ; + private String functionsName = null ; + private final Map<String, Object> datamodel ; + + public SimpleVelocityServlet(String base, Map<String, Object> datamodel) + { + this.docbase = base ; + this.datamodel = datamodel ; + velocity = new VelocityEngine(); + // Turn off logging - catch exceptions and log ourselves + velocity.setProperty( RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, velocityLog) ; + velocity.setProperty( RuntimeConstants.INPUT_ENCODING, "UTF-8" ) ; + velocity.setProperty( RuntimeConstants.FILE_RESOURCE_LOADER_PATH, base) ; + velocity.init(); + } + + // See also + @Override + public void doGet(HttpServletRequest req, HttpServletResponse resp) + { + process(req, resp) ; + } + + @Override + public void doPost(HttpServletRequest req, HttpServletResponse resp) + { + process(req, resp) ; + } + + private void process(HttpServletRequest req, HttpServletResponse resp) + { + try + { + resp.setContentType("text/html") ; + resp.setCharacterEncoding("UTF-8") ; + Writer out = resp.getWriter() ; + String path = path(req) ; + VelocityContext vc = SimpleVelocity.createContext(datamodel) ; + vc.put("request", req) ; + SimpleVelocity.process(docbase, path, out, vc) ; + } catch (IOException ex) + { + vlog.warn("IOException", ex) ; + } + } + + private String path(HttpServletRequest request) + { + String path = request.getPathInfo(); + if (path != null) return path; + path = request.getServletPath(); + if (path != null) return path; + return null ; + } + + @Override + public String getServletInfo() + { + return "Lightweight Velocity Servlet"; + } + + /** Velocity logger to SLF4J */ + static class SimpleSLF4JLogChute implements LogChute + { + // Uusally for debugging only. + private Logger logger ; + + SimpleSLF4JLogChute( Logger log ) + { + this.logger = log ; + } + + @Override + public void init(RuntimeServices rs) throws Exception + { } + + @Override + public void log(int level, String message) + { + if ( logger == null ) return ; + switch(level) + { + case LogChute.TRACE_ID : logger.trace(message) ; return ; + case LogChute.DEBUG_ID : logger.debug(message) ; return ; + case LogChute.INFO_ID : logger.info(message) ; return ; + case LogChute.WARN_ID : logger.warn(message) ; return ; + case LogChute.ERROR_ID : logger.error(message) ; return ; + } + } + + @Override + public void log(int level, String message, Throwable t) + { + if ( logger == null ) return ; + // Forget the stack trace - velcoity internal - long - unhelpful to application. + t = null ; + switch (level) + { + case LogChute.TRACE_ID : logger.trace(message, t) ; return ; + case LogChute.DEBUG_ID : logger.debug(message, t) ; return ; + case LogChute.INFO_ID : logger.info(message, t) ; return ; + case LogChute.WARN_ID : logger.warn(message, t) ; return ; + case LogChute.ERROR_ID : logger.error(message, t) ; return ; + } + } + + @Override + public boolean isLevelEnabled(int level) + { + switch(level) + { + case LogChute.TRACE_ID: return logger.isTraceEnabled() ; + case LogChute.DEBUG_ID: return logger.isDebugEnabled() ; + case LogChute.INFO_ID: return logger.isInfoEnabled() ; + case LogChute.WARN_ID: return logger.isWarnEnabled() ; + case LogChute.ERROR_ID: return logger.isErrorEnabled() ; + } + return true ; + } + } +} + http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/validation/DataValidator.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/validation/DataValidator.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/validation/DataValidator.java new file mode 100644 index 0000000..6105562 --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/validation/DataValidator.java @@ -0,0 +1,237 @@ +/* + * 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.IOException ; +import java.io.PrintStream ; +import java.io.Reader ; +import java.io.StringReader ; + +import javax.servlet.ServletOutputStream ; +import javax.servlet.http.HttpServletRequest ; +import javax.servlet.http.HttpServletResponse ; + +import org.apache.jena.atlas.io.IO ; +import org.apache.jena.atlas.lib.Sink ; +import org.apache.jena.fuseki.FusekiLib ; +import org.apache.jena.riot.Lang ; +import org.apache.jena.riot.RDFLanguages ; +import org.apache.jena.riot.RiotException ; +import org.apache.jena.riot.RiotReader ; +import org.apache.jena.riot.lang.LangRIOT ; +import org.apache.jena.riot.system.ErrorHandler ; +import org.apache.jena.riot.system.RiotLib ; +import org.apache.jena.riot.system.StreamRDF ; +import org.apache.jena.riot.system.StreamRDFLib ; +import org.apache.jena.riot.tokens.Tokenizer ; +import org.apache.jena.riot.tokens.TokenizerFactory ; + +import com.hp.hpl.jena.graph.Node ; +import com.hp.hpl.jena.sparql.core.Quad ; +import com.hp.hpl.jena.sparql.serializer.SerializationContext ; +import com.hp.hpl.jena.sparql.util.FmtUtils ; + +public class DataValidator extends ValidatorBase +{ + public DataValidator() + { } + + static final String paramLineNumbers = "linenumbers" ; + static final String paramFormat = "outputFormat" ; + static final String paramIndirection = "url" ; + static final String paramData = "data" ; + static final String paramSyntax = "languageSyntax" ; + //static final String paramSyntaxExtended = "extendedSyntax" ; + + @Override + protected void execute(HttpServletRequest httpRequest, HttpServletResponse httpResponse) + { + try { +// if ( log.isInfoEnabled() ) +// log.info("data validation request") ; + + Tokenizer tokenizer = createTokenizer(httpRequest, httpResponse) ; + if ( tokenizer == null ) + return ; + + String syntax = FusekiLib.safeParameter(httpRequest, paramSyntax) ; + if ( syntax == null || syntax.equals("") ) + syntax = RDFLanguages.NQUADS.getName() ; + + Lang language = RDFLanguages.shortnameToLang(syntax) ; + if ( language == null ) + { + httpResponse.sendError(HttpServletResponse.SC_BAD_REQUEST, "Unknown syntax: "+syntax) ; + return ; + } + + ServletOutputStream outStream = httpResponse.getOutputStream() ; + ErrorHandlerMsg errorHandler = new ErrorHandlerMsg(outStream) ; + + PrintStream stdout = System.out ; + PrintStream stderr = System.err ; + System.setOut(new PrintStream(outStream)) ; + System.setErr(new PrintStream(outStream)) ; + + // Headers + setHeaders(httpResponse) ; + + outStream.println("<html>") ; + printHead(outStream, "Jena Data Validator Report") ; + outStream.println("<body>") ; + + outStream.println("<h1>RIOT Parser Report</h1>") ; + outStream.println("<p>Line and column numbers refer to original input</p>") ; + outStream.println("<p> </p>") ; + try { + LangRIOT parser = setupParser(tokenizer, language, errorHandler, outStream) ; + startFixed(outStream) ; + RiotException exception = null ; + try { + parser.parse() ; + System.out.flush() ; + System.err.flush() ; + } catch (RiotException ex) { exception = ex ; } + } finally + { + finishFixed(outStream) ; + System.out.flush() ; + System.err.flush() ; + System.setOut(stdout) ; + System.setErr(stdout) ; + } + + outStream.println("</body>") ; + outStream.println("</html>") ; + } catch (Exception ex) + { + serviceLog.warn("Exception in validationRequest",ex) ; + } + } + + static final long LIMIT = 50000 ; + + + private LangRIOT setupParser(Tokenizer tokenizer, Lang language, ErrorHandler errorHandler, final ServletOutputStream outStream) + { + Sink<Quad> sink = new Sink<Quad>() + { + SerializationContext sCxt = new SerializationContext() ; + @Override + public void send(Quad quad) + { + // Clean up! + StringBuilder sb = new StringBuilder() ; + + sb.append(formatNode(quad.getSubject())) ; + sb.append(" ") ; + sb.append(formatNode(quad.getPredicate())) ; + sb.append(" ") ; + sb.append(formatNode(quad.getObject())) ; + + if ( ! quad.isTriple() ) + { + sb.append(" ") ; + sb.append(formatNode(quad.getGraph())) ; + } + + String $ = htmlQuote(sb.toString()) ; + try { + outStream.print($) ; + outStream.println(" .") ; + } catch (IOException ex) { IO.exception(ex) ; } + } + @Override + public void close() {} + @Override + public void flush() {} + String formatNode(Node n) { return FmtUtils.stringForNode(n, sCxt) ; } + } ; + + StreamRDF dest = StreamRDFLib.sinkQuads(sink) ; + @SuppressWarnings("deprecation") + LangRIOT parser = RiotReader.createParser(tokenizer, language, null, dest) ; + // Don't resolve IRIs. Do checking. + parser.setProfile(RiotLib.profile(null, false, true, errorHandler)) ; + return parser ; + } + + // Error handler that records messages + private static class ErrorHandlerMsg implements ErrorHandler + { + private ServletOutputStream out ; + + ErrorHandlerMsg(ServletOutputStream out) { this.out = out ; } + + @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) + { + try { + String str = fmtMessage(message, line, col) ; + //String str = typeName+": "+message ; + str = htmlQuote(str) ; + out.print("<div class=\""+className+"\">") ; + out.print(str) ; + out.print("</div>") ; + } catch (IOException ex) { IO.exception(ex) ; } + } + } + + private Tokenizer createTokenizer(HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws Exception + { + Reader reader = null ; + String[] args = httpRequest.getParameterValues(paramData) ; + if ( args == null || args.length == 0 ) + { + // Not a form? + reader = httpRequest.getReader() ; + } + else if ( args.length > 1 ) + { + httpResponse.sendError(HttpServletResponse.SC_BAD_REQUEST, "Too many parameters for '"+paramData+"='") ; + return null ; + } + else + { + reader = new StringReader(args[0]) ; + } + + if ( reader == null ) + { + httpResponse.sendError(HttpServletResponse.SC_BAD_REQUEST, "Can't find data to validate") ; + return null ; + } + + return TokenizerFactory.makeTokenizer(reader) ; + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/validation/IRIValidator.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/validation/IRIValidator.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/validation/IRIValidator.java new file mode 100644 index 0000000..2972b49 --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/validation/IRIValidator.java @@ -0,0 +1,99 @@ +/* + * 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.io.IOException ; +import java.io.PrintStream ; +import java.util.Iterator ; + +import javax.servlet.ServletOutputStream ; +import javax.servlet.http.HttpServletRequest ; +import javax.servlet.http.HttpServletResponse ; + +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 ValidatorBase +{ + public IRIValidator() + { } + + 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) {} + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/validation/QueryValidator.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/validation/QueryValidator.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/validation/QueryValidator.java new file mode 100644 index 0000000..75ad901 --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/validation/QueryValidator.java @@ -0,0 +1,269 @@ +/* + * 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.io.IOException ; + +import javax.servlet.ServletOutputStream ; +import javax.servlet.http.HttpServletRequest ; +import javax.servlet.http.HttpServletResponse ; + +import org.apache.jena.atlas.io.IndentedLineBuffer ; +import org.apache.jena.atlas.io.IndentedWriter ; + +import com.hp.hpl.jena.query.Query ; +import com.hp.hpl.jena.query.QueryFactory ; +import com.hp.hpl.jena.query.Syntax ; +import com.hp.hpl.jena.sparql.ARQException ; +import com.hp.hpl.jena.sparql.algebra.Algebra ; +import com.hp.hpl.jena.sparql.algebra.Op ; +import com.hp.hpl.jena.sparql.serializer.SerializationContext ; + +public class QueryValidator extends ValidatorBase +{ + public QueryValidator() + { } + + static final String paramLineNumbers = "linenumbers" ; + static final String paramFormat = "outputFormat" ; + static final String paramQuery = "query" ; + static final String paramSyntax = "languageSyntax" ; + //static final String paramSyntaxExtended = "extendedSyntax" ; + + @Override + protected void execute(HttpServletRequest httpRequest, HttpServletResponse httpResponse) + { + try { +// if ( log.isInfoEnabled() ) +// log.info("validation request") ; + + String[] args = httpRequest.getParameterValues(paramQuery) ; + + if ( args == null || args.length == 0 ) + { + httpResponse.sendError(HttpServletResponse.SC_BAD_REQUEST, "No query parameter to validator") ; + return ; + } + + if ( args.length > 1 ) + { + httpResponse.sendError(HttpServletResponse.SC_BAD_REQUEST, "Too many query parameters") ; + return ; + } + + final String queryString = httpRequest.getParameter(paramQuery).replaceAll("(\r|\n| )*$", "") ; +// queryString = queryString.replace("\r\n", "\n") ; +// queryString.replaceAll("(\r|\n| )*$", "") ; + + String querySyntax = httpRequest.getParameter(paramSyntax) ; + if ( querySyntax == null || querySyntax.equals("") ) + querySyntax = "SPARQL" ; + + Syntax language = Syntax.lookup(querySyntax) ; + if ( language == null ) + { + httpResponse.sendError(HttpServletResponse.SC_BAD_REQUEST, "Unknown syntax: "+querySyntax) ; + return ; + } + + String lineNumbersArg = httpRequest.getParameter(paramLineNumbers) ; + + String a[] = httpRequest.getParameterValues(paramFormat) ; + + boolean outputSPARQL = false ; + boolean outputPrefix = false ; + boolean outputAlgebra = false ; + boolean outputQuads = false ; + boolean outputOptimized = false ; + boolean outputOptimizedQuads = false ; + + if ( a != null ) + { + for ( String anA : a ) + { + if ( anA.equals( "sparql" ) ) + { + outputSPARQL = true; + } + if ( anA.equals( "prefix" ) ) + { + outputPrefix = true; + } + if ( anA.equals( "algebra" ) ) + { + outputAlgebra = true; + } + if ( anA.equals( "quads" ) ) + { + outputQuads = true; + } + if ( anA.equals( "opt" ) ) + { + outputOptimized = true; + } + if ( anA.equals( "optquads" ) ) + { + outputOptimizedQuads = true; + } + } + } + +// if ( ! outputSPARQL && ! outputPrefix ) +// outputSPARQL = true ; + + boolean lineNumbers = true ; + + if ( lineNumbersArg != null ) + lineNumbers = lineNumbersArg.equalsIgnoreCase("true") || lineNumbersArg.equalsIgnoreCase("yes") ; + + // Headers + setHeaders(httpResponse) ; + + ServletOutputStream outStream = httpResponse.getOutputStream() ; + + outStream.println("<html>") ; + + printHead(outStream, "SPARQL Query Validation Report") ; + + outStream.println("<body>") ; + outStream.println("<h1>SPARQL Query Validator</h1>") ; + // Print query as received + { + outStream.println("<p>Input:</p>") ; + // Not Java's finest hour. + Content c = new Content(){ + @Override + public void print(IndentedWriter out) + { out.print(queryString) ; } + } ; + output(outStream, c, lineNumbers) ; + } + + // Attempt to parse it. + Query query = null ; + try { + query = QueryFactory.create(queryString, "http://example/base/", language) ; + } catch (ARQException ex) + { + // Over generous exception (should be QueryException) + // but this makes the code robust. + outStream.println("<p>Syntax error:</p>") ; + startFixed(outStream) ; + outStream.println(ex.getMessage()) ; + finishFixed(outStream) ; + } + catch (RuntimeException ex) + { + outStream.println("<p>Internal error:</p>") ; + startFixed(outStream) ; + outStream.println(ex.getMessage()) ; + finishFixed(outStream) ; + } + + if ( query != null ) + { + if ( outputSPARQL ) + outputSyntax(outStream, query, lineNumbers) ; + + if ( outputAlgebra ) + outputAlgebra(outStream, query, lineNumbers) ; + + if ( outputQuads ) + outputAlgebraQuads(outStream, query, lineNumbers) ; + + if ( outputOptimized ) + outputAlgebraOpt(outStream, query, lineNumbers) ; + + if ( outputOptimizedQuads ) + outputAlgebraOptQuads(outStream, query, lineNumbers) ; + } + + outStream.println("</body>") ; + outStream.println("</html>") ; + + } catch (Exception ex) + { + serviceLog.warn("Exception in doGet",ex) ; + } + } + + interface Content { void print(IndentedWriter out) ; } + + private void outputSyntax(ServletOutputStream outStream, final Query query, boolean lineNumbers) throws IOException + { + outStream.println("<p>Formatted, parsed query:</p>") ; + Content c = new Content(){ + @Override + public void print(IndentedWriter out) + { query.serialize(out) ; } + } ; + output(outStream, c, lineNumbers) ; + } + + private void outputAlgebra(ServletOutputStream outStream, final Query query, boolean lineNumbers) throws IOException + { + outStream.println("<p>Algebra structure:</p>") ; + final Op op = Algebra.compile(query) ; // No optimization + output(outStream, query, op, lineNumbers) ; + } + + private void outputAlgebraOpt(ServletOutputStream outStream, final Query query, boolean lineNumbers) throws IOException + { + outStream.println("<p>Alebgra, with general triple optimizations:</p>") ; + final Op op = Algebra.optimize(Algebra.compile(query)) ; + output(outStream, query, op, lineNumbers) ; + } + + private void outputAlgebraQuads(ServletOutputStream outStream, final Query query, boolean lineNumbers) throws IOException + { + outStream.println("<p>Quad structure:</p>") ; + final Op op = Algebra.toQuadForm(Algebra.compile(query)) ; + output(outStream, query, op, lineNumbers) ; + } + + private void outputAlgebraOptQuads(ServletOutputStream outStream, final Query query, boolean lineNumbers) throws IOException + { + outStream.println("<p>Alebgra, with general quads optimizations:</p>") ; + final Op op = Algebra.optimize(Algebra.toQuadForm(Algebra.compile(query))) ; + output(outStream, query, op, lineNumbers) ; + } + + private void output(ServletOutputStream outStream, Query query, final Op op, boolean lineNumbers) throws IOException + { + final SerializationContext sCxt = new SerializationContext(query) ; + Content c = new Content(){ + @Override + public void print(IndentedWriter out) + { op.output(out, sCxt) ; } + } ; + output(outStream, c , lineNumbers) ; + } + + private void output(ServletOutputStream outStream, Content content, boolean lineNumbers) throws IOException + { + startFixed(outStream) ; + IndentedLineBuffer out = new IndentedLineBuffer(lineNumbers) ; + content.print(out) ; + out.flush() ; + String x = htmlQuote(out.asString()) ; + byte b[] = x.getBytes("UTF-8") ; + outStream.write(b) ; + finishFixed(outStream) ; + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/validation/UpdateValidator.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/validation/UpdateValidator.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/validation/UpdateValidator.java new file mode 100644 index 0000000..dd63de8 --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/validation/UpdateValidator.java @@ -0,0 +1,174 @@ +/* + * 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.io.IOException ; + +import javax.servlet.ServletOutputStream ; +import javax.servlet.http.HttpServletRequest ; +import javax.servlet.http.HttpServletResponse ; + +import org.apache.jena.atlas.io.IndentedLineBuffer ; +import org.apache.jena.atlas.io.IndentedWriter ; + +import com.hp.hpl.jena.query.Syntax ; +import com.hp.hpl.jena.sparql.ARQException ; +import com.hp.hpl.jena.update.UpdateFactory ; +import com.hp.hpl.jena.update.UpdateRequest ; + +public class UpdateValidator extends ValidatorBase +{ + public UpdateValidator() + { } + + static final String paramLineNumbers = "linenumbers" ; + static final String paramFormat = "outputFormat" ; + static final String paramUpdate = "update" ; + static final String paramSyntax = "languageSyntax" ; + //static final String paramSyntaxExtended = "extendedSyntax" ; + + @Override + protected void execute(HttpServletRequest httpRequest, HttpServletResponse httpResponse) + { + try { +// if ( log.isInfoEnabled() ) +// log.info("validation request") ; + + String[] args = httpRequest.getParameterValues(paramUpdate) ; + + if ( args == null || args.length == 0 ) + { + httpResponse.sendError(HttpServletResponse.SC_BAD_REQUEST, "No update parameter to validator") ; + return ; + } + + if ( args.length > 1 ) + { + httpResponse.sendError(HttpServletResponse.SC_BAD_REQUEST, "Too many update parameters") ; + return ; + } + + final String updateString = httpRequest.getParameter(paramUpdate).replaceAll("(\r|\n| )*$", "") ; + + String updateSyntax = httpRequest.getParameter(paramSyntax) ; + if ( updateSyntax == null || updateSyntax.equals("") ) + updateSyntax = "SPARQL" ; + + Syntax language = Syntax.lookup(updateSyntax) ; + if ( language == null ) + { + httpResponse.sendError(HttpServletResponse.SC_BAD_REQUEST, "Unknown syntax: "+updateSyntax) ; + return ; + } + + String lineNumbersArg = httpRequest.getParameter(paramLineNumbers) ; + String a[] = httpRequest.getParameterValues(paramFormat) ; + + // Currently default. + boolean outputSPARQL = true ; + boolean lineNumbers = true ; + + if ( lineNumbersArg != null ) + lineNumbers = lineNumbersArg.equalsIgnoreCase("true") || lineNumbersArg.equalsIgnoreCase("yes") ; + + // Headers + setHeaders(httpResponse) ; + + ServletOutputStream outStream = httpResponse.getOutputStream() ; + + outStream.println("<html>") ; + + printHead(outStream, "SPARQL Update Validation Report") ; + + outStream.println("<body>") ; + outStream.println("<h1>SPARQL Update Validator</h1>") ; + + // Print as received + { + outStream.println("<p>Input:</p>") ; + // Not Java's finest hour. + Content c = new Content(){ + @Override + public void print(IndentedWriter out) + { out.print(updateString) ; } + } ; + output(outStream, c, lineNumbers) ; + } + + // Attempt to parse it. + UpdateRequest request= null ; + try { + request = UpdateFactory.create(updateString, "http://example/base/", language) ; + } catch (ARQException ex) + { + // Over generous exception (should be QueryException) + // but this makes the code robust. + outStream.println("<p>Syntax error:</p>") ; + startFixed(outStream) ; + outStream.println(ex.getMessage()) ; + finishFixed(outStream) ; + } + catch (RuntimeException ex) + { + outStream.println("<p>Internal error:</p>") ; + startFixed(outStream) ; + outStream.println(ex.getMessage()) ; + finishFixed(outStream) ; + } + + // Because we pass into anon inner classes + final UpdateRequest updateRequest = request ; + + // OK? Pretty print + if ( updateRequest != null && outputSPARQL ) + { + outStream.println("<p>Formatted, parsed update request:</p>") ; + Content c = new Content(){ + @Override + public void print(IndentedWriter out) + { + updateRequest.output(out) ; + } + + } ; + output(outStream, c, lineNumbers) ; + } + outStream.println("</body>") ; + outStream.println("</html>") ; + + } catch (Exception ex) + { + serviceLog.warn("Exception in doGet",ex) ; + } + } + + interface Content { void print(IndentedWriter out) ; } + + private void output(ServletOutputStream outStream, Content content, boolean lineNumbers) throws IOException + { + startFixed(outStream) ; + IndentedLineBuffer out = new IndentedLineBuffer(lineNumbers) ; + content.print(out) ; + out.flush() ; + String x = htmlQuote(out.asString()) ; + byte b[] = x.getBytes("UTF-8") ; + outStream.write(b) ; + finishFixed(outStream) ; + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/java/org/apache/jena/fuseki/validation/ValidatorBase.java ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/java/org/apache/jena/fuseki/validation/ValidatorBase.java b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/validation/ValidatorBase.java new file mode 100644 index 0000000..61820f4 --- /dev/null +++ b/jena-fuseki1/src/main/java/org/apache/jena/fuseki/validation/ValidatorBase.java @@ -0,0 +1,120 @@ +/* + * 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.io.IOException ; + +import javax.servlet.ServletConfig ; +import javax.servlet.ServletException ; +import javax.servlet.ServletOutputStream ; +import javax.servlet.http.HttpServlet ; +import javax.servlet.http.HttpServletRequest ; +import javax.servlet.http.HttpServletResponse ; + +import org.apache.jena.fuseki.Fuseki ; +import org.apache.jena.fuseki.servlets.ServletBase ; +import org.slf4j.Logger ; + +public abstract class ValidatorBase extends HttpServlet +{ + protected static Logger serviceLog = Fuseki.requestLog ; + + public static final String cssFile = "/fuseki.css" ; + public static final String respService = "X-Service" ; + + + @Override + public void init() throws ServletException + { super.init() ; } + + @Override + public void init(ServletConfig config) throws ServletException + { super.init(config) ; } + + @Override + public void destroy() + { } + + @Override + public void doGet(HttpServletRequest httpRequest, HttpServletResponse httpResponse) + { execute(httpRequest, httpResponse) ; } + + @Override + public void doPost(HttpServletRequest httpRequest, HttpServletResponse httpResponse) + { execute(httpRequest, httpResponse) ; } + + protected abstract void execute(HttpServletRequest httpRequest, HttpServletResponse httpResponse) ; + + protected static void setHeaders(HttpServletResponse httpResponse) + { + ServletBase.setCommonHeaders(httpResponse) ; + httpResponse.setCharacterEncoding("UTF-8") ; + httpResponse.setContentType("text/html") ; + httpResponse.setHeader(respService, "Fuseki/ARQ SPARQL Query Validator: http://jena.apache.org/") ; + } + + protected static String htmlQuote(String str) + { + StringBuilder sBuff = new StringBuilder() ; + for ( int i = 0 ; i < str.length() ; i++ ) + { + char ch = str.charAt(i) ; + switch (ch) + { + case '<': sBuff.append("<") ; break ; + case '>': sBuff.append(">") ; break ; + case '&': sBuff.append("&") ; break ; + default: + // Work around Eclipe bug with StringBuffer.append(char) + //try { sBuff.append(ch) ; } catch (Exception ex) {} + sBuff.append(ch) ; + break ; + } + } + return sBuff.toString() ; + } + + protected static void startFixed(ServletOutputStream outStream) throws IOException + { + outStream.println("<pre class=\"box\">") ; + } + + protected static void columns(String prefix, ServletOutputStream outStream) throws IOException + { + outStream.print(prefix) ; + outStream.println(" 1 2 3 4 5 6 7") ; + outStream.print(prefix) ; + outStream.println("12345678901234567890123456789012345678901234567890123456789012345678901234567890") ; + } + + protected static void finishFixed(ServletOutputStream outStream) throws IOException + { + outStream.println("</pre>") ; + } + + protected static void printHead(ServletOutputStream outStream, String title) throws IOException + { + outStream.println("<head>") ; + outStream.println(" <title>"+title+"</title>") ; + outStream.println(" <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">") ; + outStream.println(" <link rel=\"stylesheet\" type=\"text/css\" href=\""+cssFile+"\" />") ; + //outStream.println() ; + outStream.println("</head>") ; + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/662cf71d/jena-fuseki1/src/main/resources/META-INF/DEPENDENCIES ---------------------------------------------------------------------- diff --git a/jena-fuseki1/src/main/resources/META-INF/DEPENDENCIES b/jena-fuseki1/src/main/resources/META-INF/DEPENDENCIES new file mode 100644 index 0000000..910b788 --- /dev/null +++ b/jena-fuseki1/src/main/resources/META-INF/DEPENDENCIES @@ -0,0 +1,24 @@ +This file lists the dependences for Apache Jena Fuseki. + Version numbers are given in the POM file for a particular distribution. + +Apache Projects: Apache Software License + Apache Jena, including the Jena IRI library + Apache Xerces-J + Apache log4j + Apache HttpComponents (HTTP Client) + Apache Commons Codec + Apache Common FileUpload + +ICU4J : http://icu-project.org/ + IBM X License (to version ICU4J 3.4.4) + +SLF4J : http://www.slf4j.org/ + Copyright (c) 2004-2008 QOS.ch + MIT License + +JUnit : http://junit.org/ + Common Public License - v 1.0 + +Jetty: http://www.eclipse.org/jetty/ + Apache License 2.0 + (also avilable under Eclipse Public License 1.0)
