JENA-1454: Use of new result set machinery for HTTP responses
Project: http://git-wip-us.apache.org/repos/asf/jena/repo Commit: http://git-wip-us.apache.org/repos/asf/jena/commit/1e1da957 Tree: http://git-wip-us.apache.org/repos/asf/jena/tree/1e1da957 Diff: http://git-wip-us.apache.org/repos/asf/jena/diff/1e1da957 Branch: refs/heads/master Commit: 1e1da9570e2d07a1500d85c2b2b1e495624ece00 Parents: b022dc1 Author: Andy Seaborne <[email protected]> Authored: Sat Dec 23 15:51:55 2017 +0000 Committer: Andy Seaborne <[email protected]> Committed: Sat Dec 23 21:13:28 2017 +0000 ---------------------------------------------------------------------- .../org/apache/jena/query/ResultSetFactory.java | 3 +- .../java/org/apache/jena/riot/WebContent.java | 15 ++ .../riot/lang/BlankNodeAllocatorGlobal.java | 58 ++++++ .../lang/BlankNodeAllocatorTraditional.java | 59 ------ .../org/apache/jena/riot/lang/LabelToNode.java | 8 +- .../jena/sparql/engine/QueryEngineBase.java | 2 +- .../jena/sparql/engine/QueryExecutionBase.java | 2 +- .../sparql/engine/http/QueryEngineHTTP.java | 61 +++--- .../org/apache/jena/sparql/expr/E_BNode.java | 4 +- .../org/apache/jena/sparql/lang/ParserBase.java | 1 + .../sparql/modify/UpdateProcessRemoteBase.java | 2 +- .../jena/sparql/modify/UpdateProcessorBase.java | 2 +- .../modify/UpdateProcessorStreamingBase.java | 2 +- .../apache/jena/sparql/resultset/JSONInput.java | 2 +- .../jena/sparql/resultset/JSONOutput.java | 2 +- .../apache/jena/sparql/resultset/XMLInput.java | 2 + .../jena/sparql/resultset/XMLInputSAX.java | 2 +- .../apache/jena/sparql/resultset/XMLOutput.java | 40 ++-- .../jena/sparql/sse/lang/ParseHandlerPlain.java | 6 +- .../org/apache/jena/sparql/util/Context.java | 43 +++-- .../apache/jena/sparql/util/LabelToNodeMap.java | 1 - .../jena/riot/lang/TestBlankNodeAllocator.java | 2 +- .../apache/jena/riot/lang/TestLabelToNode.java | 2 +- jena-fuseki2/examples/service-tdb1-B.ttl | 1 + .../main/java/org/apache/jena/fuseki/DEF.java | 6 + .../apache/jena/fuseki/servlets/ActionBase.java | 6 - .../apache/jena/fuseki/servlets/HttpAction.java | 8 + .../jena/fuseki/servlets/ResponseDataset.java | 2 +- .../jena/fuseki/servlets/ResponseOps.java | 21 +++ .../jena/fuseki/servlets/ResponseResultSet.java | 187 ++++++++----------- 30 files changed, 299 insertions(+), 253 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/jena/blob/1e1da957/jena-arq/src/main/java/org/apache/jena/query/ResultSetFactory.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/query/ResultSetFactory.java b/jena-arq/src/main/java/org/apache/jena/query/ResultSetFactory.java index 5682b3d..20320a5 100644 --- a/jena-arq/src/main/java/org/apache/jena/query/ResultSetFactory.java +++ b/jena-arq/src/main/java/org/apache/jena/query/ResultSetFactory.java @@ -41,8 +41,9 @@ import org.apache.jena.util.FileManager ; /** ResultSetFactory - make result sets from places other than a query. */ public class ResultSetFactory { - // See also ResultSetMgr -- this post-dates this code. + // See also ResultSetMgr - which post-dates this code. // Ideally, read operations here should call ResultSetMgr. + // The exception is XML from a string and the arcachic RDF to ResultSet forms. /** * Load a result set from file or URL into a result set (memory backed). http://git-wip-us.apache.org/repos/asf/jena/blob/1e1da957/jena-arq/src/main/java/org/apache/jena/riot/WebContent.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/riot/WebContent.java b/jena-arq/src/main/java/org/apache/jena/riot/WebContent.java index 3aa0ce8..f265acc 100644 --- a/jena-arq/src/main/java/org/apache/jena/riot/WebContent.java +++ b/jena-arq/src/main/java/org/apache/jena/riot/WebContent.java @@ -20,6 +20,7 @@ package org.apache.jena.riot; import org.apache.jena.atlas.lib.Lib ; import org.apache.jena.atlas.web.ContentType ; +import org.apache.jena.riot.resultset.ResultSetLang; public class WebContent @@ -270,4 +271,18 @@ public class WebContent return ct ; } + + /** Map content-type to lang for SPARQL results, with pragmatic adapters. */ + public static Lang contentTypeToLangResultSet(String contentType) { + if ( contentType == null ) + return null; + // Special cases : use of application/xml and application/json + if ( contentType.equals(WebContent.contentTypeXML) ) + return ResultSetLang.SPARQLResultSetXML; + else if ( contentType.equals(WebContent.contentTypeJSON) ) + return ResultSetLang.SPARQLResultSetJSON; + Lang lang = RDFLanguages.contentTypeToLang(contentType); + // May not be a reader/write result set language. + return lang; + } } http://git-wip-us.apache.org/repos/asf/jena/blob/1e1da957/jena-arq/src/main/java/org/apache/jena/riot/lang/BlankNodeAllocatorGlobal.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/riot/lang/BlankNodeAllocatorGlobal.java b/jena-arq/src/main/java/org/apache/jena/riot/lang/BlankNodeAllocatorGlobal.java new file mode 100644 index 0000000..6ad621b --- /dev/null +++ b/jena-arq/src/main/java/org/apache/jena/riot/lang/BlankNodeAllocatorGlobal.java @@ -0,0 +1,58 @@ +/** + * 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.riot.lang; + +import java.util.HashMap ; +import java.util.Map ; + +import org.apache.jena.graph.Node ; +import org.apache.jena.graph.NodeFactory ; + +/** Allocate blank nodes by creating a randomly generated blank node. + * This allocator has arbitrary sized state. + */ + +public class BlankNodeAllocatorGlobal implements BlankNodeAllocator +{ + Map<String, Node> map = new HashMap<>() ; + + public BlankNodeAllocatorGlobal() {} + + @Override + public void reset() { map.clear() ; } + + @Override + public Node alloc(String label) + { + Node b = map.get(label) ; + if ( b == null ) + { + b = create() ; + map.put(label, b) ; + } + return b ; + } + + + @Override + public Node create() + { + return NodeFactory.createBlankNode() ; + } +} http://git-wip-us.apache.org/repos/asf/jena/blob/1e1da957/jena-arq/src/main/java/org/apache/jena/riot/lang/BlankNodeAllocatorTraditional.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/riot/lang/BlankNodeAllocatorTraditional.java b/jena-arq/src/main/java/org/apache/jena/riot/lang/BlankNodeAllocatorTraditional.java deleted file mode 100644 index b44fd43..0000000 --- a/jena-arq/src/main/java/org/apache/jena/riot/lang/BlankNodeAllocatorTraditional.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * 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.riot.lang; - -import java.util.HashMap ; -import java.util.Map ; - -import org.apache.jena.graph.Node ; -import org.apache.jena.graph.NodeFactory ; - -/** Allocate blank nodes according to the traditional policy (up to jena 2.10.0) - * This allocator has arbitrary sized state. - * Create a fresh one for each parser run. - */ - -public class BlankNodeAllocatorTraditional implements BlankNodeAllocator -{ - Map<String, Node> map = new HashMap<>() ; - - public BlankNodeAllocatorTraditional() {} - - @Override - public void reset() { map.clear() ; } - - @Override - public Node alloc(String label) - { - Node b = map.get(label) ; - if ( b == null ) - { - b = create() ; - map.put(label, b) ; - } - return b ; - } - - - @Override - public Node create() - { - return NodeFactory.createBlankNode() ; - } -} http://git-wip-us.apache.org/repos/asf/jena/blob/1e1da957/jena-arq/src/main/java/org/apache/jena/riot/lang/LabelToNode.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/riot/lang/LabelToNode.java b/jena-arq/src/main/java/org/apache/jena/riot/lang/LabelToNode.java index 0222b05..4c69fa9 100644 --- a/jena-arq/src/main/java/org/apache/jena/riot/lang/LabelToNode.java +++ b/jena-arq/src/main/java/org/apache/jena/riot/lang/LabelToNode.java @@ -50,8 +50,8 @@ public class LabelToNode extends MapWithScope<String, Node, Node> { return new LabelToNode(new AllocScopePolicy(), nodeAllocatorHash(seed)) ; } /** The policy up to jena 2.10.0 - problems at very large scale */ - public static LabelToNode createScopeByDocumentOld() - { return new LabelToNode(new SingleScopePolicy(), nodeAllocatorTraditional()) ; } + public static LabelToNode createScopeGlobal() + { return new LabelToNode(new SingleScopePolicy(), nodeAllocatorGlobal()) ; } /** Allocation scoped by graph and label. */ public static LabelToNode createScopeByGraph() { @@ -101,8 +101,8 @@ public class LabelToNode extends MapWithScope<String, Node, Node> return new Alloc(new BlankNodeAllocatorLabel()) ; } - private static Allocator<String, Node, Node> nodeAllocatorTraditional() { - return new Alloc(new BlankNodeAllocatorTraditional()) ; + private static Allocator<String, Node, Node> nodeAllocatorGlobal() { + return new Alloc(new BlankNodeAllocatorGlobal()) ; } private static Allocator<String, Node, Node> nodeAllocatorEncoded() { http://git-wip-us.apache.org/repos/asf/jena/blob/1e1da957/jena-arq/src/main/java/org/apache/jena/sparql/engine/QueryEngineBase.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/engine/QueryEngineBase.java b/jena-arq/src/main/java/org/apache/jena/sparql/engine/QueryEngineBase.java index 0204c5e..5fd8aed 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/engine/QueryEngineBase.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/engine/QueryEngineBase.java @@ -63,7 +63,7 @@ public abstract class QueryEngineBase implements OpEval, Closeable // Ensure context setup - usually done in QueryExecutionBase // so it can be changed after initialization. if ( context == null ) - context = Context.setupContext(context, dataset) ; + context = Context.setupContextExec(context, dataset) ; this.query = null ; setOp(op) ; } http://git-wip-us.apache.org/repos/asf/jena/blob/1e1da957/jena-arq/src/main/java/org/apache/jena/sparql/engine/QueryExecutionBase.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/engine/QueryExecutionBase.java b/jena-arq/src/main/java/org/apache/jena/sparql/engine/QueryExecutionBase.java index dbc930e..1dc292e 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/engine/QueryExecutionBase.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/engine/QueryExecutionBase.java @@ -88,7 +88,7 @@ public class QueryExecutionBase implements QueryExecution private void init() { DatasetGraph dsg = (dataset == null) ? null : dataset.asDatasetGraph() ; - context = Context.setupContext(context, dsg) ; + context = Context.setupContextExec(context, dsg) ; if ( query != null ) context.put(ARQConstants.sysCurrentQuery, query) ; // NB: Setting timeouts via the context after creating a QueryExecutionBase http://git-wip-us.apache.org/repos/asf/jena/blob/1e1da957/jena-arq/src/main/java/org/apache/jena/sparql/engine/http/QueryEngineHTTP.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/engine/http/QueryEngineHTTP.java b/jena-arq/src/main/java/org/apache/jena/sparql/engine/http/QueryEngineHTTP.java index 713cbc0..d424b76 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/engine/http/QueryEngineHTTP.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/engine/http/QueryEngineHTTP.java @@ -35,18 +35,14 @@ import org.apache.jena.atlas.lib.Pair ; import org.apache.jena.graph.Triple ; import org.apache.jena.query.* ; import org.apache.jena.rdf.model.Model ; -import org.apache.jena.riot.Lang ; -import org.apache.jena.riot.RDFDataMgr ; -import org.apache.jena.riot.RDFLanguages ; -import org.apache.jena.riot.WebContent ; +import org.apache.jena.riot.*; +import org.apache.jena.riot.resultset.ResultSetLang; +import org.apache.jena.riot.resultset.ResultSetReaderRegistry; import org.apache.jena.sparql.ARQException ; import org.apache.jena.sparql.core.Quad; import org.apache.jena.sparql.engine.ResultSetCheckCondition ; import org.apache.jena.sparql.graph.GraphFactory ; -import org.apache.jena.sparql.resultset.CSVInput ; -import org.apache.jena.sparql.resultset.JSONInput ; -import org.apache.jena.sparql.resultset.TSVInput ; -import org.apache.jena.sparql.resultset.XMLInput ; +import org.apache.jena.sparql.resultset.ResultSetException; import org.apache.jena.sparql.util.Context ; import org.slf4j.Logger ; import org.slf4j.LoggerFactory ; @@ -367,16 +363,16 @@ public class QueryEngineHTTP implements QueryExecution { actualContentType = selectContentType; } - if (actualContentType.equals(WebContent.contentTypeResultsXML) || actualContentType.equals(WebContent.contentTypeXML)) - return ResultSetFactory.fromXML(in); - if (actualContentType.equals(WebContent.contentTypeResultsJSON) || actualContentType.equals(WebContent.contentTypeJSON)) - return ResultSetFactory.fromJSON(in); - if (actualContentType.equals(WebContent.contentTypeTextTSV)) - return ResultSetFactory.fromTSV(in); - if (actualContentType.equals(WebContent.contentTypeTextCSV)) - return CSVInput.fromCSV(in); - throw new QueryException("Endpoint returned Content-Type: " + actualContentType - + " which is not currently supported for SELECT queries"); + // Map to lang, with pragmatic alternatives. + Lang lang = WebContent.contentTypeToLangResultSet(actualContentType); + if ( lang == null ) + throw new QueryException("Endpoint returned Content-Type: " + actualContentType + " which is not rcognized for SELECT queries"); + if ( !ResultSetReaderRegistry.isRegistered(lang) ) + throw new QueryException("Endpoint returned Content-Type: " + actualContentType + " which is not supported for SELECT queries"); + // This returns a streaming result set for some formats. + // Do not close the InputStream at this point. + ResultSet result = ResultSetMgr.read(in, lang); + return result; } @Override @@ -499,18 +495,23 @@ public class QueryEngineHTTP implements QueryExecution { actualContentType = askContentType; } - // Parse the result appropriately depending on the - // selected content type. - if (actualContentType.equals(WebContent.contentTypeResultsXML) || actualContentType.equals(WebContent.contentTypeXML)) - return XMLInput.booleanFromXML(in); - if (actualContentType.equals(WebContent.contentTypeResultsJSON) || actualContentType.equals(WebContent.contentTypeJSON)) - return JSONInput.booleanFromJSON(in); - if (actualContentType.equals(WebContent.contentTypeTextTSV)) - return TSVInput.booleanFromTSV(in); - if (actualContentType.equals(WebContent.contentTypeTextCSV)) - return CSVInput.booleanFromCSV(in); - throw new QueryException("Endpoint returned Content-Type: " + actualContentType - + " which is not currently supported for ASK queries"); + Lang lang = RDFLanguages.contentTypeToLang(actualContentType); + if ( lang == null ) { + // Any specials : + // application/xml for application/sparql-results+xml + // application/json for application/sparql-results+json + if (actualContentType.equals(WebContent.contentTypeXML)) + lang = ResultSetLang.SPARQLResultSetXML; + else if ( actualContentType.equals(WebContent.contentTypeJSON)) + lang = ResultSetLang.SPARQLResultSetJSON; + } + if ( lang == null ) + throw new QueryException("Endpoint returned Content-Type: " + actualContentType + " which is not supported for ASK queries"); + Boolean result = ResultSetMgr.readBoolean(in, lang); + return result; + } catch (ResultSetException e) { + log.warn("Returned content is not a boolean result", e); + throw e; } catch (QueryExceptionHTTP e) { throw e ; } http://git-wip-us.apache.org/repos/asf/jena/blob/1e1da957/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_BNode.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_BNode.java b/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_BNode.java index 162a812..2430308 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_BNode.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/expr/E_BNode.java @@ -76,7 +76,9 @@ public class E_BNode extends ExprFunctionN // 0 or one LabelToNodeMap mapper = mapping.get(binding) ; if ( mapper == null ) { - mapper = LabelToNodeMap.createBNodeMap() ; + @SuppressWarnings("deprecation") + LabelToNodeMap mapper_ = LabelToNodeMap.createBNodeMap() ; + mapper = mapper_; mapping.put(binding, mapper) ; } http://git-wip-us.apache.org/repos/asf/jena/blob/1e1da957/jena-arq/src/main/java/org/apache/jena/sparql/lang/ParserBase.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/lang/ParserBase.java b/jena-arq/src/main/java/org/apache/jena/sparql/lang/ParserBase.java index 3a4eedd..61f206b 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/lang/ParserBase.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/lang/ParserBase.java @@ -80,6 +80,7 @@ public class ParserBase private boolean bNodesAreAllowed = true ; // label => bNode for construct templates patterns + @SuppressWarnings("deprecation") final LabelToNodeMap bNodeLabels = LabelToNodeMap.createBNodeMap() ; // label => bNode (as variable) for graph patterns http://git-wip-us.apache.org/repos/asf/jena/blob/1e1da957/jena-arq/src/main/java/org/apache/jena/sparql/modify/UpdateProcessRemoteBase.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/modify/UpdateProcessRemoteBase.java b/jena-arq/src/main/java/org/apache/jena/sparql/modify/UpdateProcessRemoteBase.java index 8e18cd1..e03b087 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/modify/UpdateProcessRemoteBase.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/modify/UpdateProcessRemoteBase.java @@ -75,7 +75,7 @@ public abstract class UpdateProcessRemoteBase implements UpdateProcessor { this.request = request; this.endpoint = endpoint; - this.context = Context.setupContext(context, null); + this.context = Context.setupContextExec(context, null); // Apply service configuration if applicable UpdateProcessRemoteBase.applyServiceConfig(endpoint, this); http://git-wip-us.apache.org/repos/asf/jena/blob/1e1da957/jena-arq/src/main/java/org/apache/jena/sparql/modify/UpdateProcessorBase.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/modify/UpdateProcessorBase.java b/jena-arq/src/main/java/org/apache/jena/sparql/modify/UpdateProcessorBase.java index fd00fca..4a83c9d 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/modify/UpdateProcessorBase.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/modify/UpdateProcessorBase.java @@ -45,7 +45,7 @@ public class UpdateProcessorBase implements UpdateProcessor this.request = request ; this.datasetGraph = datasetGraph ; this.inputBinding = inputBinding; - this.context = Context.setupContext(context, datasetGraph) ; + this.context = Context.setupContextExec(context, datasetGraph) ; this.factory = factory ; } http://git-wip-us.apache.org/repos/asf/jena/blob/1e1da957/jena-arq/src/main/java/org/apache/jena/sparql/modify/UpdateProcessorStreamingBase.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/modify/UpdateProcessorStreamingBase.java b/jena-arq/src/main/java/org/apache/jena/sparql/modify/UpdateProcessorStreamingBase.java index 48569e0..505c4a0 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/modify/UpdateProcessorStreamingBase.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/modify/UpdateProcessorStreamingBase.java @@ -36,7 +36,7 @@ public class UpdateProcessorStreamingBase implements UpdateProcessorStreaming public UpdateProcessorStreamingBase(DatasetGraph datasetGraph, Binding inputBinding, Context context, UpdateEngineFactory factory) { this.datasetGraph = datasetGraph ; - this.context = Context.setupContext(context, datasetGraph) ; + this.context = Context.setupContextExec(context, datasetGraph) ; proc = factory.create(datasetGraph, inputBinding, context) ; } http://git-wip-us.apache.org/repos/asf/jena/blob/1e1da957/jena-arq/src/main/java/org/apache/jena/sparql/resultset/JSONInput.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/resultset/JSONInput.java b/jena-arq/src/main/java/org/apache/jena/sparql/resultset/JSONInput.java index d2a3ee5..264ac6c 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/resultset/JSONInput.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/resultset/JSONInput.java @@ -26,7 +26,7 @@ import org.apache.jena.riot.resultset.ResultSetLang; import org.apache.jena.riot.resultset.rw.ResultsReader; /** - * @deprecated Use ResultSetMgr.read + * @deprecated Use {@code ResultSetMgr.read} */ @Deprecated public class JSONInput extends SPARQLResult { http://git-wip-us.apache.org/repos/asf/jena/blob/1e1da957/jena-arq/src/main/java/org/apache/jena/sparql/resultset/JSONOutput.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/resultset/JSONOutput.java b/jena-arq/src/main/java/org/apache/jena/sparql/resultset/JSONOutput.java index 9898d54..c33d70e 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/resultset/JSONOutput.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/resultset/JSONOutput.java @@ -25,7 +25,7 @@ import org.apache.jena.riot.resultset.ResultSetLang; import org.apache.jena.riot.resultset.rw.ResultsWriter; /** - * @deprecated Use ResultSetMgr.write(,,ResultSetLang.SPARQLResultSetJSON) + * @deprecated Use {@code ResultSetMgr.write(??,??,ResultSetLang.SPARQLResultSetJSON)} */ @Deprecated public class JSONOutput extends OutputBase { http://git-wip-us.apache.org/repos/asf/jena/blob/1e1da957/jena-arq/src/main/java/org/apache/jena/sparql/resultset/XMLInput.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/resultset/XMLInput.java b/jena-arq/src/main/java/org/apache/jena/sparql/resultset/XMLInput.java index 469a466..43d50a3 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/resultset/XMLInput.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/resultset/XMLInput.java @@ -29,6 +29,8 @@ import org.apache.jena.sparql.SystemARQ; /** * Code that reads an XML Result Set and builds the ARQ structure for the same. + * + * @deprecated Use {@code ResultSetMgr.read} */ @Deprecated public class XMLInput { http://git-wip-us.apache.org/repos/asf/jena/blob/1e1da957/jena-arq/src/main/java/org/apache/jena/sparql/resultset/XMLInputSAX.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/resultset/XMLInputSAX.java b/jena-arq/src/main/java/org/apache/jena/sparql/resultset/XMLInputSAX.java index e9952bf..01026ff 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/resultset/XMLInputSAX.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/resultset/XMLInputSAX.java @@ -46,7 +46,7 @@ import org.xml.sax.* ; import org.xml.sax.helpers.XMLReaderFactory ; /** Code that reads an XML Result Set and builds the ARQ structure for the same. */ - +// UNUSED class XMLInputSAX extends SPARQLResult { // See also XMLInputStAX, which is preferred. // SAX is not a streaming API - the SAX handler is called as fast as the http://git-wip-us.apache.org/repos/asf/jena/blob/1e1da957/jena-arq/src/main/java/org/apache/jena/sparql/resultset/XMLOutput.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/resultset/XMLOutput.java b/jena-arq/src/main/java/org/apache/jena/sparql/resultset/XMLOutput.java index 48e85f1..5370cd3 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/resultset/XMLOutput.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/resultset/XMLOutput.java @@ -29,8 +29,25 @@ import org.apache.jena.sparql.util.Context; public class XMLOutput extends OutputBase { - String stylesheetURL = null ; - boolean includeXMLinst = true ; + /** Set the XML style sheet processing instruction {@code <?xml-stylesheet...>}. + * Set to null to not use a stylesheet. */ + public static void setStylesheetURL(Context cxt, String stylesheetURL) { + cxt.set(ResultSetWriterXML.xmlStylesheet, stylesheetURL); + } + + /** Set whether to include {@code <?xml ...>}. + * Set to null for "default" behaviour. */ + public static void setXMLinstruction(Context cxt, Boolean value) { + if ( value != null ) + cxt.set(ResultSetWriterXML.xmlInstruction, value.booleanValue()); + else + cxt.unset(ResultSetWriterXML.xmlInstruction); + } + + // -- Older + + protected String stylesheetURL = null ; + protected boolean includeXMLinst = true ; public XMLOutput() {} @@ -46,13 +63,10 @@ public class XMLOutput extends OutputBase setStylesheetURL(stylesheetURL); setIncludeXMLinst(includeXMLinst); } - + @Override public void format(OutputStream out, ResultSet resultSet) { - Context cxt = ARQ.getContext().copy(); - if ( stylesheetURL != null ) - cxt.set(ResultSetWriterXML.xmlStylesheet, stylesheetURL); - cxt.set(ResultSetWriterXML.xmlInstruction, includeXMLinst); + Context cxt = setup(); ResultsWriter.create() .context(cxt) .lang(ResultSetLang.SPARQLResultSetXML) @@ -77,14 +91,18 @@ public class XMLOutput extends OutputBase @Override public void format(OutputStream out, boolean booleanResult) { - Context cxt = ARQ.getContext().copy(); - if ( stylesheetURL != null ) - cxt.set(ResultSetWriterXML.xmlStylesheet, stylesheetURL); - cxt.set(ResultSetWriterXML.xmlInstruction, includeXMLinst); + Context cxt = setup(); ResultsWriter.create() .context(cxt) .lang(ResultSetLang.SPARQLResultSetXML) .build() .write(out, booleanResult); } + + private Context setup() { + Context cxt = ARQ.getContext().copy(); + setStylesheetURL(cxt, stylesheetURL); + setXMLinstruction(cxt, includeXMLinst); + return cxt; + } } http://git-wip-us.apache.org/repos/asf/jena/blob/1e1da957/jena-arq/src/main/java/org/apache/jena/sparql/sse/lang/ParseHandlerPlain.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/sse/lang/ParseHandlerPlain.java b/jena-arq/src/main/java/org/apache/jena/sparql/sse/lang/ParseHandlerPlain.java index 4badbfa..6a9be97 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/sse/lang/ParseHandlerPlain.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/sse/lang/ParseHandlerPlain.java @@ -26,6 +26,7 @@ import org.apache.jena.datatypes.RDFDatatype ; import org.apache.jena.datatypes.TypeMapper ; import org.apache.jena.graph.Node ; import org.apache.jena.graph.NodeFactory ; +import org.apache.jena.riot.lang.LabelToNode; import org.apache.jena.sparql.ARQConstants ; import org.apache.jena.sparql.core.Var ; import org.apache.jena.sparql.core.VarAlloc ; @@ -33,14 +34,13 @@ import org.apache.jena.sparql.sse.Item ; import org.apache.jena.sparql.sse.ItemList ; import org.apache.jena.sparql.sse.ItemLocation ; import org.apache.jena.sparql.sse.SSEParseException ; -import org.apache.jena.sparql.util.LabelToNodeMap ; public class ParseHandlerPlain implements ParseHandler { private ListStack listStack = new ListStack() ; private Item currentItem = null ; private int depth = 0 ; - private LabelToNodeMap bNodeLabels = LabelToNodeMap.createBNodeMap() ; + private LabelToNode bNodeLabels = LabelToNode.createScopeGlobal(); // Allocation of fresh variables. private VarAlloc varAlloc = new VarAlloc(ARQConstants.allocSSEUnamedVars) ; @@ -163,7 +163,7 @@ public class ParseHandlerPlain implements ParseHandler // Fresh anonymous bNode n = NodeFactory.createBlankNode() ; else - n = bNodeLabels.asNode(label) ; + n = bNodeLabels.get(null, label) ; Item item = Item.createNode(n, line, column) ; listAdd(item) ; } http://git-wip-us.apache.org/repos/asf/jena/blob/1e1da957/jena-arq/src/main/java/org/apache/jena/sparql/util/Context.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/util/Context.java b/jena-arq/src/main/java/org/apache/jena/sparql/util/Context.java index 97f47b1..783a6db 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/util/Context.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/util/Context.java @@ -344,27 +344,40 @@ public class Context { return x ; } - // Put any per-dataset execution global configuration state here. - public static Context setupContext(Context context, DatasetGraph dataset) { - if ( context == null ) - context = ARQ.getContext() ; // Already copied? - context = context.copy() ; - - if ( dataset != null && dataset.getContext() != null ) - // Copy per-dataset settings. - context.putAll(dataset.getContext()) ; + /** Setup a context using anouter context and a dataset. + * This adds the current time. + */ + public static Context setupContextExec(Context globalContext, DatasetGraph dataset) { + if ( globalContext == null ) + globalContext = ARQ.getContext(); + // Copy per-dataset settings. + Context dsgCxt = ( dataset != null && dataset.getContext() != null ) + ? dataset.getContext() + : null; + + Context context = mergeCopy(globalContext, dsgCxt); context.set(ARQConstants.sysCurrentTime, NodeFactoryExtra.nowAsDateTime()) ; // Allocators. - // context.set(ARQConstants.sysVarAllocNamed, new - // VarAlloc(ARQConstants.allocVarMarkerExec)) ; - // context.set(ARQConstants.sysVarAllocAnon, new - // VarAlloc(ARQConstants.allocVarAnonMarkerExec)) ; + // context.set(ARQConstants.sysVarAllocNamed, new VarAlloc(ARQConstants.allocVarMarkerExec)) ; + // context.set(ARQConstants.sysVarAllocAnon, new VarAlloc(ARQConstants.allocVarAnonMarkerExec)) ; // Add VarAlloc for variables and bNodes (this is not the parse name). - // More added later e.g. query (if there is a query), algebra form (in - // setOp) + // More added later e.g. query (if there is a query), algebra form (in setOp) + return context; + } + + /** Merge an outer (fglobal) and local context to produce a new context + * The new context is always a separate copy. + */ + public static Context mergeCopy(Context contextGlobal, Context contextLocal) { + if ( contextGlobal == null ) + contextGlobal = ARQ.getContext(); + Context context = contextGlobal.copy(); + if ( contextLocal != null ) + // Copy per-dataset settings. + context.putAll(contextLocal); return context ; } http://git-wip-us.apache.org/repos/asf/jena/blob/1e1da957/jena-arq/src/main/java/org/apache/jena/sparql/util/LabelToNodeMap.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/util/LabelToNodeMap.java b/jena-arq/src/main/java/org/apache/jena/sparql/util/LabelToNodeMap.java index 9a3ddde..9fc0714 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/util/LabelToNodeMap.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/util/LabelToNodeMap.java @@ -31,7 +31,6 @@ import org.apache.jena.sparql.core.VarAlloc ; /** Map from _:* form to bNodes or variables. */ public class LabelToNodeMap { - // Wil be replaced by LabelToNode in RIOT Map<String, Node> bNodeLabels = new HashMap<>() ; // Variables or bNodes? http://git-wip-us.apache.org/repos/asf/jena/blob/1e1da957/jena-arq/src/test/java/org/apache/jena/riot/lang/TestBlankNodeAllocator.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/test/java/org/apache/jena/riot/lang/TestBlankNodeAllocator.java b/jena-arq/src/test/java/org/apache/jena/riot/lang/TestBlankNodeAllocator.java index 5b409af..7ba6ad4 100644 --- a/jena-arq/src/test/java/org/apache/jena/riot/lang/TestBlankNodeAllocator.java +++ b/jena-arq/src/test/java/org/apache/jena/riot/lang/TestBlankNodeAllocator.java @@ -42,7 +42,7 @@ public class TestBlankNodeAllocator extends BaseTest } ; Factory fUIDAlloc = new Factory() { - @Override public BlankNodeAllocator create() { return new BlankNodeAllocatorTraditional() ; } + @Override public BlankNodeAllocator create() { return new BlankNodeAllocatorGlobal() ; } @Override public String toString() { return "UID" ; } } ; http://git-wip-us.apache.org/repos/asf/jena/blob/1e1da957/jena-arq/src/test/java/org/apache/jena/riot/lang/TestLabelToNode.java ---------------------------------------------------------------------- diff --git a/jena-arq/src/test/java/org/apache/jena/riot/lang/TestLabelToNode.java b/jena-arq/src/test/java/org/apache/jena/riot/lang/TestLabelToNode.java index 9b64d55..5299268 100644 --- a/jena-arq/src/test/java/org/apache/jena/riot/lang/TestLabelToNode.java +++ b/jena-arq/src/test/java/org/apache/jena/riot/lang/TestLabelToNode.java @@ -51,7 +51,7 @@ public class TestLabelToNode extends BaseTest } ; LabelToNodeFactory fScopeByDocumentOld = new LabelToNodeFactory() { - @Override public LabelToNode create() { return LabelToNode.createScopeByDocumentOld() ; } + @Override public LabelToNode create() { return LabelToNode.createScopeGlobal() ; } @Override public String toString() { return "ScopeByDocumentOld" ; } } ; LabelToNodeFactory fScopeByGraph = new LabelToNodeFactory() { http://git-wip-us.apache.org/repos/asf/jena/blob/1e1da957/jena-fuseki2/examples/service-tdb1-B.ttl ---------------------------------------------------------------------- diff --git a/jena-fuseki2/examples/service-tdb1-B.ttl b/jena-fuseki2/examples/service-tdb1-B.ttl index 09a2151..c85963f 100644 --- a/jena-fuseki2/examples/service-tdb1-B.ttl +++ b/jena-fuseki2/examples/service-tdb1-B.ttl @@ -29,4 +29,5 @@ ## # Query timeout on this dataset (milliseconds) ## ja:context [ ja:cxtName "arq:queryTimeout" ; ja:cxtValue "1000" ] ; ## # Default graph for query is the (read-only) union of all named graphs. + tdb:unionDefaultGraph true ; . http://git-wip-us.apache.org/repos/asf/jena/blob/1e1da957/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/DEF.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/DEF.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/DEF.java index cd7962a..71eaf08 100644 --- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/DEF.java +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/DEF.java @@ -77,19 +77,25 @@ public class DEF ) ; // Offer for SELECT + // This include application/xml and application/json. public static final AcceptList rsOfferTable = AcceptList.create(contentTypeResultsJSON, + contentTypeJSON, contentTypeTextCSV, contentTypeTextTSV, contentTypeResultsXML, + contentTypeXML, contentTypeResultsThrift, contentTypeTextPlain ) ; // Offer for ASK + // This include application/xml and application/json. public static final AcceptList rsOfferBoolean = AcceptList.create(contentTypeResultsJSON, + contentTypeJSON, contentTypeTextCSV, contentTypeTextTSV, contentTypeResultsXML, + contentTypeXML, contentTypeTextPlain ) ; http://git-wip-us.apache.org/repos/asf/jena/blob/1e1da957/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionBase.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionBase.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionBase.java index 3a61e90..367c12b 100644 --- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionBase.java +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionBase.java @@ -29,10 +29,8 @@ import javax.servlet.http.HttpServletResponse ; import org.apache.jena.atlas.RuntimeIOException ; import org.apache.jena.atlas.web.HttpException; -import org.apache.jena.query.ARQ ; import org.apache.jena.query.QueryCancelledException ; import org.apache.jena.riot.web.HttpNames ; -import org.apache.jena.sparql.util.Context ; import org.apache.jena.web.HttpSC ; import org.slf4j.Logger ; @@ -50,8 +48,6 @@ public abstract class ActionBase extends ServletBase public void init() { // log.info("["+Utils.className(this)+"] ServletContextName = "+getServletContext().getServletContextName()) ; // log.info("["+Utils.className(this)+"] ContextPath = "+getServletContext().getContextPath()) ; - - //super.init() ; } /** @@ -73,8 +69,6 @@ public abstract class ActionBase extends ServletBase // The response may be changed to a HttpServletResponseTracker response = action.response ; initResponse(request, response) ; - Context cxt = ARQ.getContext() ; - try { execCommonWorker(action) ; } catch (QueryCancelledException ex) { http://git-wip-us.apache.org/repos/asf/jena/blob/1e1da957/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/HttpAction.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/HttpAction.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/HttpAction.java index f3f658b..4052f2d 100644 --- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/HttpAction.java +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/HttpAction.java @@ -37,6 +37,7 @@ import org.apache.jena.sparql.core.DatasetGraph ; import org.apache.jena.sparql.core.DatasetGraphWrapper ; import org.apache.jena.sparql.core.Transactional ; import org.apache.jena.sparql.core.TransactionalLock ; +import org.apache.jena.sparql.util.Context; import org.slf4j.Logger ; /** @@ -70,6 +71,7 @@ public class HttpAction private DataService dataService = null ; private String datasetName = null ; // Dataset URI used (e.g. registry) private DatasetGraph dsg = null ; + private Context context = null ; // ---- @@ -165,6 +167,7 @@ public class HttpAction */ private void setDataset(DatasetGraph dsg) { this.dsg = dsg ; + this.context = Context.mergeCopy(Fuseki.getContext(), dsg.getContext()); if ( dsg == null ) return ; setTransactionalPolicy(dsg) ; @@ -191,6 +194,11 @@ public class HttpAction return dsg ; } + /** Return the Context for this {@code HttpAction}. */ + public Context getContext() { + return context ; + } + /** * Return the "Transactional" for this HttpAction. */ http://git-wip-us.apache.org/repos/asf/jena/blob/1e1da957/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseDataset.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseDataset.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseDataset.java index 0e6543b..99ed62c 100644 --- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseDataset.java +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseDataset.java @@ -117,7 +117,7 @@ public class ResponseDataset ServletOps.errorBadRequest("Can't determine output content type: "+contentType) ; try { - ResponseResultSet.setHttpResponse(action, contentType, charset) ; + ResponseOps.setHttpResponse(action, contentType, charset) ; response.setStatus(HttpSC.OK_200) ; ServletOutputStream out = response.getOutputStream() ; try { http://git-wip-us.apache.org/repos/asf/jena/blob/1e1da957/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseOps.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseOps.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseOps.java index 8a3a1c9..a7e825d 100644 --- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseOps.java +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseOps.java @@ -24,6 +24,7 @@ import java.util.Map ; import javax.servlet.http.HttpServletRequest ; +import org.apache.jena.riot.WebContent; import org.apache.jena.riot.web.HttpNames ; public class ResponseOps @@ -90,5 +91,25 @@ public class ResponseOps return value ; } + /** Basic settings, including Content-Type, for a response. */ + public static void setHttpResponse(HttpAction action, String contentType, String charset) { + // ---- Set up HTTP Response + // Stop caching (not that ?queryString URLs are cached anyway) + if ( true ) + ServletOps.setNoCache(action); + // See: http://www.w3.org/International/O-HTTP-charset.html + if ( contentType != null ) { + if ( charset != null && !isXML(contentType) ) + contentType = contentType + "; charset=" + charset; + action.log.trace("Content-Type for response: " + contentType); + action.response.setContentType(contentType); + } + } + + public static boolean isXML(String contentType) { + return contentType.equals(WebContent.contentTypeRDFXML) + || contentType.equals(WebContent.contentTypeResultsXML) + || contentType.equals(WebContent.contentTypeXML) ; + } } http://git-wip-us.apache.org/repos/asf/jena/blob/1e1da957/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseResultSet.java ---------------------------------------------------------------------- diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseResultSet.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseResultSet.java index f672019..cb9298f 100644 --- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseResultSet.java +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseResultSet.java @@ -19,15 +19,7 @@ package org.apache.jena.fuseki.servlets; import static java.lang.String.format ; -import static org.apache.jena.riot.WebContent.charsetUTF8 ; -import static org.apache.jena.riot.WebContent.contentTypeRDFXML ; -import static org.apache.jena.riot.WebContent.contentTypeResultsJSON ; -import static org.apache.jena.riot.WebContent.contentTypeResultsThrift ; -import static org.apache.jena.riot.WebContent.contentTypeResultsXML ; -import static org.apache.jena.riot.WebContent.contentTypeTextCSV ; -import static org.apache.jena.riot.WebContent.contentTypeTextPlain ; -import static org.apache.jena.riot.WebContent.contentTypeTextTSV ; -import static org.apache.jena.riot.WebContent.contentTypeXML ; +import static org.apache.jena.riot.WebContent.*; import java.io.IOException ; import java.util.HashMap ; @@ -38,7 +30,6 @@ 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.web.AcceptList ; import org.apache.jena.atlas.web.MediaType ; import org.apache.jena.fuseki.DEF ; @@ -47,10 +38,14 @@ import org.apache.jena.fuseki.conneg.ConNeg ; import org.apache.jena.query.QueryCancelledException ; import org.apache.jena.query.ResultSet ; import org.apache.jena.query.ResultSetFormatter ; -import org.apache.jena.riot.ResultSetMgr ; +import org.apache.jena.riot.Lang; import org.apache.jena.riot.WebContent ; import org.apache.jena.riot.resultset.ResultSetLang ; +import org.apache.jena.riot.resultset.ResultSetWriterRegistry; +import org.apache.jena.riot.resultset.rw.ResultsWriter; import org.apache.jena.sparql.core.Prologue ; +import org.apache.jena.sparql.resultset.XMLOutput; +import org.apache.jena.sparql.util.Context; import org.apache.jena.web.HttpSC ; import org.slf4j.Logger ; import org.slf4j.LoggerFactory ; @@ -81,7 +76,7 @@ public class ResponseResultSet ResponseOps.put(shortNamesResultSet, contentOutputThrift, contentTypeResultsThrift) ; } - interface OutputContent { void output(ServletOutputStream out) ; } + interface OutputContent { void output(ServletOutputStream out) throws IOException; } public static void doResponseResultSet(HttpAction action, Boolean booleanResult) { doResponseResultSet$(action, null, booleanResult, null, DEF.rsOfferBoolean) ; @@ -91,8 +86,6 @@ public class ResponseResultSet doResponseResultSet$(action, resultSet, null, qPrologue, DEF.rsOfferTable) ; } - // If we refactor the conneg into a single function, we can split boolean and result set handling. - // One or the other argument must be null private static void doResponseResultSet$(HttpAction action, ResultSet resultSet, Boolean booleanResult, @@ -112,11 +105,12 @@ public class ResponseResultSet } String mimeType = null ; + // -- Conneg MediaType i = ConNeg.chooseContentType(request, contentTypeOffer, DEF.acceptRSXML) ; if ( i != null ) mimeType = i.getContentType() ; - // Override content type + // -- Override content type from conneg. // Does &output= override? // Requested output type by the web form or &output= in the request. String outputField = ResponseOps.paramOutput(request, shortNamesResultSet) ; // Expands short names @@ -126,7 +120,7 @@ public class ResponseResultSet String serializationType = mimeType ; // Choose the serializer based on this. String contentType = mimeType ; // Set the HTTP respose header to this. - // Stylesheet - change to application/xml. + // -- Stylesheet - change to application/xml. final String stylesheetURL = ResponseOps.paramStylesheet(request) ; if ( stylesheetURL != null && Objects.equals(serializationType,contentTypeResultsXML) ) contentType = contentTypeXML ; @@ -136,78 +130,51 @@ public class ResponseResultSet if ( forceAccept != null ) contentType = contentTypeTextPlain ; - // Better : dispatch on MediaType - if ( Objects.equals(serializationType, contentTypeResultsXML) ) - sparqlXMLOutput(action, contentType, resultSet, stylesheetURL, booleanResult) ; - else if ( Objects.equals(serializationType, contentTypeResultsJSON) ) - jsonOutput(action, contentType, resultSet, booleanResult) ; - else if ( Objects.equals(serializationType, contentTypeTextPlain) ) + // Some kind of general dispatch is neater but there are quite a few special cases. + // + // JSON is special because of ?callback + // + // XML is special because of + // (1) charset is a feature of XML, not the response + // (2) ?stylesheet= + // + // Thrift is special because + // (1) charset is meaningless + // (2) there is no boolean result form. + // + // Text plain is special because of the formatting by prologue. + + Lang lang = WebContent.contentTypeToLangResultSet(serializationType); + if (lang == null ) + ServletOps.errorBadRequest("Not recognized for SPARQL results: "+serializationType) ; + if ( ! ResultSetWriterRegistry.isRegistered(lang) ) + ServletOps.errorBadRequest("No results writer for "+serializationType); + if ( ResultSetLang.SPARQLResultSetThrift.equals(lang) && booleanResult != null ) + ServletOps.errorBadRequest("Can't write a boolean result in thrift") ; + + Context cxt = action.getContext().copy(); + String charset = charsetUTF8; + String jsonCallback = null; + + if ( Objects.equals(serializationType, contentTypeResultsXML) ) { + charset = null; + XMLOutput.setStylesheetURL(cxt, stylesheetURL); + } + if ( Objects.equals(serializationType, contentTypeResultsJSON) ) { + jsonCallback = ResponseOps.paramCallback(action.request) ; + } + if (Objects.equals(serializationType, WebContent.contentTypeResultsThrift) ) { + charset = null; + } + + if ( Objects.equals(serializationType, contentTypeTextPlain) ) { + // While text form does not use context for a Prologue. textOutput(action, contentType, resultSet, qPrologue, booleanResult) ; - else if ( Objects.equals(serializationType, contentTypeTextCSV) ) - csvOutput(action, contentType, resultSet, booleanResult) ; - else if (Objects.equals(serializationType, contentTypeTextTSV) ) - tsvOutput(action, contentType, resultSet, booleanResult) ; - else if (Objects.equals(serializationType, WebContent.contentTypeResultsThrift) ) - thriftOutput(action, contentType, resultSet, booleanResult) ; - else - ServletOps.errorBadRequest("Can't determine output serialization: "+serializationType) ; - } - - - public static void setHttpResponse(HttpAction action, String contentType, String charset) { - // ---- Set up HTTP Response - // Stop caching (not that ?queryString URLs are cached anyway) - if ( true ) - ServletOps.setNoCache(action) ; - // See: http://www.w3.org/International/O-HTTP-charset.html - if ( contentType != null ) { - if ( charset != null && ! isXML(contentType) ) - contentType = contentType+"; charset="+charset ; - action.log.trace("Content-Type for response: "+contentType) ; - action.response.setContentType(contentType) ; + return; } - } - - private static boolean isXML(String contentType) { - return contentType.equals(contentTypeRDFXML) - || contentType.equals(contentTypeResultsXML) - || contentType.equals(contentTypeXML) ; - } - - private static void sparqlXMLOutput(HttpAction action, String contentType, ResultSet resultSet, String stylesheetURL, Boolean booleanResult) { - OutputContent proc = (ServletOutputStream out) -> { - if ( resultSet != null ) - ResultSetFormatter.outputAsXML(out, resultSet, stylesheetURL) ; - if ( booleanResult != null ) - ResultSetFormatter.outputAsXML(out, booleanResult.booleanValue(), stylesheetURL) ; - } ; - output(action, contentType, null, proc) ; - } - - private static void jsonOutput(HttpAction action, String contentType, ResultSet resultSet, Boolean booleanResult) { - OutputContent proc = (ServletOutputStream out) -> { - if ( resultSet != null ) - ResultSetFormatter.outputAsJSON(out, resultSet) ; - if ( booleanResult != null ) - ResultSetFormatter.outputAsJSON(out, booleanResult.booleanValue()) ; - } ; - - try { - String callback = ResponseOps.paramCallback(action.request) ; - ServletOutputStream out = action.response.getOutputStream() ; - - if ( callback != null ) { - callback = callback.replace("\r", "") ; - callback = callback.replace("\n", "") ; - out.print(callback) ; - out.println("(") ; - } - - output(action, contentType, charsetUTF8, proc) ; - - if ( callback != null ) - out.println(")") ; - } catch (IOException ex) { IO.exception(ex) ; } + + //Finally, the general case + generalOutput(action, lang, contentType, charset, cxt, jsonCallback, resultSet, booleanResult) ; } private static void textOutput(HttpAction action, String contentType, ResultSet resultSet, Prologue qPrologue, Boolean booleanResult) { @@ -222,39 +189,37 @@ public class ResponseResultSet output(action, contentType, charsetUTF8, proc) ; } - private static void csvOutput(HttpAction action, String contentType, ResultSet resultSet, Boolean booleanResult) { - OutputContent proc = (ServletOutputStream out) -> { - if ( resultSet != null ) - ResultSetFormatter.outputAsCSV(out, resultSet) ; - if ( booleanResult != null ) - ResultSetFormatter.outputAsCSV(out, booleanResult.booleanValue()) ; - } ; - output(action, contentType, charsetUTF8, proc) ; - } - - private static void tsvOutput(HttpAction action, String contentType, ResultSet resultSet, Boolean booleanResult) { + /** Any format */ + private static void generalOutput(HttpAction action, Lang rsLang, + String contentType, String charset, + Context context, String callback, + ResultSet resultSet, Boolean booleanResult) { + ResultsWriter rw = ResultsWriter.create() + .lang(rsLang) + .context(context) + .build(); OutputContent proc = (ServletOutputStream out) -> { + if ( callback != null ) { + String callbackFunction = callback; + callbackFunction = callbackFunction.replace("\r", "") ; + callbackFunction = callbackFunction.replace("\n", "") ; + out.print(callbackFunction) ; + out.println("(") ; + } if ( resultSet != null ) - ResultSetFormatter.outputAsTSV(out, resultSet) ; + rw.write(out, resultSet) ; if ( booleanResult != null ) - ResultSetFormatter.outputAsTSV(out, booleanResult.booleanValue()) ; - } ; - output(action, contentType, charsetUTF8, proc) ; - } - - private static void thriftOutput(HttpAction action, String contentType, ResultSet resultSet, Boolean booleanResult) { - OutputContent proc = (ServletOutputStream out) -> { - if ( resultSet != null ) - ResultSetMgr.write(out, resultSet, ResultSetLang.SPARQLResultSetThrift) ; - if ( booleanResult != null ) - xlog.error("Can't write boolen result in thrift") ; + rw.write(out, booleanResult.booleanValue()) ; + if ( callback != null ) + out.println(")") ; } ; - output(action, contentType, WebContent.charsetUTF8, proc) ; + output(action, contentType, charset, proc) ; } + // Sett HTTP response Execute OutputContent inside private static void output(HttpAction action, String contentType, String charset, OutputContent proc) { try { - setHttpResponse(action, contentType, charset) ; + ResponseOps.setHttpResponse(action, contentType, charset) ; action.response.setStatus(HttpSC.OK_200) ; ServletOutputStream out = action.response.getOutputStream() ; try {
