You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by an...@apache.org on 2024/11/14 13:38:24 UTC
(jena) 02/04: JENA-2837: Reduce use of model APIs in response handling
This is an automated email from the ASF dual-hosted git repository.
andy pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/jena.git
commit 8cc133e8e50459ef076ca54ecc81570bca3a44d5
Author: Andy Seaborne <an...@apache.org>
AuthorDate: Sat Nov 9 15:55:50 2024 +0000
JENA-2837: Reduce use of model APIs in response handling
---
.../apache/jena/sparql/exec/QueryExecResult.java | 4 +-
.../apache/jena/sparql/resultset/SPARQLResult.java | 10 +
.../access/AccessCtl_SPARQL_QueryDataset.java | 10 +-
.../apache/jena/fuseki/access/SecurityContext.java | 14 +-
.../fuseki/access/SecurityContextAllowAll.java | 9 +-
.../access/SecurityContextAllowNamedGraphs.java | 10 +-
.../fuseki/access/SecurityContextAllowNone.java | 9 +-
.../jena/fuseki/access/SecurityContextView.java | 16 +-
.../fuseki/access/TestSecurityFilterLocal.java | 52 +-
.../jena/fuseki/server/ActionServiceFactory.java | 43 --
.../jena/fuseki/server/DataServiceStatus.java | 15 -
.../org/apache/jena/fuseki/server/FusekiVocab.java | 21 +-
.../jena/fuseki/servlets/ResponseDataset.java | 109 +----
.../apache/jena/fuseki/servlets/ResponseJson.java | 99 +---
.../apache/jena/fuseki/servlets/ResponseOps.java | 107 -----
.../jena/fuseki/servlets/ResponseResultSet.java | 234 +--------
.../org/apache/jena/fuseki/servlets/Responses.java | 526 +++++++++++++++++++++
.../jena/fuseki/servlets/SPARQLQueryProcessor.java | 58 +--
.../apache/jena/fuseki/system/GraphLoadUtils.java | 27 +-
19 files changed, 662 insertions(+), 711 deletions(-)
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/exec/QueryExecResult.java b/jena-arq/src/main/java/org/apache/jena/sparql/exec/QueryExecResult.java
index 6185a40569..ed922e63e0 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/exec/QueryExecResult.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/exec/QueryExecResult.java
@@ -29,7 +29,7 @@ import org.apache.jena.sparql.resultset.SPARQLResult;
/**
* This class is for the outcome of {@link QueryExec}.
* <p>
- * See {@link SPARQLResult} for The Model-level equivalent.
+ * See {@link SPARQLResult} for the model-level equivalent.
*/
public class QueryExecResult {
@@ -58,7 +58,7 @@ public class QueryExecResult {
protected QueryExecResult() {}
- public QueryExecResult(Graph model) {
+ public QueryExecResult(Graph graph) {
set(graph);
}
diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/resultset/SPARQLResult.java b/jena-arq/src/main/java/org/apache/jena/sparql/resultset/SPARQLResult.java
index 692b657d7f..12d0b4510b 100644
--- a/jena-arq/src/main/java/org/apache/jena/sparql/resultset/SPARQLResult.java
+++ b/jena-arq/src/main/java/org/apache/jena/sparql/resultset/SPARQLResult.java
@@ -22,11 +22,13 @@ import java.util.Iterator;
import java.util.Objects;
import org.apache.jena.atlas.json.JsonObject;
+import org.apache.jena.graph.Graph;
import org.apache.jena.query.Dataset;
import org.apache.jena.query.DatasetFactory;
import org.apache.jena.query.ResultSet;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.ModelFactory;
+import org.apache.jena.sparql.core.DatasetGraph;
import org.apache.jena.sparql.exec.QueryExecResult;
/**
@@ -132,6 +134,14 @@ public class SPARQLResult {
return booleanResult;
}
+ public Graph getGraph() {
+ return getModel().getGraph();
+ }
+
+ public DatasetGraph getDatasetGraph() {
+ return dataset.asDatasetGraph();
+ }
+
public Model getModel() {
if ( !hasBeenSet )
throw new ResultSetException("Not set");
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AccessCtl_SPARQL_QueryDataset.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AccessCtl_SPARQL_QueryDataset.java
index 19d54e7b78..f4c452bc97 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AccessCtl_SPARQL_QueryDataset.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/AccessCtl_SPARQL_QueryDataset.java
@@ -28,9 +28,9 @@ import java.util.function.Function;
import org.apache.jena.atlas.lib.Pair;
import org.apache.jena.fuseki.servlets.*;
import org.apache.jena.query.Query;
-import org.apache.jena.query.QueryExecution;
import org.apache.jena.sparql.core.*;
import org.apache.jena.sparql.core.DynamicDatasets.DynamicDatasetGraph;
+import org.apache.jena.sparql.exec.QueryExec;
/** A Query {@link ActionService} that inserts a security filter on each query. */
final
@@ -105,7 +105,7 @@ public class AccessCtl_SPARQL_QueryDataset extends SPARQL_QueryDataset {
}
@Override
- protected QueryExecution createQueryExecution(HttpAction action, Query query, DatasetGraph target) {
+ protected QueryExec createQueryExec(HttpAction action, Query query, DatasetGraph target) {
if ( ! ALLOW_FROM ) {
if ( target instanceof DynamicDatasetGraph )
// Protocol query/FROM should have been caught by decideDataset
@@ -116,13 +116,13 @@ public class AccessCtl_SPARQL_QueryDataset extends SPARQL_QueryDataset {
// Dataset of the service, not computed by decideDataset.
DatasetGraph dsg = action.getActiveDSG();
if ( dsg == null )
- return super.createQueryExecution(action, query, target);
+ return super.createQueryExec(action, query, target);
if ( ! DataAccessCtl.isAccessControlled(dsg) )
- return super.createQueryExecution(action, query, target);
+ return super.createQueryExec(action, query, target);
SecurityContext sCxt = DataAccessLib.getSecurityContext(action, dsg, requestUser);
// A QueryExecution for controlled access
- QueryExecution qExec = sCxt.createQueryExecution(query, target);
+ QueryExec qExec = sCxt.createQueryExec(query, target);
return qExec;
}
}
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityContext.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityContext.java
index b9ce9bdefb..42e31532f5 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityContext.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityContext.java
@@ -31,6 +31,8 @@ import org.apache.jena.query.QueryExecution;
import org.apache.jena.query.QueryFactory;
import org.apache.jena.sparql.core.DatasetGraph;
import org.apache.jena.sparql.core.Quad;
+import org.apache.jena.sparql.exec.QueryExec;
+import org.apache.jena.sparql.exec.QueryExecutionAdapter;
import org.apache.jena.sparql.util.Context;
/** A {@link SecurityContext} is the things actor (user, role) is allowed to do.
@@ -78,11 +80,19 @@ public interface SecurityContext {
public boolean visableDefaultGraph();
+ /** @deprecated Use {@link #createQueryExec(Query, DatasetGraph)} */
+ @Deprecated(forRemoval = true)
public default QueryExecution createQueryExecution(String queryString, DatasetGraph dsg) {
return createQueryExecution(QueryFactory.create(queryString), dsg);
}
- public QueryExecution createQueryExecution(Query query, DatasetGraph dsg);
+ /** @deprecated Use {@link #createQueryExec(Query, DatasetGraph)} */
+ @Deprecated(forRemoval = true)
+ public default QueryExecution createQueryExecution(Query query, DatasetGraph dsg) {
+ return QueryExecutionAdapter.adapt(createQueryExec(query, dsg));
+ }
+
+ public QueryExec createQueryExec(Query query, DatasetGraph dsg);
/**
* Quad filter to reflect the security policy of this {@link SecurityContext}. It is
@@ -97,7 +107,7 @@ public interface SecurityContext {
* Throws {@link IllegalArgumentException} if {@link DatasetGraph} is not a TDB1 or TDB2 backed dataset.
* May throw {@link UnsupportedOperationException}.
*/
- public default void filterTDB(DatasetGraph dsg, QueryExecution qExec) {
+ public default void filterTDB(DatasetGraph dsg, QueryExec qExec) {
if ( ! org.apache.jena.tdb1.sys.TDBInternal.isTDB1(dsg) || ! org.apache.jena.tdb2.sys.TDBInternal.isTDB2(dsg) )
throw new IllegalArgumentException("Not a TDB database");
throw new UnsupportedOperationException();
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityContextAllowAll.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityContextAllowAll.java
index 125b984f3a..80b7b959de 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityContextAllowAll.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityContextAllowAll.java
@@ -23,10 +23,9 @@ import java.util.function.Predicate;
import org.apache.jena.graph.Node;
import org.apache.jena.query.Query;
-import org.apache.jena.query.QueryExecution;
-import org.apache.jena.query.QueryExecutionFactory;
import org.apache.jena.sparql.core.DatasetGraph;
import org.apache.jena.sparql.core.Quad;
+import org.apache.jena.sparql.exec.QueryExec;
/** A {@link SecurityContext} that allow any graph, default or named. */
public class SecurityContextAllowAll implements SecurityContext {
@@ -43,8 +42,8 @@ public class SecurityContextAllowAll implements SecurityContext {
public boolean visableDefaultGraph() { return true; }
@Override
- public QueryExecution createQueryExecution(Query query, DatasetGraph dsg) {
- return QueryExecutionFactory.create(query, dsg);
+ public QueryExec createQueryExec(Query query, DatasetGraph dsg) {
+ return QueryExec.dataset(dsg).query(query).build();
}
/**
@@ -56,7 +55,7 @@ public class SecurityContextAllowAll implements SecurityContext {
public Predicate<Quad> predicateQuad() { return q->true; }
@Override
- public void filterTDB(DatasetGraph dsg, QueryExecution qExec) {
+ public void filterTDB(DatasetGraph dsg, QueryExec qExec) {
// No filter necessary.
// Predicate<?> pred = tuple->true;
// qExec.getContext().set(GraphFilter.getContextKey(dsg), pred);
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityContextAllowNamedGraphs.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityContextAllowNamedGraphs.java
index 33c64e1013..c54006fb34 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityContextAllowNamedGraphs.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityContextAllowNamedGraphs.java
@@ -23,10 +23,9 @@ import java.util.function.Predicate;
import org.apache.jena.graph.Node;
import org.apache.jena.query.Query;
-import org.apache.jena.query.QueryExecution;
-import org.apache.jena.query.QueryExecutionFactory;
import org.apache.jena.sparql.core.DatasetGraph;
import org.apache.jena.sparql.core.Quad;
+import org.apache.jena.sparql.exec.QueryExec;
/** A {@link SecurityContext} that allows access to the default graph but not named graphs. */
public class SecurityContextAllowNamedGraphs implements SecurityContext {
@@ -43,9 +42,8 @@ public class SecurityContextAllowNamedGraphs implements SecurityContext {
public boolean visableDefaultGraph() { return true; }
@Override
- public QueryExecution createQueryExecution(Query query, DatasetGraph dsg) {
-
- return QueryExecutionFactory.create(query, dsg);
+ public QueryExec createQueryExec(Query query, DatasetGraph dsg) {
+ return QueryExec.dataset(dsg).query(query).build();
}
/**
@@ -54,5 +52,5 @@ public class SecurityContextAllowNamedGraphs implements SecurityContext {
* efficient.
*/
@Override
- public Predicate<Quad> predicateQuad() { return q-> ! Quad.isDefaultGraph(q.getGraph()); }
+ public Predicate<Quad> predicateQuad() { return q -> ! Quad.isDefaultGraph(q.getGraph()); }
}
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityContextAllowNone.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityContextAllowNone.java
index dcf8da9f48..d9512f49f3 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityContextAllowNone.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityContextAllowNone.java
@@ -24,11 +24,10 @@ import java.util.function.Predicate;
import org.apache.jena.graph.Node;
import org.apache.jena.query.Query;
-import org.apache.jena.query.QueryExecution;
-import org.apache.jena.query.QueryExecutionFactory;
import org.apache.jena.sparql.core.DatasetGraph;
import org.apache.jena.sparql.core.DatasetGraphSink;
import org.apache.jena.sparql.core.Quad;
+import org.apache.jena.sparql.exec.QueryExec;
/** A {@link SecurityContext} that does not allow any access. */
public class SecurityContextAllowNone implements SecurityContext {
@@ -44,15 +43,15 @@ public class SecurityContextAllowNone implements SecurityContext {
public boolean visableDefaultGraph() { return false; }
@Override
- public QueryExecution createQueryExecution(Query query, DatasetGraph dsg) {
- return QueryExecutionFactory.create(query, DatasetGraphSink.create());
+ public QueryExec createQueryExec(Query query, DatasetGraph dsg) {
+ return QueryExec.dataset(DatasetGraphSink.create()).query(query).build();
}
@Override
public Predicate<Quad> predicateQuad() { return q -> false; }
@Override
- public void filterTDB(DatasetGraph dsg, QueryExecution qExec) {
+ public void filterTDB(DatasetGraph dsg, QueryExec qExec) {
Predicate<?> pred = tuple->false;
qExec.getContext().set(GraphFilter.getContextKey(dsg), pred);
}
diff --git a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityContextView.java b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityContextView.java
index 10be3e25f2..a0826485ec 100644
--- a/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityContextView.java
+++ b/jena-fuseki2/jena-fuseki-access/src/main/java/org/apache/jena/fuseki/access/SecurityContextView.java
@@ -27,10 +27,9 @@ import java.util.function.Predicate;
import org.apache.jena.graph.Node;
import org.apache.jena.query.Query;
import org.apache.jena.query.QueryExecution;
-import org.apache.jena.query.QueryExecutionFactory;
-import org.apache.jena.query.QueryFactory;
import org.apache.jena.sparql.core.DatasetGraph;
import org.apache.jena.sparql.core.Quad;
+import org.apache.jena.sparql.exec.QueryExec;
import org.apache.jena.sparql.util.Context;
import org.apache.jena.sparql.util.NodeUtils;
import org.apache.jena.tdb1.TDB1Factory;
@@ -87,20 +86,15 @@ public class SecurityContextView implements SecurityContext {
}
@Override
- public QueryExecution createQueryExecution(String queryString, DatasetGraph dsg) {
- return createQueryExecution(QueryFactory.create(queryString), dsg);
- }
-
- @Override
- public QueryExecution createQueryExecution(Query query, DatasetGraph dsg) {
+ public QueryExec createQueryExec(Query query, DatasetGraph dsg) {
if ( isAccessControlledTDB(dsg) ) {
- QueryExecution qExec = QueryExecutionFactory.create(query, dsg);
+ QueryExec qExec = QueryExec.dataset(dsg).query(query).build();
filterTDB(dsg, qExec);
return qExec;
}
// Not TDB - do by selecting graphs.
DatasetGraph dsgA = DataAccessCtl.filteredDataset(dsg, this);
- return QueryExecutionFactory.create(query, dsgA);
+ return QueryExec.dataset(dsgA).query(query).build();
}
/**
@@ -108,7 +102,7 @@ public class SecurityContextView implements SecurityContext {
* {@link QueryExecution}. This does not modify the {@link DatasetGraph}.
*/
@Override
- public void filterTDB(DatasetGraph dsg, QueryExecution qExec) {
+ public void filterTDB(DatasetGraph dsg, QueryExec qExec) {
GraphFilter<?> predicate = predicate(dsg);
qExec.getContext().set(predicate.getContextKey(), predicate);
}
diff --git a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterLocal.java b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterLocal.java
index 03630f40d0..af42f6a5f4 100644
--- a/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterLocal.java
+++ b/jena-fuseki2/jena-fuseki-access/src/test/java/org/apache/jena/fuseki/access/TestSecurityFilterLocal.java
@@ -25,35 +25,30 @@ import java.util.*;
import java.util.function.Function;
import java.util.stream.Stream;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
+
import org.apache.jena.atlas.iterator.Iter;
import org.apache.jena.atlas.lib.Creator;
import org.apache.jena.atlas.lib.SetUtils;
import org.apache.jena.atlas.lib.StrUtils;
import org.apache.jena.graph.Graph;
import org.apache.jena.graph.Node;
-import org.apache.jena.query.Dataset;
-import org.apache.jena.query.DatasetFactory;
-import org.apache.jena.query.QueryExecution;
-import org.apache.jena.query.QueryExecutionFactory;
-import org.apache.jena.query.QuerySolution;
-import org.apache.jena.rdf.model.Model;
-import org.apache.jena.rdf.model.ModelFactory;
-import org.apache.jena.rdf.model.RDFNode;
import org.apache.jena.riot.Lang;
import org.apache.jena.riot.RDFParser;
import org.apache.jena.sparql.core.DatasetGraph;
import org.apache.jena.sparql.core.DatasetGraphFactory;
import org.apache.jena.sparql.core.Quad;
+import org.apache.jena.sparql.engine.binding.Binding;
+import org.apache.jena.sparql.exec.QueryExec;
import org.apache.jena.sparql.sse.SSE;
import org.apache.jena.system.Txn;
import org.apache.jena.tdb1.TDB1;
import org.apache.jena.tdb1.TDB1Factory;
import org.apache.jena.tdb2.DatabaseMgr;
import org.apache.jena.tdb2.TDB2;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
/** Test a controlled Dataset with access by TDB filter or general DatasetGraphFiltered. */
@RunWith(Parameterized.class)
@@ -117,26 +112,19 @@ public class TestSecurityFilterLocal {
final DatasetGraph dsg1 = applyFilterDSG
? DataAccessCtl.filteredDataset(dsg, sCxt)
: dsg;
- Dataset ds = DatasetFactory.wrap(dsg1);
return
- Txn.calculateRead(ds, ()->{
- try(QueryExecution qExec = QueryExecutionFactory.create(queryString, ds)) {
-// if ( applyFilterTDB && ! sCxt.equals(SecurityContext.NONE)) {
-// ((SecurityContextView)sCxt).filterTDB(dsg1, qExec);
-// }
+ Txn.calculateRead(dsg1, ()->{
+ try(QueryExec qExec = QueryExec.dataset(dsg1).query(queryString).build()) {
if ( applyFilterTDB )
sCxt.filterTDB(dsg1, qExec);
- List<QuerySolution> results = Iter.toList(qExec.execSelect());
- Stream<Node> stream = results.stream()
- .map(qs->qs.get("s"))
- .filter(Objects::nonNull)
- .map(RDFNode::asNode);
+ List<Binding> results = Iter.toList(qExec.select());
+ Stream<Node> stream = results.stream().map(qs -> qs.get("s")).filter(Objects::nonNull);
return SetUtils.toSet(stream);
}
});
}
- private Set<Node> subjects(DatasetGraph dsg, Function<DatasetGraph, Graph> graphChoice, String queryString, SecurityContext sCxt) {
+ private Set<Node> subjects(DatasetGraph dsg, Function<DatasetGraph, Graph> graphChoice, String queryString, SecurityContext sCxt) {
final DatasetGraph dsg1 = applyFilterDSG
? DataAccessCtl.filteredDataset(dsg, sCxt)
: dsg;
@@ -144,14 +132,13 @@ public class TestSecurityFilterLocal {
if ( graph == null )
// Can't see the graph.
return Collections.emptySet();
- Model model = ModelFactory.createModelForGraph(graph);
return
Txn.calculateRead(testdsg, ()->{
- try(QueryExecution qExec = QueryExecutionFactory.create(queryString, model)) {
+ try(QueryExec qExec = QueryExec.graph(graph).query(queryString).build()) {
if ( applyFilterTDB )
sCxt.filterTDB(dsg1, qExec);
- List<QuerySolution> results = Iter.toList(qExec.execSelect());
- Stream<Node> stream = results.stream().map(qs->qs.get("s")).filter(Objects::nonNull).map(RDFNode::asNode);
+ List<Binding> results = Iter.toList(qExec.select());
+ Stream<Node> stream = results.stream().map(qs->qs.get("s")).filter(Objects::nonNull);
return SetUtils.toSet(stream);
}
});
@@ -162,14 +149,13 @@ public class TestSecurityFilterLocal {
final DatasetGraph dsg1 = applyFilterDSG
? DataAccessCtl.filteredDataset(dsg, sCxt)
: dsg;
- Dataset ds = DatasetFactory.wrap(dsg1);
return
- Txn.calculateRead(ds, ()->{
- try(QueryExecution qExec = QueryExecutionFactory.create(queryGraphNames, ds)) {
+ Txn.calculateRead(dsg1, ()->{
+ try(QueryExec qExec = QueryExec.dataset(dsg1).query(queryGraphNames).build()) {
if ( applyFilterTDB )
sCxt.filterTDB(dsg1, qExec);
- List<QuerySolution> results = Iter.toList(qExec.execSelect());
- Stream<Node> stream = results.stream().map(qs->qs.get("g")).filter(Objects::nonNull).map(RDFNode::asNode);
+ List<Binding> results = Iter.toList(qExec.select());
+ Stream<Node> stream = results.stream().map(qs->qs.get("g")).filter(Objects::nonNull);
return SetUtils.toSet(stream);
}
});
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/ActionServiceFactory.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/ActionServiceFactory.java
deleted file mode 100644
index bf396db2f2..0000000000
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/ActionServiceFactory.java
+++ /dev/null
@@ -1,43 +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.fuseki.server;
-
-import org.apache.jena.fuseki.servlets.ActionProcessor;
-import org.apache.jena.fuseki.servlets.ActionService;
-import org.apache.jena.rdf.model.Resource;
-
-/**
- * Factory for creating the {@link ActionProcessor} for an endpoint. Each endpoint -
- * a service name for a dataset has an associated {@link ActionProcessor} to handle
- * the request. This created when the configuration file is read or the server built
- * programmatically.
- *
- * {@link ActionService} is a common super class of request handlers, includes counters
- * for operations and has a validate-execute lifecycle.
- *
- * @see ActionService
- */
-@FunctionalInterface
-public interface ActionServiceFactory {
- /**
- * Create an {@link ActionServiceFactory} (it can be shared with endpoints), given the description
- * which is a link into the server configuration graph.
- */
- public ActionService newActionService(Operation operation, Resource endpoint);
-}
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/DataServiceStatus.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/DataServiceStatus.java
index 20047616fa..43706685f5 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/DataServiceStatus.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/DataServiceStatus.java
@@ -18,8 +18,6 @@
package org.apache.jena.fuseki.server;
-import org.apache.jena.rdf.model.Resource;
-
public enum DataServiceStatus {
UNINITIALIZED("Uninitialized"),
@@ -30,17 +28,4 @@ public enum DataServiceStatus {
public final String name;
DataServiceStatus(String string) { name = string; }
-
- public static DataServiceStatus status(Resource r) {
- if ( FusekiVocab.stateActive.equals(r) )
- return ACTIVE;
- if ( FusekiVocab.stateOffline.equals(r) )
- return OFFLINE;
- if ( FusekiVocab.stateClosing.equals(r) )
- return CLOSING;
- if ( FusekiVocab.stateClosed.equals(r) )
- return CLOSED;
- return null;
- }
}
-
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/FusekiVocab.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/FusekiVocab.java
index 8efe594790..47974435d9 100755
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/FusekiVocab.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/FusekiVocab.java
@@ -21,15 +21,13 @@ package org.apache.jena.fuseki.server;
import org.apache.jena.fuseki.FusekiException;
import org.apache.jena.irix.IRIException;
import org.apache.jena.irix.IRIx;
-import org.apache.jena.rdf.model.Model;
-import org.apache.jena.rdf.model.ModelFactory;
import org.apache.jena.rdf.model.Property;
import org.apache.jena.rdf.model.Resource;
+import org.apache.jena.rdf.model.ResourceFactory;
public class FusekiVocab
{
public static String NS = "http://jena.apache.org/fuseki#";
- private static Model model = ModelFactory.createDefaultModel();
public static final Resource tServer = resource("Server");
@@ -92,21 +90,8 @@ public class FusekiVocab
public static final Resource opPREFIXES_R = resource("prefixes-r");
public static final Resource opPREFIXES_RW = resource("prefixes-rw");
- // Internal
- private static final String stateNameActive = DataServiceStatus.ACTIVE.name;
- private static final String stateNameOffline = DataServiceStatus.OFFLINE.name;
- private static final String stateNameClosing = DataServiceStatus.CLOSING.name;
- private static final String stateNameClosed = DataServiceStatus.CLOSED.name;
-
- public static final Resource stateActive = resource(stateNameActive);
- public static final Resource stateOffline = resource(stateNameOffline);
- public static final Resource stateClosing = resource(stateNameClosing);
- public static final Resource stateClosed = resource(stateNameClosed);
-
-// public static final Property pStatus = property("status");
-
- private static Resource resource(String localname) { return model.createResource(iri(localname)); }
- private static Property property(String localname) { return model.createProperty(iri(localname)); }
+ private static Resource resource(String localname) { return ResourceFactory.createResource(iri(localname)); }
+ private static Property property(String localname) { return ResourceFactory.createProperty(iri(localname)); }
private static String iri(String localname) {
String uri = NS + localname;
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 43eafc7ea6..5d9635ffc3 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
@@ -18,121 +18,24 @@
package org.apache.jena.fuseki.servlets;
-import static org.apache.jena.riot.WebContent.*;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import jakarta.servlet.ServletOutputStream;
-import jakarta.servlet.http.HttpServletRequest;
-
-import org.apache.jena.atlas.web.MediaType;
-import org.apache.jena.fuseki.DEF;
-import org.apache.jena.fuseki.Fuseki;
-import org.apache.jena.fuseki.system.ConNeg;
-import org.apache.jena.fuseki.system.FusekiNetLib;
import org.apache.jena.query.Dataset;
import org.apache.jena.query.DatasetFactory;
import org.apache.jena.rdf.model.Model;
-import org.apache.jena.riot.Lang;
-import org.apache.jena.riot.RDFFormat;
-import org.apache.jena.riot.RDFLanguages;
-import org.apache.jena.shared.JenaException;
-import org.apache.jena.web.HttpSC;
+import org.apache.jena.sparql.exec.QueryExec;
+/**
+ * @deprecated Use {@link QueryExec} and {@link Responses}.
+ */
+@Deprecated(forRemoval = true)
public class ResponseDataset
{
- // Short names for "output="
- private static final String contentOutputJSONLD = "json-ld";
- private static final String contentOutputJSONRDF = "json-rdf";
- private static final String contentOutputJSON = "json";
- private static final String contentOutputXML = "xml";
- private static final String contentOutputText = "text";
- private static final String contentOutputTTL = "ttl";
- private static final String contentOutputTurtle = "turtle";
- private static final String contentOutputNT = "nt";
- private static final String contentOutputTriG = "trig";
- private static final String contentOutputNQuads = "n-quads";
-
- public static Map<String,String> shortNamesModel = new HashMap<>();
- static {
- // Some short names. keys are lowercase.
- ResponseOps.put(shortNamesModel, contentOutputJSONLD, contentTypeJSONLD);
- ResponseOps.put(shortNamesModel, contentOutputJSONRDF, contentTypeRDFJSON);
- ResponseOps.put(shortNamesModel, contentOutputJSON, contentTypeJSONLD);
- ResponseOps.put(shortNamesModel, contentOutputXML, contentTypeRDFXML);
- ResponseOps.put(shortNamesModel, contentOutputText, contentTypeTurtle);
- ResponseOps.put(shortNamesModel, contentOutputTTL, contentTypeTurtle);
- ResponseOps.put(shortNamesModel, contentOutputTurtle, contentTypeTurtle);
- ResponseOps.put(shortNamesModel, contentOutputNT, contentTypeNTriples);
- ResponseOps.put(shortNamesModel, contentOutputNQuads, contentTypeNQuads);
- ResponseOps.put(shortNamesModel, contentOutputTriG, contentTypeTriG);
- }
-
public static void doResponseModel(HttpAction action, Model model) {
Dataset ds = DatasetFactory.wrap(model);
ResponseDataset.doResponseDataset(action, ds);
}
public static void doResponseDataset(HttpAction action, Dataset dataset) {
- HttpServletRequest request = action.getRequest();
-
- String mimeType = null; // Header request type
-
- MediaType i = ConNeg.chooseContentType(request, DEF.constructOffer, DEF.acceptTurtle);
- if ( i != null )
- mimeType = i.getContentTypeStr();
-
- String outputField = ResponseOps.paramOutput(request, shortNamesModel);
- if ( outputField != null )
- mimeType = outputField;
-
- String writerMimeType = mimeType;
-
- if ( mimeType == null ) {
- Fuseki.actionLog.warn("Can't find MIME type for response");
- String x = FusekiNetLib.getAccept(request);
- String msg;
- if ( x == null )
- msg = "No Accept: header";
- else
- msg = "Accept: " + x + " : Not understood";
- ServletOps.error(HttpSC.NOT_ACCEPTABLE_406, msg);
- }
-
- String contentType = mimeType;
- String charset = charsetUTF8;
-
- String forceAccept = ResponseOps.paramForceAccept(request);
- if ( forceAccept != null ) {
- contentType = forceAccept;
- charset = charsetUTF8;
- }
-
- Lang lang = RDFLanguages.contentTypeToLang(contentType);
- if ( lang == null )
- ServletOps.errorBadRequest("Can't determine output content type: "+contentType);
- RDFFormat format = ActionLib.getNetworkFormatForLang(lang);
-
- try {
- ServletOps.success(action);
- ServletOutputStream out = action.getResponseOutputStream();
- try {
- // Use the Content-Type from the content negotiation.
- if ( RDFLanguages.isQuads(lang) )
- ActionLib.datasetResponse(action, dataset.asDatasetGraph(), format, contentType);
- else
- ActionLib.graphResponse(action, dataset.getDefaultModel().getGraph(), format, contentType);
- out.flush();
- } catch (JenaException ex) {
- ServletOps.errorOccurred("Failed to write output: "+ex.getMessage(), ex);
- }
- }
- catch (ActionErrorException ex) { throw ex; }
- catch (Exception ex) {
- action.log.info("Exception while writing the response model: "+ex.getMessage(), ex);
- ServletOps.errorOccurred("Exception while writing the response model: "+ex.getMessage(), ex);
- }
+ Responses.doResponseDataset(action, dataset.asDatasetGraph());
}
}
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseJson.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseJson.java
index 9f02811f46..8c7ce104ed 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseJson.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseJson.java
@@ -18,34 +18,17 @@
package org.apache.jena.fuseki.servlets;
-import static java.lang.String.format;
-
-import java.io.IOException;
-import java.io.PrintStream;
import java.util.Iterator;
-import jakarta.servlet.ServletOutputStream;
-
-import org.apache.commons.lang3.StringUtils;
import org.apache.jena.atlas.json.JsonObject;
-import org.apache.jena.atlas.lib.StrUtils;
-import org.apache.jena.fuseki.FusekiException;
-import org.apache.jena.fuseki.servlets.ResponseResultSet.OutputContent;
-import org.apache.jena.query.QueryCancelledException;
-import org.apache.jena.query.ResultSetFormatter;
-import org.apache.jena.riot.WebContent;
-import org.apache.jena.web.HttpSC;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.apache.jena.sparql.exec.QueryExec;
/**
- * Responsible for handling JSON response output.
+ * @deprecated Use {@link QueryExec} and {@link Responses}.
*/
+@Deprecated(forRemoval = true)
public class ResponseJson {
- // Loggers
- private static Logger xlog = LoggerFactory.getLogger(ResponseJson.class);
-
/**
* Outputs a JSON query result
*
@@ -53,80 +36,6 @@ public class ResponseJson {
* @param jsonItem a ResultSetJsonStream instance
*/
public static void doResponseJson(HttpAction action, Iterator<JsonObject> jsonItem) {
- if ( jsonItem == null ) {
- xlog.warn("doResponseJson: Result set is null");
- throw new FusekiException("Result set is null");
- }
-
- jsonOutput(action, jsonItem);
- }
-
- private static void jsonOutput(HttpAction action, final Iterator<JsonObject> jsonItems) {
- OutputContent proc = out-> {
- if ( jsonItems != null )
- ResultSetFormatter.output(out, jsonItems);
- };
-
- try {
- String callback = ResponseOps.paramCallback(action.getRequest());
- ServletOutputStream out = action.getResponseOutputStream();
-
- if ( callback != null ) {
- callback = StringUtils.replaceChars(callback, "\r", "");
- callback = StringUtils.replaceChars(callback, "\n", "");
- out.write(StrUtils.asUTF8bytes(callback));
- out.write('('); out.write('\n');
- }
-
- output(action, "application/json", WebContent.charsetUTF8, proc);
-
- if ( callback != null ) {
- out.write(')'); out.write('\n');
- }
- } catch (IOException ex) {
- ServletOps.errorOccurred(ex);
- }
- }
-
- private static void output(HttpAction action, String contentType, String charset, OutputContent proc) {
- try {
- setHttpResponse(action, contentType, charset);
- action.setResponseStatus(HttpSC.OK_200);
- ServletOutputStream out = action.getResponseOutputStream();
- try {
- proc.output(out);
- out.flush();
- } catch (QueryCancelledException ex) {
- // Bother. Status code 200 already sent.
- xlog.info(format("[%d] Query Cancelled - results truncated (but 200 already sent)", action.id));
- PrintStream ps = new PrintStream(out);
- ps.println();
- ps.println("## Query cancelled due to timeout during execution ##");
- ps.println("## **** Incomplete results **** ##");
- ps.flush();
- out.flush();
- // No point raising an exception - 200 was sent already.
- // errorOccurred(ex);
- }
- // Includes client gone.
- } catch (IOException ex) {
- ServletOps.errorOccurred(ex);
- }
- // Do not call httpResponse.flushBuffer(); here - Jetty closes the stream if
- // it is a gzip stream
- // then the JSON callback closing details can't be added.
- }
-
- public static void setHttpResponse(HttpAction action, String contentType, String charset) {
- // ---- Set up HTTP Response
- // Stop caching (not that ?queryString URLs are cached anyway)
- ServletOps.setNoCache(action);
- // See: http://www.w3.org/International/O-HTTP-charset.html
- if ( contentType != null ) {
- if ( charset != null )
- contentType = contentType + "; charset=" + charset;
- xlog.trace("Content-Type for response: " + contentType);
- action.setResponseContentType(contentType);
- }
+ Responses.doResponseJson(action, jsonItem);
}
}
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
deleted file mode 100644
index d98afc439a..0000000000
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ResponseOps.java
+++ /dev/null
@@ -1,107 +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.fuseki.servlets;
-
-import static org.apache.jena.atlas.lib.Lib.lowercase;
-
-import java.io.IOException;
-import java.util.Map;
-
-import jakarta.servlet.http.HttpServletRequest;
-import org.apache.jena.riot.WebContent;
-import org.apache.jena.riot.web.HttpNames;
-
-class ResponseOps {
- // Helpers
- public static void put(Map<String, String> map, String key, String value) {
- map.put(lowercase(key), value);
- }
-
- public static boolean isEOFexception(IOException ioEx) {
- if ( ioEx.getClass().getName().equals("org.mortbay.jetty.EofException eofEx") )
- return true;
- if ( ioEx instanceof java.io.EOFException )
- return true;
- return false;
- }
-
- public static String paramForceAccept(HttpServletRequest request) {
- String x = fetchParam(request, HttpNames.paramForceAccept);
- return x;
- }
-
- public static String paramStylesheet(HttpServletRequest request) {
- return fetchParam(request, HttpNames.paramStyleSheet);
- }
-
- public static String paramOutput(HttpServletRequest request, Map<String, String> map) {
- // Two names.
- String x = fetchParam(request, HttpNames.paramOutput1);
- if ( x == null )
- x = fetchParam(request, HttpNames.paramOutput2);
- if ( x == null )
- x = fetchParam(request, HttpNames.paramOutput3);
- return expandShortName(x, map);
- }
-
- public static String expandShortName(String str, Map<String, String> map) {
- if ( str == null )
- return null;
- // Force keys to lower case. See put() above.
- String key = lowercase(str);
- String str2 = map.get(key);
- if ( str2 == null )
- return str;
- return str2;
- }
-
- public static String paramCallback(HttpServletRequest request) {
- return fetchParam(request, HttpNames.paramCallback);
- }
-
- public static String fetchParam(HttpServletRequest request, String parameterName) {
- String value = request.getParameter(parameterName);
- if ( value != null ) {
- value = value.trim();
- if ( value.length() == 0 )
- value = null;
- }
- return value;
- }
-
- /** 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.setResponseContentType(contentType);
- }
- }
-
- public static boolean isXML(String contentType) {
- return contentType.equals(WebContent.contentTypeRDFXML) || contentType.equals(WebContent.contentTypeResultsXML)
- || contentType.equals(WebContent.contentTypeXML);
- }
-}
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 c20e35bcc1..51a51c4dfa 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
@@ -18,238 +18,24 @@
package org.apache.jena.fuseki.servlets;
-import static java.lang.String.format;
-import static org.apache.jena.riot.WebContent.*;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.PrintStream;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-
-import jakarta.servlet.http.HttpServletRequest;
-
-import org.apache.jena.atlas.lib.StrUtils;
-import org.apache.jena.atlas.web.AcceptList;
-import org.apache.jena.atlas.web.MediaType;
-import org.apache.jena.fuseki.DEF;
-import org.apache.jena.fuseki.FusekiException;
-import org.apache.jena.fuseki.system.ConNeg;
-import org.apache.jena.query.QueryCancelledException;
import org.apache.jena.query.ResultSet;
-import org.apache.jena.query.ResultSetFormatter;
-import org.apache.jena.riot.Lang;
-import org.apache.jena.riot.WebContent;
-import org.apache.jena.riot.resultset.ResultSetWriterRegistry;
-import org.apache.jena.riot.rowset.rw.RowSetWriterXML;
import org.apache.jena.sparql.core.Prologue;
-import org.apache.jena.sparql.resultset.ResultsWriter;
-import org.apache.jena.sparql.util.Context;
-import org.apache.jena.web.HttpSC;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.apache.jena.sparql.exec.QueryExec;
+import org.apache.jena.sparql.exec.RowSet;
-/** This is the content negotiation for each kind of SPARQL query result */
+/**
+ * This is the content negotiation for each kind of SPARQL query result.
+ * @deprecated Use {@link QueryExec} and {@link Responses}.
+ */
+@Deprecated(forRemoval = true)
public class ResponseResultSet
{
- private static Logger xlog = LoggerFactory.getLogger(ResponseResultSet.class);
-
- // Short names for "output="
- private static final String contentOutputJSON = "json";
- private static final String contentOutputXML = "xml";
- private static final String contentOutputSPARQL = "sparql";
- private static final String contentOutputText = "text";
- private static final String contentOutputCSV = "csv";
- private static final String contentOutputTSV = "tsv";
- private static final String contentOutputThrift = "thrift";
-
- public static Map<String,String> shortNamesResultSet = new HashMap<>();
- static {
- // Some short names. keys are lowercase.
- ResponseOps.put(shortNamesResultSet, contentOutputJSON, contentTypeResultsJSON);
- ResponseOps.put(shortNamesResultSet, contentOutputSPARQL, contentTypeResultsXML);
- ResponseOps.put(shortNamesResultSet, contentOutputXML, contentTypeResultsXML);
- ResponseOps.put(shortNamesResultSet, contentOutputText, contentTypeTextPlain);
- ResponseOps.put(shortNamesResultSet, contentOutputCSV, contentTypeTextCSV);
- ResponseOps.put(shortNamesResultSet, contentOutputTSV, contentTypeTextTSV);
- ResponseOps.put(shortNamesResultSet, contentOutputThrift, contentTypeResultsThrift);
- }
-
- interface OutputContent { void output(OutputStream out) throws IOException; }
-
public static void doResponseResultSet(HttpAction action, Boolean booleanResult) {
- doResponseResultSet$(action, null, booleanResult, null, DEF.rsOfferBoolean);
+ Responses.doResponseResultSet(action, booleanResult);
}
public static void doResponseResultSet(HttpAction action, ResultSet resultSet, Prologue qPrologue) {
- doResponseResultSet$(action, resultSet, null, qPrologue, DEF.rsOfferTable);
- }
-
- // One or the other argument must be null
- private static void doResponseResultSet$(HttpAction action,
- ResultSet resultSet, Boolean booleanResult,
- Prologue qPrologue, AcceptList contentTypeOffer) {
- HttpServletRequest request = action.getRequest();
- long id = action.id;
-
- if ( resultSet == null && booleanResult == null ) {
- xlog.warn("doResponseResult: Both result set and boolean result are null");
- throw new FusekiException("Both result set and boolean result are null");
- }
-
- if ( resultSet != null && booleanResult != null ) {
- xlog.warn("doResponseResult: Both result set and boolean result are set");
- throw new FusekiException("Both result set and boolean result are set");
- }
-
- String mimeType = null;
- // -- Conneg
- MediaType i = ConNeg.chooseContentType(request, contentTypeOffer, DEF.acceptResultSetXML);
- if ( i != null )
- mimeType = i.getContentTypeStr();
-
- // -- 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
- if ( outputField != null )
- mimeType = outputField;
-
- String serializationType = mimeType; // Choose the serializer based on this.
- String contentType = mimeType; // Set the HTTP response header to this.
-
- // -- Stylesheet - change to application/xml.
- final String stylesheetURL = ResponseOps.paramStylesheet(request);
- if ( stylesheetURL != null && Objects.equals(serializationType,contentTypeResultsXML) )
- contentType = contentTypeXML;
-
- // Force to text/plain?
- String forceAccept = ResponseOps.paramForceAccept(request);
- if ( forceAccept != null )
- contentType = contentTypeTextPlain;
-
- // Some kind of general dispatch is neater but there are quite a few special cases.
- // text/plain is special because there is no ResultSetWriter for it (yet).
- // Text plain is special because of the formatting by prologue.
- // text/plain is not a registered result set language.
- //
- // 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.
-
- if ( Objects.equals(serializationType, contentTypeTextPlain) ) {
- textOutput(action, contentType, resultSet, qPrologue, booleanResult);
- return;
- }
-
- 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);
-
- Context cxt = action.getContext().copy();
- String charset = charsetUTF8;
- String jsonCallback = null;
-
- if ( Objects.equals(serializationType, contentTypeResultsXML) ) {
- charset = null;
- if ( stylesheetURL != null )
- cxt.set(RowSetWriterXML.xmlStylesheet, stylesheetURL);
- }
- if ( Objects.equals(serializationType, contentTypeResultsJSON) ) {
- jsonCallback = ResponseOps.paramCallback(action.getRequest());
- }
- if (Objects.equals(serializationType, WebContent.contentTypeResultsThrift) ) {
- if ( booleanResult != null )
- ServletOps.errorBadRequest("Can't write a boolean result in thrift");
- charset = null;
- }
- if (Objects.equals(serializationType, WebContent.contentTypeResultsProtobuf) ) {
- if ( booleanResult != null )
- ServletOps.errorBadRequest("Can't write a boolean result in protobuf");
- charset = null;
- }
-
-
- // 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) {
- // Text is not streaming.
- OutputContent proc = out -> {
- if ( resultSet != null )
- ResultSetFormatter.out(out, resultSet, qPrologue);
- if ( booleanResult != null )
- ResultSetFormatter.out(out, booleanResult.booleanValue());
- };
-
- output(action, contentType, charsetUTF8, proc);
- }
-
- /** 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 = (out) -> {
- if ( callback != null ) {
- String callbackFunction = callback;
- callbackFunction = callbackFunction.replace("\r", "");
- callbackFunction = callbackFunction.replace("\n", "");
- out.write(StrUtils.asUTF8bytes(callbackFunction));
- out.write('('); out.write('\n');
- }
- if ( resultSet != null )
- rw.write(out, resultSet);
- if ( booleanResult != null )
- rw.write(out, booleanResult.booleanValue());
- if ( callback != null ) {
- out.write(')'); out.write('\n');
- }
- };
- output(action, contentType, charset, proc);
- }
-
- // Set HTTP response and execute OutputContent inside try-catch.
- private static void output(HttpAction action, String contentType, String charset, OutputContent proc) {
- try {
- ResponseOps.setHttpResponse(action, contentType, charset);
- ServletOps.success(action);
- OutputStream out = action.getResponseOutputStream();
- try {
- proc.output(out);
- out.flush();
- } catch (QueryCancelledException ex) {
- // Status code 200 may have already been sent.
- // We can try to set the HTTP response code anyway.
- // Breaking the results is the best we can do to indicate the timeout.
- action.setResponseStatus(HttpSC.BAD_REQUEST_400);
- action.log.info(format("[%d] Query Cancelled - results truncated (but 200 may have already been sent)", action.id));
- PrintStream ps = new PrintStream(out);
- ps.println();
- ps.println("## Query cancelled due to timeout during execution ##");
- ps.println("## **** Incomplete results **** ##");
- ps.flush();
- out.flush();
- // No point raising an exception - 200 was sent already.
- //errorOccurred(ex);
- }
- // Includes client gone.
- } catch (IOException ex) { ServletOps.errorOccurred(ex); }
- // Do not call httpResponse.flushBuffer() at this point. JSON callback closing details haven't been added.
- // Jetty closes the stream if it is a gzip stream.
+ RowSet rowSet = RowSet.adapt(resultSet);
+ Responses.doResponseResultSet(action, rowSet, qPrologue);
}
}
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/Responses.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/Responses.java
new file mode 100644
index 0000000000..a0943fdb6f
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/Responses.java
@@ -0,0 +1,526 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.servlets;
+
+import static java.lang.String.format;
+import static org.apache.jena.atlas.lib.Lib.lowercase;
+import static org.apache.jena.riot.WebContent.*;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Objects;
+
+import jakarta.servlet.ServletOutputStream;
+import jakarta.servlet.http.HttpServletRequest;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.jena.atlas.json.JsonObject;
+import org.apache.jena.atlas.lib.Lib;
+import org.apache.jena.atlas.lib.StrUtils;
+import org.apache.jena.atlas.web.AcceptList;
+import org.apache.jena.atlas.web.MediaType;
+import org.apache.jena.fuseki.DEF;
+import org.apache.jena.fuseki.Fuseki;
+import org.apache.jena.fuseki.FusekiException;
+import org.apache.jena.fuseki.system.ConNeg;
+import org.apache.jena.fuseki.system.FusekiNetLib;
+import org.apache.jena.graph.Graph;
+import org.apache.jena.query.QueryCancelledException;
+import org.apache.jena.query.ResultSetFormatter;
+import org.apache.jena.riot.Lang;
+import org.apache.jena.riot.RDFFormat;
+import org.apache.jena.riot.RDFLanguages;
+import org.apache.jena.riot.WebContent;
+import org.apache.jena.riot.resultset.ResultSetWriterRegistry;
+import org.apache.jena.riot.rowset.rw.RowSetWriterXML;
+import org.apache.jena.riot.web.HttpNames;
+import org.apache.jena.shared.JenaException;
+import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.core.DatasetGraphFactory;
+import org.apache.jena.sparql.core.Prologue;
+import org.apache.jena.sparql.exec.RowSet;
+import org.apache.jena.sparql.exec.RowSetOps;
+import org.apache.jena.sparql.resultset.ResultsWriter;
+import org.apache.jena.sparql.util.Context;
+import org.apache.jena.web.HttpSC;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** This is the content negotiation for each kind of SPARQL query result */
+public class Responses
+{
+ private static Logger xlog = LoggerFactory.getLogger(Responses.class);
+
+ // Short names for result sets "output="
+ private static final String contentOutputJSON = "json";
+ private static final String contentOutputXML = "xml";
+ private static final String contentOutputSPARQL = "sparql";
+ private static final String contentOutputText = "text";
+ private static final String contentOutputCSV = "csv";
+ private static final String contentOutputTSV = "tsv";
+ private static final String contentOutputThrift = "thrift";
+
+ // Short names for graph and datasets "output="
+ private static final String contentOutputJSONLD = "json-ld";
+ private static final String contentOutputJSONRDF = "json-rdf";
+ private static final String contentOutputTTL = "ttl";
+ private static final String contentOutputTurtle = "turtle";
+ private static final String contentOutputNT = "nt";
+ private static final String contentOutputTriG = "trig";
+ private static final String contentOutputNQuads = "n-quads";
+
+
+ // Short names - result sets
+ public static final Map<String,String> shortNamesResultSet = Map.ofEntries
+ (entryLC(contentOutputJSON, contentTypeResultsJSON),
+ entryLC(contentOutputSPARQL, contentTypeResultsXML),
+ entryLC(contentOutputXML, contentTypeResultsXML),
+ entryLC(contentOutputText, contentTypeTextPlain),
+ entryLC(contentOutputCSV, contentTypeTextCSV),
+ entryLC(contentOutputTSV, contentTypeTextTSV),
+ entryLC(contentOutputThrift, contentTypeResultsThrift)
+ );
+
+ // Short names - graphs etc
+ public static final Map<String,String> shortNamesGraph = Map.ofEntries
+ (entryLC(contentOutputJSONLD, contentTypeJSONLD),
+ entryLC(contentOutputJSONRDF, contentTypeRDFJSON),
+ entryLC(contentOutputJSON, contentTypeJSONLD),
+ entryLC(contentOutputXML, contentTypeRDFXML),
+ entryLC(contentOutputText, contentTypeTurtle),
+ entryLC(contentOutputTTL, contentTypeTurtle),
+ entryLC(contentOutputTurtle, contentTypeTurtle),
+ entryLC(contentOutputNT, contentTypeNTriples),
+ entryLC(contentOutputNQuads, contentTypeNQuads),
+ entryLC(contentOutputTriG, contentTypeTriG)
+ );
+
+ private static Map.Entry<String, String> entryLC(String key, String value) {
+ return Map.entry(Lib.lowercase(key), value);
+ }
+
+ interface OutputContent { void output(OutputStream out) throws IOException; }
+
+ // Set HTTP response and execute OutputContent inside try-catch.
+ private static void output(HttpAction action, String contentType, String charset, OutputContent proc) {
+ try {
+ setHttpResponse(action, contentType, charset);
+ ServletOps.success(action);
+ OutputStream out = action.getResponseOutputStream();
+ try {
+ proc.output(out);
+ out.flush();
+ } catch (QueryCancelledException ex) {
+ // Status code 200 may have already been sent.
+ // We can try to set the HTTP response code anyway.
+ // Breaking the results is the best we can do to indicate the timeout.
+ action.setResponseStatus(HttpSC.BAD_REQUEST_400);
+ action.log.info(format("[%d] Query Cancelled - results truncated (but 200 may have already been sent)", action.id));
+ PrintStream ps = new PrintStream(out);
+ ps.println();
+ ps.println("## Query cancelled due to timeout during execution ##");
+ ps.println("## **** Incomplete results **** ##");
+ ps.flush();
+ out.flush();
+ // No point raising an exception - 200 was sent already.
+ //errorOccurred(ex);
+ }
+ // Includes client gone.
+ } catch (IOException ex) { ServletOps.errorOccurred(ex); }
+ // Do not call httpResponse.flushBuffer() at this point. JSON callback closing details haven't been added.
+ // Jetty closes the stream if it is a gzip stream.
+ }
+
+ public static void doResponseResultSet(HttpAction action, Boolean booleanResult) {
+ ResponseResults.doResponseResultSet$(action, null, booleanResult, null, DEF.rsOfferBoolean);
+ }
+
+ public static void doResponseResultSet(HttpAction action, RowSet rowSet, Prologue qPrologue) {
+ ResponseResults.doResponseResultSet$(action, rowSet, null, qPrologue, DEF.rsOfferTable);
+ }
+
+ static class ResponseResults {
+ // One or the other argument must be null
+ private static void doResponseResultSet$(HttpAction action,
+ RowSet rowSet, Boolean booleanResult,
+ Prologue qPrologue, AcceptList contentTypeOffer) {
+ HttpServletRequest request = action.getRequest();
+ long id = action.id;
+
+ if ( rowSet == null && booleanResult == null ) {
+ xlog.warn("doResponseResult: Both result set and boolean result are null");
+ throw new FusekiException("Both result set and boolean result are null");
+ }
+
+ if ( rowSet != null && booleanResult != null ) {
+ xlog.warn("doResponseResult: Both result set and boolean result are set");
+ throw new FusekiException("Both result set and boolean result are set");
+ }
+
+ String mimeType = null;
+ // -- Conneg
+ MediaType i = ConNeg.chooseContentType(request, contentTypeOffer, DEF.acceptResultSetXML);
+ if ( i != null )
+ mimeType = i.getContentTypeStr();
+
+ // -- Override content type from conneg.
+ // Does &output= override?
+ // Requested output type by the web form or &output= in the request.
+ String outputField = paramOutput(request, shortNamesResultSet); // Expands short names
+ if ( outputField != null )
+ mimeType = outputField;
+
+ String serializationType = mimeType; // Choose the serializer based on this.
+ String contentType = mimeType; // Set the HTTP response header to this.
+
+ // -- Stylesheet - change to application/xml.
+ final String stylesheetURL = paramStylesheet(request);
+ if ( stylesheetURL != null && Objects.equals(serializationType,contentTypeResultsXML) )
+ contentType = contentTypeXML;
+
+ // Force to text/plain?
+ String forceAccept = paramForceAccept(request);
+ if ( forceAccept != null )
+ contentType = contentTypeTextPlain;
+
+ // Some kind of general dispatch is neater but there are quite a few special cases.
+ // text/plain is special because there is no ResultSetWriter for it (yet).
+ // Text plain is special because of the formatting by prologue.
+ // text/plain is not a registered result set language.
+ //
+ // 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.
+
+ if ( Objects.equals(serializationType, contentTypeTextPlain) ) {
+ textOutput(action, contentType, rowSet, qPrologue, booleanResult);
+ return;
+ }
+
+ 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);
+
+ Context cxt = action.getContext().copy();
+ String charset = charsetUTF8;
+ String jsonCallback = null;
+
+ if ( Objects.equals(serializationType, contentTypeResultsXML) ) {
+ charset = null;
+ if ( stylesheetURL != null )
+ cxt.set(RowSetWriterXML.xmlStylesheet, stylesheetURL);
+ }
+ if ( Objects.equals(serializationType, contentTypeResultsJSON) ) {
+ jsonCallback = paramCallback(action.getRequest());
+ }
+ if (Objects.equals(serializationType, WebContent.contentTypeResultsThrift) ) {
+ if ( booleanResult != null )
+ ServletOps.errorBadRequest("Can't write a boolean result in thrift");
+ charset = null;
+ }
+ if (Objects.equals(serializationType, WebContent.contentTypeResultsProtobuf) ) {
+ if ( booleanResult != null )
+ ServletOps.errorBadRequest("Can't write a boolean result in protobuf");
+ charset = null;
+ }
+
+ // Finally, the general case
+ generalOutput(action, lang, contentType, charset, cxt, jsonCallback, rowSet, booleanResult);
+ }
+
+ private static void textOutput(HttpAction action, String contentType, RowSet resultSet, Prologue qPrologue, Boolean booleanResult) {
+ // Text is not streaming.
+ OutputContent proc = out -> {
+ if ( resultSet != null )
+ RowSetOps.out(out, resultSet, qPrologue);
+ if ( booleanResult != null )
+ ResultSetFormatter.out(out, booleanResult.booleanValue());
+ };
+
+ output(action, contentType, charsetUTF8, proc);
+ }
+
+ /** Any format */
+ private static void generalOutput(HttpAction action, Lang rsLang,
+ String contentType, String charset,
+ Context context, String callback,
+ RowSet resultSet, Boolean booleanResult) {
+ ResultsWriter rw = ResultsWriter.create()
+ .lang(rsLang)
+ .context(context)
+ .build();
+ OutputContent proc = (out) -> {
+ if ( callback != null ) {
+ String callbackFunction = callback;
+ callbackFunction = callbackFunction.replace("\r", "");
+ callbackFunction = callbackFunction.replace("\n", "");
+ out.write(StrUtils.asUTF8bytes(callbackFunction));
+ out.write('('); out.write('\n');
+ }
+ if ( resultSet != null )
+ rw.write(out, resultSet);
+ if ( booleanResult != null )
+ rw.write(out, booleanResult.booleanValue());
+ if ( callback != null ) {
+ out.write(')'); out.write('\n');
+ }
+ };
+ output(action, contentType, charset, proc);
+ }
+ }
+
+ public static void doResponseGraph(HttpAction action, Graph graph) {
+ DatasetGraph ds = DatasetGraphFactory.wrap(graph);
+ doResponseDataset(action, ds);
+ }
+
+ public static void doResponseDataset(HttpAction action, DatasetGraph dataset) {
+ ResponseGraph.doResponseDataset$(action, dataset);
+ }
+
+
+ static class ResponseGraph {
+ static void doResponseDataset$(HttpAction action, DatasetGraph dataset) {
+ HttpServletRequest request = action.getRequest();
+
+ String mimeType = null; // Header request type
+
+ MediaType i = ConNeg.chooseContentType(request, DEF.constructOffer, DEF.acceptTurtle);
+ if ( i != null )
+ mimeType = i.getContentTypeStr();
+
+ String outputField = paramOutput(request, shortNamesGraph);
+ if ( outputField != null )
+ mimeType = outputField;
+
+ String writerMimeType = mimeType;
+
+ if ( mimeType == null ) {
+ Fuseki.actionLog.warn("Can't find MIME type for response");
+ String x = FusekiNetLib.getAccept(request);
+ String msg;
+ if ( x == null )
+ msg = "No Accept: header";
+ else
+ msg = "Accept: " + x + " : Not understood";
+ ServletOps.error(HttpSC.NOT_ACCEPTABLE_406, msg);
+ }
+
+ String contentType = mimeType;
+ String charset = charsetUTF8;
+
+ String forceAccept = paramForceAccept(request);
+ if ( forceAccept != null ) {
+ contentType = forceAccept;
+ charset = charsetUTF8;
+ }
+
+ Lang lang = RDFLanguages.contentTypeToLang(contentType);
+ if ( lang == null )
+ ServletOps.errorBadRequest("Can't determine output content type: "+contentType);
+ RDFFormat format = ActionLib.getNetworkFormatForLang(lang);
+
+ try {
+ ServletOps.success(action);
+ ServletOutputStream out = action.getResponseOutputStream();
+ try {
+ // Use the Content-Type from the content negotiation.
+ if ( RDFLanguages.isQuads(lang) )
+ ActionLib.datasetResponse(action, dataset, format, contentType);
+ else
+ ActionLib.graphResponse(action, dataset.getDefaultGraph(), format, contentType);
+ out.flush();
+ } catch (JenaException ex) {
+ ServletOps.errorOccurred("Failed to write output: "+ex.getMessage(), ex);
+ }
+ }
+ catch (ActionErrorException ex) { throw ex; }
+ catch (Exception ex) {
+ action.log.info("Exception while writing the response model: "+ex.getMessage(), ex);
+ ServletOps.errorOccurred("Exception while writing the response model: "+ex.getMessage(), ex);
+ }
+ }
+ }
+
+
+ public static void doResponseJson(HttpAction action, Iterator<JsonObject> jsonItem) {
+ ResponseJson.doResponseJson$(action, jsonItem);
+ }
+
+ static class ResponseJson {
+ /**
+ * Outputs a JSON query result
+ *
+ * @param action HTTP action
+ * @param jsonItem a ResultSetJsonStream instance
+ */
+ static void doResponseJson$(HttpAction action, Iterator<JsonObject> jsonItem) {
+ if ( jsonItem == null ) {
+ xlog.warn("doResponseJson: Result set is null");
+ throw new FusekiException("Result set is null");
+ }
+
+ jsonOutput(action, jsonItem);
+ }
+
+ private static void jsonOutput(HttpAction action, final Iterator<JsonObject> jsonItems) {
+ OutputContent proc = out-> {
+ if ( jsonItems != null )
+ ResultSetFormatter.output(out, jsonItems);
+ };
+
+ try {
+ String callback = paramCallback(action.getRequest());
+ ServletOutputStream out = action.getResponseOutputStream();
+
+ if ( callback != null ) {
+ callback = StringUtils.replaceChars(callback, "\r", "");
+ callback = StringUtils.replaceChars(callback, "\n", "");
+ out.write(StrUtils.asUTF8bytes(callback));
+ out.write('('); out.write('\n');
+ }
+
+ output(action, "application/json", WebContent.charsetUTF8, proc);
+
+ if ( callback != null ) {
+ out.write(')'); out.write('\n');
+ }
+ } catch (IOException ex) {
+ ServletOps.errorOccurred(ex);
+ }
+ }
+
+ private static void output(HttpAction action, String contentType, String charset, OutputContent proc) {
+ try {
+ setHttpResponse(action, contentType, charset);
+ action.setResponseStatus(HttpSC.OK_200);
+ ServletOutputStream out = action.getResponseOutputStream();
+ try {
+ proc.output(out);
+ out.flush();
+ } catch (QueryCancelledException ex) {
+ // Bother. Status code 200 already sent.
+ xlog.info(format("[%d] Query Cancelled - results truncated (but 200 already sent)", action.id));
+ PrintStream ps = new PrintStream(out);
+ ps.println();
+ ps.println("## Query cancelled due to timeout during execution ##");
+ ps.println("## **** Incomplete results **** ##");
+ ps.flush();
+ out.flush();
+ // No point raising an exception - 200 was sent already.
+ // errorOccurred(ex);
+ }
+ // Includes client gone.
+ } catch (IOException ex) {
+ ServletOps.errorOccurred(ex);
+ }
+ // Do not call httpResponse.flushBuffer(); here - Jetty closes the stream if
+ // it is a gzip stream
+ // then the JSON callback closing details can't be added.
+ }
+
+ public static void setHttpResponse(HttpAction action, String contentType, String charset) {
+ // ---- Set up HTTP Response
+ // Stop caching (not that ?queryString URLs are cached anyway)
+ ServletOps.setNoCache(action);
+ // See: http://www.w3.org/International/O-HTTP-charset.html
+ if ( contentType != null ) {
+ if ( charset != null )
+ contentType = contentType + "; charset=" + charset;
+ xlog.trace("Content-Type for response: " + contentType);
+ action.setResponseContentType(contentType);
+ }
+ }
+ }
+
+ static String paramForceAccept(HttpServletRequest request) {
+ String x = fetchParam(request, HttpNames.paramForceAccept);
+ return x;
+ }
+
+ static String paramStylesheet(HttpServletRequest request) {
+ return fetchParam(request, HttpNames.paramStyleSheet);
+ }
+
+ static String paramOutput(HttpServletRequest request, Map<String, String> map) {
+ // Two names.
+ String x = fetchParam(request, HttpNames.paramOutput1);
+ if ( x == null )
+ x = fetchParam(request, HttpNames.paramOutput2);
+ if ( x == null )
+ x = fetchParam(request, HttpNames.paramOutput3);
+ return expandShortName(x, map);
+ }
+
+ private static String expandShortName(String str, Map<String, String> map) {
+ if ( str == null )
+ return null;
+ // Force keys to lower case. See put() above.
+ String key = lowercase(str);
+ String str2 = map.get(key);
+ if ( str2 == null )
+ return str;
+ return str2;
+ }
+
+ static String paramCallback(HttpServletRequest request) {
+ return fetchParam(request, HttpNames.paramCallback);
+ }
+
+ private static String fetchParam(HttpServletRequest request, String parameterName) {
+ String value = request.getParameter(parameterName);
+ if ( value != null ) {
+ value = value.trim();
+ if ( value.length() == 0 )
+ value = null;
+ }
+ return value;
+ }
+
+ /** Basic settings, including Content-Type, for a response. */
+ 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.setResponseContentType(contentType);
+ }
+ }
+
+ private static boolean isXML(String contentType) {
+ return contentType.equals(WebContent.contentTypeRDFXML) || contentType.equals(WebContent.contentTypeResultsXML)
+ || contentType.equals(WebContent.contentTypeXML);
+ }
+
+}
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQLQueryProcessor.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQLQueryProcessor.java
index f0b4199ce9..385594535e 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQLQueryProcessor.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQLQueryProcessor.java
@@ -33,7 +33,6 @@ import java.util.*;
import java.util.concurrent.TimeUnit;
import jakarta.servlet.http.HttpServletRequest;
-
import org.apache.jena.atlas.io.IO;
import org.apache.jena.atlas.io.IndentedLineBuffer;
import org.apache.jena.atlas.json.JsonObject;
@@ -41,16 +40,16 @@ import org.apache.jena.atlas.lib.Pair;
import org.apache.jena.atlas.web.ContentType;
import org.apache.jena.fuseki.Fuseki;
import org.apache.jena.fuseki.system.FusekiNetLib;
+import org.apache.jena.graph.Graph;
import org.apache.jena.query.*;
-import org.apache.jena.rdf.model.Model;
import org.apache.jena.riot.web.HttpNames;
import org.apache.jena.sparql.core.DatasetGraph;
import org.apache.jena.sparql.core.Prologue;
import org.apache.jena.sparql.engine.Timeouts;
import org.apache.jena.sparql.exec.QueryExec;
import org.apache.jena.sparql.exec.QueryExecDatasetBuilder;
-import org.apache.jena.sparql.exec.QueryExecutionAdapter;
-import org.apache.jena.sparql.resultset.SPARQLResult;
+import org.apache.jena.sparql.exec.QueryExecResult;
+import org.apache.jena.sparql.exec.RowSet;
import org.apache.jena.web.HttpSC;
/**
@@ -276,8 +275,8 @@ public abstract class SPARQLQueryProcessor extends ActionService
if ( q == null )
q = query;
- try ( QueryExecution qExec = createQueryExecution(action, q, dataset); ) {
- SPARQLResult result = executeQuery(action, qExec, query, queryStringLog);
+ try ( QueryExec qExec = createQueryExec(action, q, dataset); ) {
+ QueryExecResult result = executeQuery(action, qExec, query, queryStringLog);
// Deals with exceptions itself.
sendResults(action, result, query.getPrologue());
}
@@ -313,7 +312,7 @@ public abstract class SPARQLQueryProcessor extends ActionService
* @param dataset
* @return QueryExecution
*/
- protected QueryExecution createQueryExecution(HttpAction action, Query query, DatasetGraph dataset) {
+ protected QueryExec createQueryExec(HttpAction action, Query query, DatasetGraph dataset) {
QueryExecDatasetBuilder builder = QueryExec.newBuilder()
.dataset(dataset)
.query(query)
@@ -321,7 +320,7 @@ public abstract class SPARQLQueryProcessor extends ActionService
;
setTimeouts(builder, action);
QueryExec qExec = builder.build();
- return QueryExecutionAdapter.adapt(qExec);
+ return qExec;
}
/**
@@ -376,9 +375,9 @@ public abstract class SPARQLQueryProcessor extends ActionService
* @param queryStringLog Informational string created from the initial query.
* @return
*/
- protected SPARQLResult executeQuery(HttpAction action, QueryExecution queryExecution, Query requestQuery, String queryStringLog) {
+ protected QueryExecResult executeQuery(HttpAction action, QueryExec queryExecution, Query requestQuery, String queryStringLog) {
if ( requestQuery.isSelectType() ) {
- ResultSet rs = queryExecution.execSelect();
+ RowSet rs = queryExecution.select();
// Force some query execution now.
// If the timeout-first-row goes off, the output stream has not
@@ -390,33 +389,28 @@ public abstract class SPARQLQueryProcessor extends ActionService
// the result now to see if the timeout-end-of-query goes off.
// rs = ResultSetFactory.copyResults(rs);
- //action.log.info(format("[%d] exec/select", action.id));
- return new SPARQLResult(rs);
+ return new QueryExecResult(rs);
}
if ( requestQuery.isConstructType() ) {
- Dataset dataset = queryExecution.execConstructDataset();
- //action.log.info(format("[%d] exec/construct", action.id));
- return new SPARQLResult(dataset);
+ DatasetGraph dataset = queryExecution.constructDataset();
+ return new QueryExecResult(dataset);
}
if ( requestQuery.isDescribeType() ) {
- Model model = queryExecution.execDescribe();
- //action.log.info(format("[%d] exec/describe", action.id));
- return new SPARQLResult(model);
+ Graph graph = queryExecution.describe();
+ return new QueryExecResult(graph);
}
if ( requestQuery.isAskType() ) {
- boolean b = queryExecution.execAsk();
- //action.log.info(format("[%d] exec/ask", action.id));
- return new SPARQLResult(b);
+ boolean b = queryExecution.ask();
+ return new QueryExecResult(b);
}
if ( requestQuery.isJsonType() ) {
Iterator<JsonObject> jsonIterator = queryExecution.execJsonItems();
//JsonArray jsonArray = queryExecution.execJson();
- action.log.info(format("[%d] exec/json", action.id));
- return new SPARQLResult(jsonIterator);
+ return new QueryExecResult(jsonIterator);
}
ServletOps.errorBadRequest("Unknown query type - " + queryStringLog);
@@ -436,19 +430,19 @@ public abstract class SPARQLQueryProcessor extends ActionService
* @param result
* @param qPrologue
*/
- protected void sendResults(HttpAction action, SPARQLResult result, Prologue qPrologue) {
- if ( result.isResultSet() )
- ResponseResultSet.doResponseResultSet(action, result.getResultSet(), qPrologue);
+ protected void sendResults(HttpAction action, QueryExecResult result, Prologue qPrologue) {
+ if ( result.isRowSet() )
+ Responses.doResponseResultSet(action, result.rowSet(), qPrologue);
else if ( result.isDataset() )
// CONSTRUCT is processed as a extended CONSTRUCT - result is a dataset.
- ResponseDataset.doResponseDataset(action, result.getDataset());
- else if ( result.isModel() )
- // DESCRIBE results are models
- ResponseDataset.doResponseModel(action, result.getModel());
+ Responses.doResponseDataset(action, result.dataset());
+ else if ( result.isGraph() )
+ // DESCRIBE results are graphs
+ Responses.doResponseGraph(action, result.graph());
else if ( result.isBoolean() )
- ResponseResultSet.doResponseResultSet(action, result.getBooleanResult());
+ Responses.doResponseResultSet(action, result.booleanResult());
else if ( result.isJson() )
- ResponseJson.doResponseJson(action, result.getJsonItems());
+ Responses.doResponseJson(action, result.jsonItems());
else
ServletOps.errorOccurred("Unknown or invalid result type");
}
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/system/GraphLoadUtils.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/system/GraphLoadUtils.java
index a4a5c95013..495cb6e88e 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/system/GraphLoadUtils.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/system/GraphLoadUtils.java
@@ -20,18 +20,18 @@
package org.apache.jena.fuseki.system;
import org.apache.jena.fuseki.Fuseki;
-import org.apache.jena.graph.GraphMemFactory;
import org.apache.jena.graph.Graph;
+import org.apache.jena.graph.GraphMemFactory;
import org.apache.jena.riot.RDFParser;
import org.apache.jena.riot.system.StreamRDF;
import org.apache.jena.riot.system.StreamRDFLib;
+import org.apache.jena.sparql.core.DatasetGraph;
+import org.apache.jena.sparql.core.DatasetGraphFactory;
-/** A packaging of code to do a controlled read of a graph or model */
+/** A packaging of code to do a controlled read of a graph or */
public class GraphLoadUtils
{
- // ---- Graph level
-
public static Graph readGraph(String uri, int limit) {
Graph g = GraphMemFactory.createDefaultGraphSameTerm();
readUtil(g, uri, limit);
@@ -42,10 +42,27 @@ public class GraphLoadUtils
readUtil(g, uri, limit);
}
- // ** Worker.
+ public static DatasetGraph readDataset(String uri, int limit) {
+ DatasetGraph dsg = DatasetGraphFactory.createTxnMem();
+ readUtil(dsg, uri, limit);
+ return dsg;
+ }
+
+ public static void loadDataset(DatasetGraph dsg, String uri, int limit) {
+ readUtil(dsg, uri, limit);
+ }
+
+ // Worker/Graph
private static void readUtil(Graph graph, String uri, int limit) {
StreamRDF sink = StreamRDFLib.graph(graph);
sink = new StreamRDFLimited(sink, limit);
RDFParser.source(uri).streamManager(Fuseki.webStreamManager).parse(sink);
}
+
+ // Worker/DatasetGraph
+ private static void readUtil(DatasetGraph dsg, String uri, int limit) {
+ StreamRDF sink = StreamRDFLib.dataset(dsg);
+ sink = new StreamRDFLimited(sink, limit);
+ RDFParser.source(uri).streamManager(Fuseki.webStreamManager).parse(sink);
+ }
}