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 <[email protected]>
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);
+    }
 }


Reply via email to