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 f39105563841733b7a97aaada5dd2f02adc21c40 Author: Andy Seaborne <[email protected]> AuthorDate: Mon Nov 11 15:23:41 2024 +0000 JENA-2837: Reduce use of model APIs in FusekiConfig --- .../jena/sparql/core/assembler/AssemblerUtils.java | 11 +- .../apache/jena/sparql/util/graph/GraphUtils.java | 2 +- .../src/main/java/org/apache/jena/system/G.java | 15 +- .../org/apache/jena/fuseki/build/BuildLib.java | 201 ++++++------ .../jena/fuseki/build/DatasetDescriptionMap.java | 14 +- .../org/apache/jena/fuseki/build/FusekiConfig.java | 352 ++++++++++++--------- .../build/{BuildLib.java => model/BuildLibM.java} | 14 +- .../DatasetDescriptionMapM.java} | 6 +- .../FusekiConfigM.java} | 332 +++++++++---------- .../org/apache/jena/fuseki/server/FusekiVocab.java | 3 +- .../apache/jena/fuseki/server/FusekiVocabG.java | 111 +++++++ .../org/apache/jena/fuseki/server/Operation.java | 33 +- .../org/apache/jena/fuseki/main/FusekiServer.java | 135 ++++---- .../jena/fuseki/main/access/TestAuthorized.java | 34 +- .../org/apache/jena/fuseki/mgt/ActionDatasets.java | 3 +- .../apache/jena/fuseki/webapp/FusekiWebapp.java | 4 +- .../java/org/apache/jena/fuseki/TestBuilder.java | 17 +- 17 files changed, 731 insertions(+), 556 deletions(-) diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/core/assembler/AssemblerUtils.java b/jena-arq/src/main/java/org/apache/jena/sparql/core/assembler/AssemblerUtils.java index 7c4b2224c2..e67baddb97 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/core/assembler/AssemblerUtils.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/core/assembler/AssemblerUtils.java @@ -108,13 +108,14 @@ public class AssemblerUtils } public static Model readAssemblerFile(String assemblerFile) { - Model spec = null ; + Model spec = null; try { - spec = RDFDataMgr.loadModel(assemblerFile) ; - } catch (Exception ex) - { throw new ARQException("Failed reading assembler description: "+ex.getMessage()) ; } + spec = RDFDataMgr.loadModel(assemblerFile); + } catch (Exception ex) { + throw new ARQException("Failed reading assembler description: " + ex.getMessage()); + } addRegistered(spec); - return spec ; + return spec; } /** Add any extra information to the model. diff --git a/jena-arq/src/main/java/org/apache/jena/sparql/util/graph/GraphUtils.java b/jena-arq/src/main/java/org/apache/jena/sparql/util/graph/GraphUtils.java index 79daa39a15..cad5c3e69a 100644 --- a/jena-arq/src/main/java/org/apache/jena/sparql/util/graph/GraphUtils.java +++ b/jena-arq/src/main/java/org/apache/jena/sparql/util/graph/GraphUtils.java @@ -44,7 +44,7 @@ import org.apache.jena.util.iterator.ExtendedIterator; import org.apache.jena.util.iterator.NiceIterator; import org.apache.jena.vocabulary.RDF; -/** Graph utilities. See also GraphFactory. */ +/** Graph utilities. */ public class GraphUtils { diff --git a/jena-arq/src/main/java/org/apache/jena/system/G.java b/jena-arq/src/main/java/org/apache/jena/system/G.java index c18b46b7d5..547c19fc1b 100644 --- a/jena-arq/src/main/java/org/apache/jena/system/G.java +++ b/jena-arq/src/main/java/org/apache/jena/system/G.java @@ -41,10 +41,10 @@ import java.util.function.Supplier; import static org.apache.jena.graph.Node.ANY; /** - * A library of functions for working with {@link Graph}. Internally all + * A library of functions for working with {@link Graph graphs}. Internally, all * {@link ExtendedIterator ExtendedIterators} used, run to completion or have * {@code .close()} called. Any {@link ExtendedIterator ExtendedIterators} returned - * by functions in this library must be used in the same way + * by functions in this library must be used in the same way. */ public class G { private G() {} @@ -93,6 +93,12 @@ public class G { return hasDatatype(n, XSDDatatype.XSDboolean); } + public static boolean asBoolean(Node n) { + if ( ! isBoolean(n) ) + throw new RDFDataException("Not a boolean: "+NodeFmtLib.strTTL(n)); + return Boolean.TRUE.equals(n.getLiteralValue()); + } + /** * Convert null to Node.ANY, otherwise return the original node so that * {@code ==} may be used to test whether any change has occurred. @@ -267,7 +273,6 @@ public class G { */ public static boolean hasOnePO(Graph graph, Node predicate, Node object) { Objects.requireNonNull(graph, "graph"); - // Not contains! return findUniqueTriple(graph, Node.ANY, predicate, object) != null; } @@ -325,8 +330,6 @@ public class G { return findZeroOneQuad(dsg, graph, subject, predicate, object); } - // ---- Multiple matches. - /** * Get triple if there is exactly one to match the s/p/o; else return null * if none or more than one. @@ -336,6 +339,8 @@ public class G { return findQuadOrNull(dsg, graph, subject, predicate, object); } + // ---- Multiple matches. + /** * {@link ExtendedIterator} of objects where the triple matches for subject and * predicate (which can be wildcards). The {@link ExtendedIterator} must be fully diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/BuildLib.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/BuildLib.java index 302496b0b6..e54000bb62 100644 --- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/BuildLib.java +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/BuildLib.java @@ -19,7 +19,6 @@ package org.apache.jena.fuseki.build; import static org.apache.jena.fuseki.build.FusekiPrefixes.PREFIXES; -import static org.apache.jena.riot.out.NodeFmtLib.displayStr; import java.lang.reflect.Constructor; import java.net.URL; @@ -34,11 +33,24 @@ import org.apache.jena.fuseki.Fuseki; import org.apache.jena.fuseki.FusekiConfigException; import org.apache.jena.fuseki.server.Operation; import org.apache.jena.fuseki.servlets.ActionService; +import org.apache.jena.graph.Graph; +import org.apache.jena.graph.Node; import org.apache.jena.graph.NodeFactory; -import org.apache.jena.query.*; -import org.apache.jena.rdf.model.*; +import org.apache.jena.query.Query; +import org.apache.jena.query.QueryFactory; +import org.apache.jena.riot.out.NodeFmtLib; +import org.apache.jena.riot.system.PrefixMap; +import org.apache.jena.riot.system.Prefixes; import org.apache.jena.shared.JenaException; -import org.apache.jena.shared.PrefixMapping; +import org.apache.jena.sparql.core.Var; +import org.apache.jena.sparql.engine.binding.Binding; +import org.apache.jena.sparql.engine.binding.BindingFactory; +import org.apache.jena.sparql.exec.QueryExec; +import org.apache.jena.sparql.exec.QueryExecBuilder; +import org.apache.jena.sparql.exec.RowSet; +import org.apache.jena.sparql.util.graph.GNode; +import org.apache.jena.sparql.util.graph.GraphList; +import org.apache.jena.system.G; import org.apache.jena.vocabulary.RDFS; /** @@ -48,72 +60,39 @@ import org.apache.jena.vocabulary.RDFS; private BuildLib() {} - // ---- Helper code - /*package*/ static ResultSet query(String string, Model m) { - return query(string, m, null, null); + /*package*/ static RowSet query(String string, Graph graph) { + return query(string, graph, null, null); } - /*package*/ static RDFNode queryOne(String string, Model m, String varname) { - ResultSet rs = query(string, m); - return getExactlyOne(rs, varname); - } - - private static RDFNode getExactlyOne(ResultSet rs, String varname) { - if ( ! rs.hasNext() ) - return null; - QuerySolution qs = rs.next(); - if ( rs.hasNext() ) - return null; - return qs.get(varname); - } - - /*package*/ static ResultSet query(String string, Dataset ds) { - return query(string, ds, null, null); - } - - /*package*/ static ResultSet query(String string, Model m, String varName, RDFNode value) { - Query query = QueryFactory.create(PREFIXES + string); - QuerySolutionMap initValues = null; - if ( varName != null && value != null ) - initValues = querySolution(varName, value); - try ( QueryExecution qExec = QueryExecution.create().query(query).model(m).initialBinding(initValues).build() ) { - return ResultSetFactory.copyResults(qExec.execSelect()); - } - } - - /*package*/ static ResultSet query(String string, Dataset ds, String varName, RDFNode value) { + /*package*/ static RowSet query(String string, Graph graph, String varName, Node value) { Query query = QueryFactory.create(PREFIXES + string); - QuerySolutionMap initValues = null; + QueryExecBuilder qExec = QueryExec.graph(graph).query(query); if ( varName != null && value != null ) - initValues = querySolution(varName, value); - try ( QueryExecution qExec = QueryExecution - .dataset(ds) - .query(query) - .substitution(initValues) - .build() ) { - return ResultSetFactory.copyResults(qExec.execSelect()); - } + qExec.substitution(varName, value); + return qExec.build().select().materialize(); } - private static QuerySolutionMap querySolution(String varName, RDFNode value) { - QuerySolutionMap qsm = new QuerySolutionMap(); - querySolution(qsm, varName, value); - return qsm; + private static Binding querySubstitution(String varName, Node value) { + Var var = Var.alloc(varName); + return BindingFactory.binding(var, value); } - /*package*/ static QuerySolutionMap querySolution(QuerySolutionMap qsm, String varName, RDFNode value) { - qsm.add(varName, value); - return qsm; + /*package*/ static Node getOne(Graph graph, Node subject, Node property) { + List<Node> x = G.listSP(graph, subject, property); + if ( x.isEmpty() ) + throw new FusekiConfigException("No property '" + BuildLib.displayStr(graph, property) + "' for service " + BuildLib.displayStr(graph, subject)); + if ( x.size() > 1 ) + throw new FusekiConfigException("Multiple properties '" + BuildLib.displayStr(graph, property) + "' for service " + BuildLib.displayStr(graph, subject)); + return x.get(0); } - /*package*/ static RDFNode getOne(Resource svc, Property property) { - ResultSet rs = BuildLib.query("SELECT * { ?svc <" + property.getURI() + "> ?x}", svc.getModel(), "svc", svc); - if ( !rs.hasNext() ) - throw new FusekiConfigException("No property '" + property + "' for service " + BuildLib.nodeLabel(svc)); - RDFNode x = rs.next().get("x"); - if ( rs.hasNext() ) - throw new FusekiConfigException("Multiple properties '" + property + "' for service " + BuildLib.nodeLabel(svc)); - return x; + /*package*/ static Node getZeroOrOne(Graph graph, Node subject, Node property) { + List<Node> x = G.listSP(graph, subject, property); + if ( x.isEmpty() ) + return null; + if ( x.size() > 1 ) + throw new FusekiConfigException("Multiple triples for "+displayStr(graph, subject)+" "+displayStr(graph, property)); + return x.get(0); } /** @@ -121,84 +100,80 @@ import org.apache.jena.vocabulary.RDFS; * mixture. If the subject/property isn't present, return null, so a caller can tell * the difference between "not present" and an empty list value. */ - /*package*/ static Collection<RDFNode> getAll(Resource resource, String property) { - ResultSet rs = BuildLib.query("SELECT * { ?subject " + property + " ?x}", resource.getModel(), "subject", resource); - if ( ! rs.hasNext() ) + /*package*/ static Collection<Node> getMultiple(Graph graph, Node resource, Node property) { + List<Node> nodes = G.listSP(graph, resource, property); + if ( nodes.isEmpty() ) return null; - List<RDFNode> results = new ArrayList<>(); - rs.forEachRemaining(qs->{ - RDFNode n = qs.get("x"); - try { - RDFList list = n.as(RDFList.class); - results.addAll(list.asJavaList()); - } catch (JenaException x) { - // Not a list. - results.add(n); - } + // For mall lists (one level) - expand by members. + List<Node> results = new ArrayList<>(); + + nodes.forEach(node->{ + List<Node> members = listMembers(graph, node); + if ( members != null ) + results.addAll(members); + else + results.add(node); }); return results; } + private static List<Node> listMembers(Graph graph, Node node) { + GNode gnode = new GNode(graph, node); + if ( ! GraphList.isListNode(gnode) ) + return null; + List<Node> list = GraphList.members(gnode); + return list; + } + // Node presentation - /*package*/ static String nodeLabel(RDFNode n) { + + /*package*/ static String displayStr(Graph graph, Node n) { if ( n == null ) - return "<null>"; - if ( n instanceof Resource r) - return strForResource(r); + return "NULL"; + if ( graph == null ) + return NodeFmtLib.str(n, null); - Literal lit = (Literal)n; - return lit.getLexicalForm(); + PrefixMap prefixMap = Prefixes.adapt(graph); + return NodeFmtLib.str(n, null, prefixMap); } - /*package*/ static String strForResource(Resource r) { - return strForResource(r, r.getModel()); - } + /*package*/ static String strForResource(Graph graph, Node node) { + if ( node == null ) + return "NULL"; + if ( G.hasProperty(graph, node, RDFS.Nodes.label) ) { - /*package*/ static String strForResource(Resource r, PrefixMapping pm) { - if ( r == null ) - return "NULL "; - if ( r.hasProperty(RDFS.label) ) { - RDFNode n = r.getProperty(RDFS.label).getObject(); - if ( n instanceof Literal literal ) - return literal.getString(); + Node label = G.getOneSP(graph, node, RDFS.Nodes.label); + if ( label.isLiteral() ) + return label.getLiteralLexicalForm(); } - if ( r.isAnon() ) + if ( node.isBlank() ) return "<<blank node>>"; - if ( pm == null ) - pm = r.getModel(); - - return strForURI(r.getURI(), pm); + if ( node.isURI() ) + return strForURI(graph, node.getURI()); + throw notSupported(node); } - /*package*/ static String strForURI(String uri, PrefixMapping pm) { - if ( pm != null ) { - String x = pm.shortForm(uri); + private static RuntimeException notSupported(Node node) { + return new JenaException("Not supported: "+node); + } - if ( !x.equals(uri) ) + /*package*/ static String strForURI(Graph graph, String uri) { + if ( graph != null ) { + PrefixMap prefixMap = Prefixes.adapt(graph); + String x = graph.getPrefixMapping().qnameFor(uri); + if ( x != null ) return x; } return "<" + uri + ">"; } - /*package*/ static RDFNode getZeroOrOne(Resource ep, Property property) { - StmtIterator iter = ep.listProperties(property); - try { - if ( ! iter.hasNext() ) - return null; - RDFNode x = iter.next().getObject(); - if ( iter.hasNext() ) - throw new FusekiConfigException("Multiple triples for "+displayStr(ep)+" "+displayStr(property)); - return x; - } finally { iter.close(); } - } - /** Load a class (an {@link ActionService}) and create an {@link Operation} for it. */ - /*package*/ static Pair<Operation, ActionService> loadOperationActionService(RDFNode implementation) { + /*package*/ static Pair<Operation, ActionService> loadOperationActionService(Graph graph, Node implementation) { String classURI = implementation.isLiteral() - ? implementation.asLiteral().getLexicalForm() - : ((Resource)implementation).getURI(); + ? implementation.getLiteralLexicalForm() + : implementation.getURI(); String javaScheme = "java:"; String fileScheme = "file:"; String scheme = null; diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/DatasetDescriptionMap.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/DatasetDescriptionMap.java index 647aa94959..c4bf383066 100644 --- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/DatasetDescriptionMap.java +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/DatasetDescriptionMap.java @@ -22,8 +22,8 @@ import java.util.HashMap; import java.util.Map; import org.apache.jena.atlas.logging.Log; -import org.apache.jena.query.Dataset; -import org.apache.jena.rdf.model.Resource; +import org.apache.jena.graph.Node; +import org.apache.jena.sparql.core.DatasetGraph; /** * Record of datasets created from descriptions. @@ -33,20 +33,20 @@ import org.apache.jena.rdf.model.Resource; * corresponds to one dataset object when multiple services refer to the * same dataset. */ -public class DatasetDescriptionMap { +public class DatasetDescriptionMap { - private Map<Resource, Dataset> map = new HashMap<>(); + private Map<Node, DatasetGraph> map = new HashMap<>(); public DatasetDescriptionMap() {} - public void register(Resource node, Dataset ds) { - Dataset dsCurrent = map.get(node); + public void register(Node node, DatasetGraph ds) { + DatasetGraph dsCurrent = map.get(node); if ( dsCurrent != null && ! dsCurrent.equals(ds) ) Log.warn(this.getClass(), "Replacing registered dataset for "+node); map.put(node, ds); } - public Dataset get(Resource node) { + public DatasetGraph get(Node node) { return map.get(node); } diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiConfig.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiConfig.java index cacbb712b0..db6de6c5de 100644 --- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiConfig.java +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiConfig.java @@ -21,10 +21,11 @@ package org.apache.jena.fuseki.build; import static java.lang.String.format; import static java.util.stream.Collectors.toList; import static org.apache.jena.fuseki.build.BuildLib.getZeroOrOne; -import static org.apache.jena.fuseki.build.BuildLib.nodeLabel; -import static org.apache.jena.fuseki.server.FusekiVocab.*; +import static org.apache.jena.fuseki.server.FusekiVocabG.*; +import static org.apache.jena.fuseki.build.BuildLib.displayStr; import static org.apache.jena.riot.RDFLanguages.filenameToLang; import static org.apache.jena.riot.RDFParserRegistry.isRegistered; +import static org.apache.jena.system.G.isResource; import java.io.File; import java.io.IOException; @@ -39,7 +40,6 @@ import org.apache.jena.assembler.Assembler; import org.apache.jena.assembler.JA; import org.apache.jena.atlas.lib.IRILib; import org.apache.jena.atlas.lib.Pair; -import org.apache.jena.datatypes.xsd.XSDDatatype; import org.apache.jena.fuseki.Fuseki; import org.apache.jena.fuseki.FusekiConfigException; import org.apache.jena.fuseki.FusekiException; @@ -48,10 +48,10 @@ import org.apache.jena.fuseki.auth.AuthPolicy; import org.apache.jena.fuseki.auth.AuthPolicyList; import org.apache.jena.fuseki.server.*; import org.apache.jena.fuseki.servlets.ActionService; +import org.apache.jena.graph.Graph; import org.apache.jena.graph.Node; import org.apache.jena.query.Dataset; -import org.apache.jena.query.QuerySolution; -import org.apache.jena.query.ResultSet; +import org.apache.jena.query.DatasetFactory; import org.apache.jena.rdf.model.*; import org.apache.jena.rdf.model.impl.Util; import org.apache.jena.riot.Lang; @@ -59,9 +59,10 @@ import org.apache.jena.shared.JenaException; import org.apache.jena.sparql.core.DatasetGraph; import org.apache.jena.sparql.core.assembler.AssemblerUtils; import org.apache.jena.sparql.core.assembler.NamedDatasetAssembler; +import org.apache.jena.sparql.engine.binding.Binding; +import org.apache.jena.sparql.exec.RowSet; import org.apache.jena.sparql.util.Context; -import org.apache.jena.sparql.util.FmtUtils; -import org.apache.jena.sparql.util.graph.GraphUtils; +import org.apache.jena.system.G; import org.apache.jena.vocabulary.RDF; import org.slf4j.Logger; @@ -112,22 +113,22 @@ public class FusekiConfig { return dataServiceBuilder; } - public static void addDataService(DataAccessPointRegistry dataAccessPoints, String name, DataService dataService) { - name = DataAccessPoint.canonical(name); - if ( dataAccessPoints.isRegistered(name) ) - throw new FusekiConfigException("Data service name already registered: "+name); - DataAccessPoint dap = new DataAccessPoint(name, dataService); - dataAccessPoints.register(dap); - } - - public static void addDataset(DataAccessPointRegistry dataAccessPoints, String name, DatasetGraph dsg, boolean withUpdate) { - name = DataAccessPoint.canonical(name); - if ( dataAccessPoints.isRegistered(name) ) - throw new FusekiConfigException("Data service name already registered: "+name); - DataService dataService = buildDataServiceStd(dsg, withUpdate); - DataAccessPoint dap = new DataAccessPoint(name, dataService); - dataAccessPoints.register(dap); - } +// private static void addDataService(DataAccessPointRegistry dataAccessPoints, String name, DataService dataService) { +// name = DataAccessPoint.canonical(name); +// if ( dataAccessPoints.isRegistered(name) ) +// throw new FusekiConfigException("Data service name already registered: "+name); +// DataAccessPoint dap = new DataAccessPoint(name, dataService); +// dataAccessPoints.register(dap); +// } +// +// public static void addDataset(DataAccessPointRegistry dataAccessPoints, String name, DatasetGraph dsg, boolean withUpdate) { +// name = DataAccessPoint.canonical(name); +// if ( dataAccessPoints.isRegistered(name) ) +// throw new FusekiConfigException("Data service name already registered: "+name); +// DataService dataService = buildDataServiceStd(dsg, withUpdate); +// DataAccessPoint dap = new DataAccessPoint(name, dataService); +// dataAccessPoints.register(dap); +// } public static DataService buildDataServiceStd(DatasetGraph dsg, boolean withUpdate) { return DataService.newBuilder(dsg) @@ -140,21 +141,25 @@ public class FusekiConfig { dataAccessPoints.remove(name); } + @Deprecated + public static AuthPolicy allowedUsers(Resource server) { + return allowedUsers(server.getModel().getGraph(), server.asNode()); + } + /** Get the allowed users on a resource. * Returns null if the resource is null or if there were no settings. * * @return RequestAuthorization */ - public static AuthPolicy allowedUsers(Resource resource) { + public static AuthPolicy allowedUsers(Graph graph, Node resource) { if ( resource == null ) return null; - Collection<RDFNode> allowedUsers = BuildLib.getAll(resource, "fu:"+pAllowedUsers.getLocalName()); + Collection<Node> allowedUsers = BuildLib.getMultiple(graph, resource, pAllowedUsers); if ( allowedUsers == null ) // Indicate no settings. return null; // Check all values are simple strings List<String> bad = allowedUsers.stream() - .map(RDFNode::asNode) .filter(rn -> ! Util.isSimpleString(rn)) .map(rn->rn.toString()) .collect(toList()); @@ -164,14 +169,13 @@ public class FusekiConfig { } // RDFNodes/literals to strings. Collection<String> userNames = allowedUsers.stream() - .map(RDFNode::asNode) .map(Node::getLiteralLexicalForm) .collect(toList()); return Auth.policyAllowSpecific(userNames); } /** - * Process a configuration file and return the {@link DataAccessPoint DataAccessPoints}; + * Process a configuration and return the {@link DataAccessPoint DataAccessPoints}; * set the context provided for server-wide settings. * * This bundles together the steps: @@ -182,22 +186,57 @@ public class FusekiConfig { * <li>{@link #servicesAndDatasets} * </ul> */ - public static List<DataAccessPoint> processServerConfiguration(Model configuration, Context context) { - Resource server = findServer(configuration); + public static List<DataAccessPoint> processServerConfiguration(Graph configuration, Context context) { + Node server = findServer(configuration); if ( server != null ) { - mergeContext(server, context); - processLoadClass(server); + // XXX Temporary + Resource rServer = resource(configuration, server); + mergeContext(configuration,server, context); + processLoadClass(configuration,server); } // Process services, whether via server ja:services or, if absent, by finding by type. - return servicesAndDatasets$(server, configuration); + return servicesAndDatasets$(configuration, server); + } + + /** + * Process a configuration and return the {@link DataAccessPoint DataAccessPoints}; + * set the context provided for server-wide settings. + * + * This bundles together the steps: + * <ul> + * <li>{@link #findServer} + * <li>{@link #parseContext} + * <li>{@link #processLoadClass} (legacy) + * <li>{@link #servicesAndDatasets} + * </ul> + */ + public static List<DataAccessPoint> processServerConfiguration(Model configuration, Context context) { + return processServerConfiguration(configuration.getGraph(), context); + } + + // XXX Adapter + /*package*/ static Resource resource(Graph graph, Node node) { + Model m = ModelFactory.createModelForGraph(graph); + RDFNode rNode = m.asRDFNode(node); + if ( ! rNode.isResource() ) + throw new FusekiConfigException("Not a resource: "+node); + return rNode.asResource(); + } + + @Deprecated + public static Resource findServer(Model model) { + Node server = findServer(model.getGraph()); + if ( server == null ) + return null; + return resource(model.getGraph(), server); } /* Find the server resource in a configuration file. * Returns null if there isn't one. * Raises {@link FusekiConfigException} is there are more than one. */ - public static Resource findServer(Model model) { - List<Resource> servers = GraphUtils.listResourcesByType(model, FusekiVocab.tServer); + public static Node findServer(Graph graph) { + List<Node> servers = G.nodesOfTypeAsList(graph, FusekiVocabG.tServer); if ( servers.size() == 0 ) // "No server" is fine. return null; @@ -205,7 +244,7 @@ public class FusekiConfig { throw new FusekiConfigException(servers.size() + " servers found (must be exactly one in a configuration file)"); // ---- Server - Resource server = servers.get(0); + Node server = servers.get(0); return server; } @@ -213,48 +252,51 @@ public class FusekiConfig { * Process the resource for {@link Context} settings. * Return a new {@link Context} */ - private static Context parseContext(Resource resource) { + private static Context parseContext(Graph configuration, Node resource) { if ( resource == null ) return null; - return AssemblerUtils.parseContext(resource); + Resource r = resource(configuration, resource); + return AssemblerUtils.parseContext(r); } /** * Process the resource for {@link Context} settings * and update an existing {@link Context}. */ - private static void mergeContext(Resource resource, Context context) { + private static void mergeContext(Graph configuration, Node resource, Context context) { if ( resource == null ) return ; - AssemblerUtils.mergeContext(resource, context); + Resource r = resource(configuration, resource); + AssemblerUtils.mergeContext(r, context); } /** * Process any {@code ja:loadClass} */ - public static void processLoadClass(Resource server) { + public static void processLoadClass(Graph configuration, Node server) { if ( server == null ) return; - StmtIterator sIter = server.listProperties(JA.loadClass); - for (; sIter.hasNext(); ) { - Statement s = sIter.nextStatement(); - RDFNode rn = s.getObject(); + List<Node> x = G.listSP(configuration, server, JA.loadClass.asNode()); + + for ( Node rn : x ) { String className = null; - if ( rn instanceof Resource res ) { - String uri = res.getURI(); - if ( uri == null ) { - log.warn("Blank node for class to load"); - continue; - } + if ( rn.isBlank() ) { + log.warn("Blank node for class to load"); + continue; + } + if ( rn.isURI() ) { + String uri = rn.getURI(); String javaScheme = "java:"; if ( !uri.startsWith(javaScheme) ) { log.warn("Class to load is not 'java:': " + uri); continue; } className = uri.substring(javaScheme.length()); - } - if ( rn instanceof Literal lit) - className = lit.getLexicalForm(); + } else if ( G.isString(rn) ) + className = rn.getLiteralLexicalForm(); + + if ( className == null ) + throw new FusekiConfigException("Not a class name: "+displayStr(configuration, rn)); loadAndInit(className); } } @@ -264,40 +306,39 @@ public class FusekiConfig { * It looks {@code fuseki:services ( .... )} then, if not found, all {@code rtdf:type fuseki:services}. * @see #processServerConfiguration */ - public static List<DataAccessPoint> servicesAndDatasets(Model model) { - Resource server = findServer(model); - return servicesAndDatasets$(server, model); + public static List<DataAccessPoint> servicesAndDatasets(Graph graph) { + Node server = findServer(graph); + return servicesAndDatasets$(graph, server); } - /** Find and process datasets and services in a configuration file - * starting from {@code server} which can have a {@code fuseki:services ( .... )} - * but, if not found, all {@code rdf:type fuseki:services} are processed. - */ - private - /*public*/ static List<DataAccessPoint> servicesAndDatasets_notUsed(Resource server) { - Objects.requireNonNull(server); - return servicesAndDatasets$(server, server.getModel()); + @Deprecated + public static List<DataAccessPoint> servicesAndDatasets(Model model) { + return servicesAndDatasets(model.getGraph()); } - private static List<DataAccessPoint> servicesAndDatasets$(Resource server, Model model) { + private static List<DataAccessPoint> servicesAndDatasets$(Graph configuration, Node server) { DatasetDescriptionMap dsDescMap = new DatasetDescriptionMap(); NamedDatasetAssembler.sharedDatasetPool.clear(); // ---- Services // Server to services. - ResultSet rs = BuildLib.query("SELECT * { ?s fu:services [ list:member ?service ] }", model, "s", server); + RowSet rs = BuildLib.query("SELECT * { ?s fu:services [ list:member ?service ] }", configuration, "s", server); + + List<Node> services = rs.stream().map(b->b.get("service")).toList(); + List<DataAccessPoint> accessPoints = new ArrayList<>(); // If none, look for services by type. if ( ! rs.hasNext() ) // No "fu:services ( .... )" so try looking for services directly. // This means Fuseki2, service configuration files (no server section) work for --conf. - rs = BuildLib.query("SELECT ?service { ?service a fu:Service }", model); + rs = BuildLib.query("SELECT ?service { ?service a fu:Service }", configuration); // rs is a result set of services to process. for (; rs.hasNext(); ) { - QuerySolution soln = rs.next(); - Resource svc = soln.getResource("service"); - DataAccessPoint acc = buildDataAccessPoint(svc, dsDescMap); + Binding soln = rs.next(); + Node svc = soln.get("service"); + + DataAccessPoint acc = buildDataAccessPoint(configuration, svc, dsDescMap); if ( acc != null ) accessPoints.add(acc); } @@ -350,7 +391,7 @@ public class FusekiConfig { DatasetDescriptionMap dsDescMap = new DatasetDescriptionMap(); String fn = IRILib.filenameToIRI(p.toString()); log.info("Load configuration: "+fn); - Model m = readAssemblerFile(fn); + Graph m = readAssemblerFile(fn).getGraph(); readConfiguration(m, dsDescMap, dataServiceRef); } } catch (IOException ex) { @@ -359,53 +400,57 @@ public class FusekiConfig { return dataServiceRef; } - /** Read a configuration in a model. + /** + * Read a configuration in a model. * Allow dataset descriptions to be carried over from another place. * Add to a list. */ - private static void readConfiguration(Model m, DatasetDescriptionMap dsDescMap, List<DataAccessPoint> dataServiceRef) { - List<Resource> services = GraphUtils.listResourcesByType(m, FusekiVocab.fusekiService); + private static void readConfiguration(Graph configuration, DatasetDescriptionMap dsDescMap, List<DataAccessPoint> dataServiceRef) { + List<Node> services = G.nodesOfTypeAsList(configuration, FusekiVocabG.fusekiService); if ( services.size() == 0 ) { log.error("No services found"); throw new FusekiConfigException(); } - for ( Resource service : services ) { - DataAccessPoint acc = buildDataAccessPoint(service, dsDescMap); + for ( Node service : services ) { + DataAccessPoint acc = buildDataAccessPoint(configuration,service, dsDescMap); if ( acc != null ) dataServiceRef.add(acc); } } - /** Build a DataAccessPoint, including DataService, from the description at Resource svc */ + @Deprecated public static DataAccessPoint buildDataAccessPoint(Resource svc, DatasetDescriptionMap dsDescMap) { - RDFNode n = BuildLib.getOne(svc, FusekiVocab.pServiceName); + return buildDataAccessPoint(svc.getModel().getGraph(), svc.asNode(), dsDescMap); + } + + /** Build a DataAccessPoint, including DataService, from the description at Resource svc */ + public static DataAccessPoint buildDataAccessPoint(Graph configuration, Node fusekiService, DatasetDescriptionMap dsDescMap) { + Node n = BuildLib.getOne(configuration, fusekiService, FusekiVocabG.pServiceName); try { if ( ! n.isLiteral() ) - throw new FusekiConfigException("Not a literal for access point name: "+FmtUtils.stringForRDFNode(n)); - Literal object = n.asLiteral(); - - if ( object.getDatatype() != null && ! object.getDatatype().equals(XSDDatatype.XSDstring) ) - Fuseki.configLog.error(format("Service name '%s' is not a string", FmtUtils.stringForRDFNode(object))); + throw new FusekiConfigException("Not a literal for access point name: "+BuildLib.displayStr(configuration, n)); + if ( ! Util.isSimpleString(n) ) + Fuseki.configLog.error(format("Service name '%s' is not a string", BuildLib.strForResource(configuration, n))); - String name = object.getLexicalForm(); + String name = n.getLiteralLexicalForm(); name = DataAccessPoint.canonical(name); - AuthPolicy allowedUsers = allowedUsers(svc); - DataService dataService = buildDataService(svc, dsDescMap).setAuthPolicy(allowedUsers).build(); + AuthPolicy allowedUsers = allowedUsers(configuration, fusekiService); + DataService dataService = buildDataService(configuration, fusekiService, dsDescMap).setAuthPolicy(allowedUsers).build(); DataAccessPoint dataAccess = new DataAccessPoint(name, dataService); return dataAccess; } catch (FusekiException ex) { - Fuseki.configLog.error("Skipping: Failed to build service for "+FmtUtils.stringForRDFNode(n)); + Fuseki.configLog.error("Skipping: Failed to build service for "+BuildLib.displayStr(configuration, n)); Fuseki.configLog.error(" "+ex.getMessage()); return null; } } - private static DataService.Builder buildDataService(Resource fusekiService, DatasetDescriptionMap dsDescMap) { - Resource datasetDesc = (Resource)BuildLib.getOne(fusekiService, FusekiVocab.pDataset); - Dataset ds = getDataset(datasetDesc, dsDescMap); - DataService.Builder dataService = DataService.newBuilder(ds.asDatasetGraph()); + private static DataService.Builder buildDataService(Graph configuration, Node fusekiService, DatasetDescriptionMap dsDescMap) { + Node datasetDesc = BuildLib.getOne(configuration, fusekiService, FusekiVocabG.pDataset); + DatasetGraph dsg = getDataset(configuration, datasetDesc, dsDescMap); + DataService.Builder dataService = DataService.newBuilder(dsg); Set<Endpoint> endpoints1 = new HashSet<>(); Set<Endpoint> endpoints2 = new HashSet<>(); @@ -413,11 +458,11 @@ public class FusekiConfig { // fuseki:serviceQuery "sparql"; //or // fuseki:serviceQuery [ fuseki:name "sparql" ; fuseki:allowedUsers (..) ]; - accEndpointOldStyle(endpoints1, Operation.Query, fusekiService, pServiceQueryEP); - accEndpointOldStyle(endpoints1, Operation.Update, fusekiService, pServiceUpdateEP); - //accEndpointOldStyle(endpoints1, Operation.Upload, fusekiService, pServiceUploadEP); - accEndpointOldStyle(endpoints1, Operation.GSP_R, fusekiService, pServiceReadGraphStoreEP); - accEndpointOldStyle(endpoints1, Operation.GSP_RW, fusekiService, pServiceReadWriteGraphStoreEP); + accEndpointOldStyle(endpoints1, Operation.Query, configuration, fusekiService, pServiceQueryEP); + accEndpointOldStyle(endpoints1, Operation.Update, configuration, fusekiService, pServiceUpdateEP); + //accEndpointOldStyle(endpoints1, Operation.Upload, configuration, fusekiService, pServiceUploadEP); + accEndpointOldStyle(endpoints1, Operation.GSP_R, configuration, fusekiService, pServiceReadGraphStoreEP); + accEndpointOldStyle(endpoints1, Operation.GSP_RW, configuration, fusekiService, pServiceReadWriteGraphStoreEP); // ---- Legacy for old style: a request would also try the dataset (i.e. no endpoint name). // If "sparql" then allow /dataset?query= @@ -435,7 +480,7 @@ public class FusekiConfig { // fuseki:endpoint [ fuseki:operation fuseki:query ; fuseki:name "" ; fuseki:allowedUsers (....) ] ; // and more. - accFusekiEndpoints(endpoints2, fusekiService, dsDescMap); + accFusekiEndpoints(endpoints2, configuration, fusekiService, dsDescMap); // This will overwrite old style entries of the same fuseki:name. endpoints2.forEach(dataService::addEndpoint); @@ -477,12 +522,12 @@ public class FusekiConfig { /** Find and parse {@code fuseki:endpoint} descriptions. */ private - static void accFusekiEndpoints(Set<Endpoint> endpoints, Resource fusekiService, DatasetDescriptionMap dsDescMap) { - StmtIterator endpointsDesc = fusekiService.listProperties(pEndpoint); - endpointsDesc.forEachRemaining(ep-> { - if ( ! ep.getObject().isResource() ) - throw new FusekiConfigException("Literal for fuseki:endpoint: expected blank node or resource: "+FmtUtils.stringForRDFNode(fusekiService)); - Endpoint endpoint = buildEndpoint(fusekiService, ep.getObject().asResource()); + static void accFusekiEndpoints(Set<Endpoint> endpoints, Graph configuration, Node fusekiService, DatasetDescriptionMap dsDescMap) { + List<Node> endpointsDesc = G.listSP(configuration, fusekiService, pEndpoint); + endpointsDesc.forEach(ep-> { + if ( ! isResource(ep) ) + throw new FusekiConfigException("Literal for fuseki:endpoint: expected blank node or resource: "+displayStr(configuration, fusekiService)); + Endpoint endpoint = buildEndpoint(configuration, fusekiService, ep); endpoints.add(endpoint); }); } @@ -505,51 +550,50 @@ public class FusekiConfig { * ] ; * </pre> */ - private static Endpoint buildEndpoint(Resource fusekiService, Resource endpoint) { + private static Endpoint buildEndpoint(Graph configuration, Node fusekiService, Node endpoint) { // Endpoints are often blank nodes so use fusekiService in error messages. // fuseki:operation - RDFNode opResource = getZeroOrOne(endpoint, pOperation); + Node opRef = BuildLib.getOne(configuration, endpoint, pOperation); Operation op = null; - if ( opResource != null ) { - if ( ! opResource.isResource() || opResource.isAnon() ) - throw exception("Blank node endpoint operation in service %s", nodeLabel(fusekiService)); - Node opRef = opResource.asNode(); + if ( opRef != null ) { + if ( G.isBlank(opRef) ) + throw exception("Blank node endpoint operation in service %s", displayStr(configuration, fusekiService)); op = Operation.get(opRef); } // fuseki:implementation - checking only, not active. if ( op == null ) { - RDFNode rImpl = getZeroOrOne(endpoint, pImplementation); + Node rImpl = getZeroOrOne(configuration, endpoint, pImplementation); if ( rImpl == null ) - throw exception("No implementation for fuseki:operation '%s' in service %s", nodeLabel(opResource), nodeLabel(fusekiService)); + throw exception("No implementation for fuseki:operation '%s' in service %s", displayStr(configuration, opRef), displayStr(configuration, fusekiService)); // Global registry. Replace existing registry. - Pair<Operation, ActionService> x = BuildLib.loadOperationActionService(rImpl); + Pair<Operation, ActionService> x = BuildLib.loadOperationActionService(configuration, rImpl); Operation op2 = x.getLeft(); ActionService proc = x.getRight(); if ( op2 == null ) - throw exception("Failed to load implementation for fuseki:operation '%s' in service %s", nodeLabel(opResource), nodeLabel(fusekiService)); + throw exception("Failed to load implementation for fuseki:operation '%s' in service %s", displayStr(configuration, opRef), displayStr(configuration, fusekiService)); op = op2; // Using a blank node (!) for the operation means this is safe! // OperationRegistry.get().register(op2, proc); } // fuseki:allowedUsers - AuthPolicy authPolicy = FusekiConfig.allowedUsers(endpoint); + AuthPolicy authPolicy = FusekiConfig.allowedUsers(configuration, endpoint); // fuseki:name - RDFNode epNameR = getZeroOrOne(endpoint, pEndpointName); - String epName = null; - if ( epNameR == null ) { + Node epNameN = getZeroOrOne(configuration, endpoint, pEndpointName); + String epName; + if ( epNameN == null ) { // // Make required to give "" for dataset, not default to dataset if missing. // throw exception("No service name for endpoint", fusekiService, ep, pServiceName); epName = Endpoint.DatasetEP.string; } else { - if ( ! epNameR.isLiteral() ) + if ( ! G.isString(epNameN) ) throw exception("Not a literal for service name for endpoint", fusekiService, endpoint, pEndpointName); - epName = epNameR.asLiteral().getLexicalForm(); + epName = epNameN.getLiteralLexicalForm(); } - Context cxt = parseContext(endpoint); + Context cxt = parseContext(configuration, endpoint); // Per-endpoint context. // Could add special names: @@ -576,33 +620,34 @@ public class FusekiConfig { // fuseki:serviceQuery "sparql"; //or // fuseki:serviceQuery [ fuseki:name "sparql" ; fuseki:allowedUsers (..) ]; - private static void accEndpointOldStyle(Collection<Endpoint> endpoints, Operation operation, Resource svc, Property property) { - String p = "<"+property.getURI()+">"; - ResultSet rs = BuildLib.query("SELECT * { ?svc " + p + " ?ep}", svc.getModel(), "svc", svc); - for (; rs.hasNext(); ) { - QuerySolution soln = rs.next(); - // No policy yet - set below if one is found. + private static void accEndpointOldStyle(Collection<Endpoint> endpoints, Operation operation, Graph configuration, Node svc, Node endpointProperty) { + + List<Node> endPts = G.listSP(configuration, svc, endpointProperty); + endPts.forEach(ep->{ AuthPolicy authPolicy = null; - RDFNode ep = soln.get("ep"); String endpointName = null; if ( ep.isLiteral() ) // fuseki:serviceQuery "sparql" - endpointName = soln.getLiteral("ep").getLexicalForm(); - else if ( ep.isResource() ) { - Resource r = (Resource)ep; + endpointName = ep.getLiteralLexicalForm(); + else if ( isResource(ep) ) { try { // [ fuseki:name ""; fuseki:allowedUsers ( "" "" ) ] - Statement stmt = r.getProperty(FusekiVocab.pEndpointName); - if ( stmt == null ) - throw new FusekiConfigException("Expected property <"+FusekiVocab.pEndpointName+"> with <"+property.getURI()+"> for <"+svc+">"); - endpointName = stmt.getString(); - List<RDFNode> x = GraphUtils.multiValue(r, FusekiVocab.pAllowedUsers); + + List<Node> named = G.listSP(configuration, ep, FusekiVocabG.pEndpointName); + if ( named.isEmpty() ) + throw new FusekiConfigException("Expected property <"+FusekiVocabG.pEndpointName+"> with <"+endpointProperty.getURI()+"> for "+BuildLib.displayStr(configuration, svc)); + if ( named.size() > 1 ) + throw new FusekiConfigException("Multiple property values for <"+FusekiVocabG.pEndpointName+"> with <"+endpointProperty.getURI()+"> for "+BuildLib.displayStr(configuration, svc)); + endpointName = named.get(0).getLiteralLexicalForm(); + // XXX Necessary? check + List<Node> x = G.listSP(configuration, ep, FusekiVocabG.pAllowedUsers); if ( x.size() > 1 ) - throw new FusekiConfigException("Multiple fuseki:"+FusekiVocab.pAllowedUsers.getLocalName()+" for "+r); + throw new FusekiConfigException("Multiple fuseki:"+FusekiVocabG.pAllowedUsers.getLocalName()+" for "+displayStr(configuration, ep)); + // Check if ( ! x.isEmpty() ) - authPolicy = allowedUsers(r); + authPolicy = allowedUsers(configuration, ep); } catch(JenaException | ClassCastException ex) { - throw new FusekiConfigException("Failed to parse endpoint: "+r); + throw new FusekiConfigException("Failed to parse endpoint: "+displayStr(configuration, ep)); } } else { throw new FusekiConfigException("Unrecognized: "+ep); @@ -612,7 +657,7 @@ public class FusekiConfig { endpointName = null; Endpoint endpoint = Endpoint.create(operation, endpointName, authPolicy); endpoints.add(endpoint); - } + }); } private static void accEndpoint(Collection<Endpoint> endpoints, Operation operation) { @@ -630,26 +675,37 @@ public class FusekiConfig { endpoints.add(endpoint); } + @Deprecated public static Dataset getDataset(Resource datasetDesc, DatasetDescriptionMap dsDescMap) { + DatasetGraph dsg = getDataset(datasetDesc.getModel().getGraph(), datasetDesc.asNode(), dsDescMap); + return DatasetFactory.wrap(dsg); + } + + public static DatasetGraph getDataset(Graph configuration, Node datasetDesc, DatasetDescriptionMap dsDescMap) { // check if this one already built // This is absolute and does not require a NamedDatasetAssembler and to have a ja:name. // ja:name/NamedDatasetAssembler must be used if the service datasets need to // wire up sharing of a graph of datasets (not TDB). - Dataset ds = dsDescMap.get(datasetDesc); - if ( ds != null ) - return ds; + DatasetGraph dsg = dsDescMap.get(datasetDesc); + if ( dsg != null ) + return dsg; // Not seen before. // Check if the description is in the model. - if ( !datasetDesc.hasProperty(RDF.type) ) - throw new FusekiConfigException("No rdf:type for dataset " + nodeLabel(datasetDesc)); + if ( ! G.hasProperty(configuration, datasetDesc, RDF.Nodes.type) ) + throw new FusekiConfigException("No rdf:type for dataset " + displayStr(configuration, datasetDesc)); // Should have been done already. e.g. ActionDatasets.execPostContainer, // AssemblerUtils.readAssemblerFile < FusekiServer.parseConfigFile. //AssemblerUtils.addRegistered(datasetDesc.getModel()); - ds = (Dataset)Assembler.general.open(datasetDesc); - dsDescMap.register(datasetDesc, ds); - return ds; + + Resource r = resource(configuration, datasetDesc); + Dataset ds = (Dataset)Assembler.general.open(r); + if ( ds == null ) + throw new FusekiConfigException("Bad description of a dataset: " + displayStr(configuration, datasetDesc)); + dsg = ds.asDatasetGraph(); + dsDescMap.register(datasetDesc, dsg); + return dsg; } } diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/BuildLib.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/model/BuildLibM.java similarity index 94% copy from jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/BuildLib.java copy to jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/model/BuildLibM.java index 302496b0b6..cc00bb01b8 100644 --- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/BuildLib.java +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/model/BuildLibM.java @@ -16,7 +16,7 @@ * limitations under the License. */ -package org.apache.jena.fuseki.build; +package org.apache.jena.fuseki.build.model; import static org.apache.jena.fuseki.build.FusekiPrefixes.PREFIXES; import static org.apache.jena.riot.out.NodeFmtLib.displayStr; @@ -44,9 +44,9 @@ import org.apache.jena.vocabulary.RDFS; /** * Library code for operations related to building Fuseki servers and services. */ -/*package*/ class BuildLib { +/*package*/ class BuildLibM { - private BuildLib() {} + private BuildLibM() {} // ---- Helper code /*package*/ static ResultSet query(String string, Model m) { @@ -107,12 +107,12 @@ import org.apache.jena.vocabulary.RDFS; } /*package*/ static RDFNode getOne(Resource svc, Property property) { - ResultSet rs = BuildLib.query("SELECT * { ?svc <" + property.getURI() + "> ?x}", svc.getModel(), "svc", svc); + ResultSet rs = BuildLibM.query("SELECT * { ?svc <" + property.getURI() + "> ?x}", svc.getModel(), "svc", svc); if ( !rs.hasNext() ) - throw new FusekiConfigException("No property '" + property + "' for service " + BuildLib.nodeLabel(svc)); + throw new FusekiConfigException("No property '" + property + "' for service " + BuildLibM.nodeLabel(svc)); RDFNode x = rs.next().get("x"); if ( rs.hasNext() ) - throw new FusekiConfigException("Multiple properties '" + property + "' for service " + BuildLib.nodeLabel(svc)); + throw new FusekiConfigException("Multiple properties '" + property + "' for service " + BuildLibM.nodeLabel(svc)); return x; } @@ -122,7 +122,7 @@ import org.apache.jena.vocabulary.RDFS; * the difference between "not present" and an empty list value. */ /*package*/ static Collection<RDFNode> getAll(Resource resource, String property) { - ResultSet rs = BuildLib.query("SELECT * { ?subject " + property + " ?x}", resource.getModel(), "subject", resource); + ResultSet rs = BuildLibM.query("SELECT * { ?subject " + property + " ?x}", resource.getModel(), "subject", resource); if ( ! rs.hasNext() ) return null; List<RDFNode> results = new ArrayList<>(); diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/DatasetDescriptionMap.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/model/DatasetDescriptionMapM.java similarity index 93% copy from jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/DatasetDescriptionMap.java copy to jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/model/DatasetDescriptionMapM.java index 647aa94959..6595ebe657 100644 --- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/DatasetDescriptionMap.java +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/model/DatasetDescriptionMapM.java @@ -16,7 +16,7 @@ * limitations under the License. */ -package org.apache.jena.fuseki.build; +package org.apache.jena.fuseki.build.model; import java.util.HashMap; import java.util.Map; @@ -33,11 +33,11 @@ import org.apache.jena.rdf.model.Resource; * corresponds to one dataset object when multiple services refer to the * same dataset. */ -public class DatasetDescriptionMap { +class DatasetDescriptionMapM { private Map<Resource, Dataset> map = new HashMap<>(); - public DatasetDescriptionMap() {} + /*package*/ DatasetDescriptionMapM() {} public void register(Resource node, Dataset ds) { Dataset dsCurrent = map.get(node); diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiConfig.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/model/FusekiConfigM.java similarity index 71% copy from jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiConfig.java copy to jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/model/FusekiConfigM.java index cacbb712b0..7564e973cd 100644 --- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/FusekiConfig.java +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/build/model/FusekiConfigM.java @@ -16,45 +16,34 @@ * limitations under the License. */ -package org.apache.jena.fuseki.build; +package org.apache.jena.fuseki.build.model; import static java.lang.String.format; -import static java.util.stream.Collectors.toList; -import static org.apache.jena.fuseki.build.BuildLib.getZeroOrOne; -import static org.apache.jena.fuseki.build.BuildLib.nodeLabel; +import static org.apache.jena.fuseki.build.model.BuildLibM.getZeroOrOne; +import static org.apache.jena.fuseki.build.model.BuildLibM.nodeLabel; import static org.apache.jena.fuseki.server.FusekiVocab.*; -import static org.apache.jena.riot.RDFLanguages.filenameToLang; -import static org.apache.jena.riot.RDFParserRegistry.isRegistered; -import java.io.File; -import java.io.IOException; import java.lang.reflect.Method; -import java.nio.file.DirectoryStream; -import java.nio.file.Files; -import java.nio.file.Path; import java.util.*; import org.apache.commons.lang3.StringUtils; import org.apache.jena.assembler.Assembler; -import org.apache.jena.assembler.JA; -import org.apache.jena.atlas.lib.IRILib; import org.apache.jena.atlas.lib.Pair; import org.apache.jena.datatypes.xsd.XSDDatatype; import org.apache.jena.fuseki.Fuseki; import org.apache.jena.fuseki.FusekiConfigException; import org.apache.jena.fuseki.FusekiException; -import org.apache.jena.fuseki.auth.Auth; import org.apache.jena.fuseki.auth.AuthPolicy; import org.apache.jena.fuseki.auth.AuthPolicyList; +import org.apache.jena.fuseki.build.FusekiConfig; import org.apache.jena.fuseki.server.*; import org.apache.jena.fuseki.servlets.ActionService; +import org.apache.jena.graph.Graph; import org.apache.jena.graph.Node; import org.apache.jena.query.Dataset; import org.apache.jena.query.QuerySolution; import org.apache.jena.query.ResultSet; import org.apache.jena.rdf.model.*; -import org.apache.jena.rdf.model.impl.Util; -import org.apache.jena.riot.Lang; import org.apache.jena.shared.JenaException; import org.apache.jena.sparql.core.DatasetGraph; import org.apache.jena.sparql.core.assembler.AssemblerUtils; @@ -66,7 +55,8 @@ import org.apache.jena.vocabulary.RDF; import org.slf4j.Logger; /** Functions to setup and act on the configuration of a Fuseki server */ -public class FusekiConfig { +//public +class FusekiConfigM { private static Logger log = Fuseki.configLog; // The default setup of a DataService. @@ -98,46 +88,51 @@ public class FusekiConfig { /** Convenience operation to populate a {@link DataService} with the conventional default services. */ public static DataService.Builder populateStdServices(DataService.Builder dataServiceBuilder, boolean allowUpdate) { - Set<Endpoint> endpoints = new HashSet<>(); - if ( allowUpdate ) { - stdWrite.forEach((name, op) -> accEndpoint(endpoints, op, name)); - stdDatasetWrite.forEach(op -> accEndpoint(endpoints, op)); - } else { - stdRead.forEach((name, op) -> accEndpoint(endpoints, op, name)); - stdDatasetRead.forEach(op -> accEndpoint(endpoints, op)); - if ( FusekiExt.extraOperationServicesRead != null ) - FusekiExt.extraOperationServicesRead.forEach((name, op) -> accEndpoint(endpoints, op, name)); - } - endpoints.forEach(dataServiceBuilder::addEndpoint); - return dataServiceBuilder; - } - - public static void addDataService(DataAccessPointRegistry dataAccessPoints, String name, DataService dataService) { - name = DataAccessPoint.canonical(name); - if ( dataAccessPoints.isRegistered(name) ) - throw new FusekiConfigException("Data service name already registered: "+name); - DataAccessPoint dap = new DataAccessPoint(name, dataService); - dataAccessPoints.register(dap); + return FusekiConfig.populateStdServices(dataServiceBuilder, allowUpdate); + +// Set<Endpoint> endpoints = new HashSet<>(); +// if ( allowUpdate ) { +// stdWrite.forEach((name, op) -> accEndpoint(endpoints, op, name)); +// stdDatasetWrite.forEach(op -> accEndpoint(endpoints, op)); +// } else { +// stdRead.forEach((name, op) -> accEndpoint(endpoints, op, name)); +// stdDatasetRead.forEach(op -> accEndpoint(endpoints, op)); +// if ( FusekiExt.extraOperationServicesRead != null ) +// FusekiExt.extraOperationServicesRead.forEach((name, op) -> accEndpoint(endpoints, op, name)); +// } +// endpoints.forEach(dataServiceBuilder::addEndpoint); +// return dataServiceBuilder; } - public static void addDataset(DataAccessPointRegistry dataAccessPoints, String name, DatasetGraph dsg, boolean withUpdate) { - name = DataAccessPoint.canonical(name); - if ( dataAccessPoints.isRegistered(name) ) - throw new FusekiConfigException("Data service name already registered: "+name); - DataService dataService = buildDataServiceStd(dsg, withUpdate); - DataAccessPoint dap = new DataAccessPoint(name, dataService); - dataAccessPoints.register(dap); - } +// private static void addDataService(DataAccessPointRegistry dataAccessPoints, String name, DataService dataService) { +// +// name = DataAccessPoint.canonical(name); +// if ( dataAccessPoints.isRegistered(name) ) +// throw new FusekiConfigException("Data service name already registered: "+name); +// DataAccessPoint dap = new DataAccessPoint(name, dataService); +// dataAccessPoints.register(dap); +// } +// +// public static void addDataset(DataAccessPointRegistry dataAccessPoints, String name, DatasetGraph dsg, boolean withUpdate) { +// name = DataAccessPoint.canonical(name); +// if ( dataAccessPoints.isRegistered(name) ) +// throw new FusekiConfigException("Data service name already registered: "+name); +// DataService dataService = buildDataServiceStd(dsg, withUpdate); +// DataAccessPoint dap = new DataAccessPoint(name, dataService); +// dataAccessPoints.register(dap); +// } public static DataService buildDataServiceStd(DatasetGraph dsg, boolean withUpdate) { - return DataService.newBuilder(dsg) - .withStdServices(withUpdate) - .build(); + return FusekiConfig.buildDataServiceStd(dsg, withUpdate); +// return DataService.newBuilder(dsg) +// .withStdServices(withUpdate) +// .build(); } public static void removeDataset(DataAccessPointRegistry dataAccessPoints, String name) { - name = DataAccessPoint.canonical(name); - dataAccessPoints.remove(name); + FusekiConfig.removeDataset(dataAccessPoints, name); +// name = DataAccessPoint.canonical(name); +// dataAccessPoints.remove(name); } /** Get the allowed users on a resource. @@ -146,28 +141,29 @@ public class FusekiConfig { * @return RequestAuthorization */ public static AuthPolicy allowedUsers(Resource resource) { - if ( resource == null ) - return null; - Collection<RDFNode> allowedUsers = BuildLib.getAll(resource, "fu:"+pAllowedUsers.getLocalName()); - if ( allowedUsers == null ) - // Indicate no settings. - return null; - // Check all values are simple strings - List<String> bad = allowedUsers.stream() - .map(RDFNode::asNode) - .filter(rn -> ! Util.isSimpleString(rn)) - .map(rn->rn.toString()) - .collect(toList()); - if ( ! bad.isEmpty() ) { - //Fuseki.configLog.error(format("User names must be a simple string: bad = %s", bad)); - throw new FusekiConfigException(format("User names should be a simple string: bad = %s", bad)); - } - // RDFNodes/literals to strings. - Collection<String> userNames = allowedUsers.stream() - .map(RDFNode::asNode) - .map(Node::getLiteralLexicalForm) - .collect(toList()); - return Auth.policyAllowSpecific(userNames); + return FusekiConfig.allowedUsers(resource.getModel().getGraph(), resource.asNode()); +// if ( resource == null ) +// return null; +// Collection<RDFNode> allowedUsers = BuildLib.getAll(resource, "fu:"+pAllowedUsers.getLocalName()); +// if ( allowedUsers == null ) +// // Indicate no settings. +// return null; +// // Check all values are simple strings +// List<String> bad = allowedUsers.stream() +// .map(RDFNode::asNode) +// .filter(rn -> ! Util.isSimpleString(rn)) +// .map(rn->rn.toString()) +// .collect(toList()); +// if ( ! bad.isEmpty() ) { +// //Fuseki.configLog.error(format("User names must be a simple string: bad = %s", bad)); +// throw new FusekiConfigException(format("User names should be a simple string: bad = %s", bad)); +// } +// // RDFNodes/literals to strings. +// Collection<String> userNames = allowedUsers.stream() +// .map(RDFNode::asNode) +// .map(Node::getLiteralLexicalForm) +// .collect(toList()); +// return Auth.policyAllowSpecific(userNames); } /** @@ -183,13 +179,14 @@ public class FusekiConfig { * </ul> */ public static List<DataAccessPoint> processServerConfiguration(Model configuration, Context context) { - Resource server = findServer(configuration); - if ( server != null ) { - mergeContext(server, context); - processLoadClass(server); - } - // Process services, whether via server ja:services or, if absent, by finding by type. - return servicesAndDatasets$(server, configuration); + return FusekiConfig.processServerConfiguration(configuration.getGraph(), context); +// Resource server = findServer(configuration); +// if ( server != null ) { +// mergeContext(server, context); +// processLoadClass(server); +// } +// // Process services, whether via server ja:services or, if absent, by finding by type. +// return servicesAndDatasets$(server, configuration); } /* Find the server resource in a configuration file. @@ -197,16 +194,29 @@ public class FusekiConfig { * Raises {@link FusekiConfigException} is there are more than one. */ public static Resource findServer(Model model) { - List<Resource> servers = GraphUtils.listResourcesByType(model, FusekiVocab.tServer); - if ( servers.size() == 0 ) - // "No server" is fine. + Node n = FusekiConfig.findServer(model.getGraph()); + if ( n == null ) return null; - if ( servers.size() > 1 ) - throw new FusekiConfigException(servers.size() - + " servers found (must be exactly one in a configuration file)"); - // ---- Server - Resource server = servers.get(0); - return server; + return resourceAdapter(model.getGraph(), n); + +// List<Resource> servers = GraphUtils.listResourcesByType(model, FusekiVocab.tServer); +// if ( servers.size() == 0 ) +// // "No server" is fine. +// return null; +// if ( servers.size() > 1 ) +// throw new FusekiConfigException(servers.size() +// + " servers found (must be exactly one in a configuration file)"); +// // ---- Server +// Resource server = servers.get(0); +// return server; + } + + private static Resource resourceAdapter(Graph graph, Node node) { + Model m = ModelFactory.createModelForGraph(graph); + RDFNode rNode = m.asRDFNode(node); + if ( ! rNode.isResource() ) + throw new FusekiConfigException("Not a resource: "+node); + return rNode.asResource(); } /** @@ -233,30 +243,31 @@ public class FusekiConfig { * Process any {@code ja:loadClass} */ public static void processLoadClass(Resource server) { - if ( server == null ) - return; - StmtIterator sIter = server.listProperties(JA.loadClass); - for (; sIter.hasNext(); ) { - Statement s = sIter.nextStatement(); - RDFNode rn = s.getObject(); - String className = null; - if ( rn instanceof Resource res ) { - String uri = res.getURI(); - if ( uri == null ) { - log.warn("Blank node for class to load"); - continue; - } - String javaScheme = "java:"; - if ( !uri.startsWith(javaScheme) ) { - log.warn("Class to load is not 'java:': " + uri); - continue; - } - className = uri.substring(javaScheme.length()); - } - if ( rn instanceof Literal lit) - className = lit.getLexicalForm(); - loadAndInit(className); - } + FusekiConfig.processLoadClass(server.getModel().getGraph(), server.asNode()); +// if ( server == null ) +// return; +// StmtIterator sIter = server.listProperties(JA.loadClass); +// for (; sIter.hasNext(); ) { +// Statement s = sIter.nextStatement(); +// RDFNode rn = s.getObject(); +// String className = null; +// if ( rn instanceof Resource res ) { +// String uri = res.getURI(); +// if ( uri == null ) { +// log.warn("Blank node for class to load"); +// continue; +// } +// String javaScheme = "java:"; +// if ( !uri.startsWith(javaScheme) ) { +// log.warn("Class to load is not 'java:': " + uri); +// continue; +// } +// className = uri.substring(javaScheme.length()); +// } +// if ( rn instanceof Literal lit) +// className = lit.getLexicalForm(); +// loadAndInit(className); +// } } /** Find and process datasets and services in a configuration file. @@ -269,29 +280,19 @@ public class FusekiConfig { return servicesAndDatasets$(server, model); } - /** Find and process datasets and services in a configuration file - * starting from {@code server} which can have a {@code fuseki:services ( .... )} - * but, if not found, all {@code rdf:type fuseki:services} are processed. - */ - private - /*public*/ static List<DataAccessPoint> servicesAndDatasets_notUsed(Resource server) { - Objects.requireNonNull(server); - return servicesAndDatasets$(server, server.getModel()); - } - private static List<DataAccessPoint> servicesAndDatasets$(Resource server, Model model) { - DatasetDescriptionMap dsDescMap = new DatasetDescriptionMap(); + DatasetDescriptionMapM dsDescMap = new DatasetDescriptionMapM(); NamedDatasetAssembler.sharedDatasetPool.clear(); // ---- Services // Server to services. - ResultSet rs = BuildLib.query("SELECT * { ?s fu:services [ list:member ?service ] }", model, "s", server); + ResultSet rs = BuildLibM.query("SELECT * { ?s fu:services [ list:member ?service ] }", model, "s", server); List<DataAccessPoint> accessPoints = new ArrayList<>(); // If none, look for services by type. if ( ! rs.hasNext() ) // No "fu:services ( .... )" so try looking for services directly. // This means Fuseki2, service configuration files (no server section) work for --conf. - rs = BuildLib.query("SELECT ?service { ?service a fu:Service }", model); + rs = BuildLibM.query("SELECT ?service { ?service a fu:Service }", model); // rs is a result set of services to process. for (; rs.hasNext(); ) { @@ -327,43 +328,44 @@ public class FusekiConfig { /** Read service descriptions in the given directory */ public static List<DataAccessPoint> readConfigurationDirectory(String dir) { - Path pDir = Path.of(dir).normalize(); - File dirFile = pDir.toFile(); - if ( ! dirFile.exists() ) { - log.warn("Not found: directory for assembler files for services: '"+dir+"'"); - return Collections.emptyList(); - } - if ( ! dirFile.isDirectory() ) { - log.warn("Not a directory: '"+dir+"'"); - return Collections.emptyList(); - } - // Files that are not hidden. - DirectoryStream.Filter<Path> filter = (entry)-> { - File f = entry.toFile(); - final Lang lang = filenameToLang(f.getName()); - return ! f.isHidden() && f.isFile() && lang != null && isRegistered(lang); - }; - - List<DataAccessPoint> dataServiceRef = new ArrayList<>(); - try (DirectoryStream<Path> stream = Files.newDirectoryStream(pDir, filter)) { - for ( Path p : stream ) { - DatasetDescriptionMap dsDescMap = new DatasetDescriptionMap(); - String fn = IRILib.filenameToIRI(p.toString()); - log.info("Load configuration: "+fn); - Model m = readAssemblerFile(fn); - readConfiguration(m, dsDescMap, dataServiceRef); - } - } catch (IOException ex) { - log.warn("IOException:"+ex.getMessage(), ex); - } - return dataServiceRef; + return FusekiConfig.readConfigurationDirectory(dir); +// Path pDir = Path.of(dir).normalize(); +// File dirFile = pDir.toFile(); +// if ( ! dirFile.exists() ) { +// log.warn("Not found: directory for assembler files for services: '"+dir+"'"); +// return Collections.emptyList(); +// } +// if ( ! dirFile.isDirectory() ) { +// log.warn("Not a directory: '"+dir+"'"); +// return Collections.emptyList(); +// } +// // Files that are not hidden. +// DirectoryStream.Filter<Path> filter = (entry)-> { +// File f = entry.toFile(); +// final Lang lang = filenameToLang(f.getName()); +// return ! f.isHidden() && f.isFile() && lang != null && isRegistered(lang); +// }; +// +// List<DataAccessPoint> dataServiceRef = new ArrayList<>(); +// try (DirectoryStream<Path> stream = Files.newDirectoryStream(pDir, filter)) { +// for ( Path p : stream ) { +// DatasetDescriptionMap dsDescMap = new DatasetDescriptionMap(); +// String fn = IRILib.filenameToIRI(p.toString()); +// log.info("Load configuration: "+fn); +// Model m = readAssemblerFile(fn); +// readConfiguration(m, dsDescMap, dataServiceRef); +// } +// } catch (IOException ex) { +// log.warn("IOException:"+ex.getMessage(), ex); +// } +// return dataServiceRef; } /** Read a configuration in a model. * Allow dataset descriptions to be carried over from another place. * Add to a list. */ - private static void readConfiguration(Model m, DatasetDescriptionMap dsDescMap, List<DataAccessPoint> dataServiceRef) { + private static void readConfiguration(Model m, DatasetDescriptionMapM dsDescMap, List<DataAccessPoint> dataServiceRef) { List<Resource> services = GraphUtils.listResourcesByType(m, FusekiVocab.fusekiService); if ( services.size() == 0 ) { @@ -379,8 +381,8 @@ public class FusekiConfig { } /** Build a DataAccessPoint, including DataService, from the description at Resource svc */ - public static DataAccessPoint buildDataAccessPoint(Resource svc, DatasetDescriptionMap dsDescMap) { - RDFNode n = BuildLib.getOne(svc, FusekiVocab.pServiceName); + public static DataAccessPoint buildDataAccessPoint(Resource svc, DatasetDescriptionMapM dsDescMap) { + RDFNode n = BuildLibM.getOne(svc, FusekiVocab.pServiceName); try { if ( ! n.isLiteral() ) throw new FusekiConfigException("Not a literal for access point name: "+FmtUtils.stringForRDFNode(n)); @@ -402,8 +404,8 @@ public class FusekiConfig { } } - private static DataService.Builder buildDataService(Resource fusekiService, DatasetDescriptionMap dsDescMap) { - Resource datasetDesc = (Resource)BuildLib.getOne(fusekiService, FusekiVocab.pDataset); + private static DataService.Builder buildDataService(Resource fusekiService, DatasetDescriptionMapM dsDescMap) { + Resource datasetDesc = (Resource)BuildLibM.getOne(fusekiService, FusekiVocab.pDataset); Dataset ds = getDataset(datasetDesc, dsDescMap); DataService.Builder dataService = DataService.newBuilder(ds.asDatasetGraph()); Set<Endpoint> endpoints1 = new HashSet<>(); @@ -477,7 +479,7 @@ public class FusekiConfig { /** Find and parse {@code fuseki:endpoint} descriptions. */ private - static void accFusekiEndpoints(Set<Endpoint> endpoints, Resource fusekiService, DatasetDescriptionMap dsDescMap) { + static void accFusekiEndpoints(Set<Endpoint> endpoints, Resource fusekiService, DatasetDescriptionMapM dsDescMap) { StmtIterator endpointsDesc = fusekiService.listProperties(pEndpoint); endpointsDesc.forEachRemaining(ep-> { if ( ! ep.getObject().isResource() ) @@ -523,7 +525,7 @@ public class FusekiConfig { if ( rImpl == null ) throw exception("No implementation for fuseki:operation '%s' in service %s", nodeLabel(opResource), nodeLabel(fusekiService)); // Global registry. Replace existing registry. - Pair<Operation, ActionService> x = BuildLib.loadOperationActionService(rImpl); + Pair<Operation, ActionService> x = BuildLibM.loadOperationActionService(rImpl); Operation op2 = x.getLeft(); ActionService proc = x.getRight(); if ( op2 == null ) @@ -534,7 +536,7 @@ public class FusekiConfig { } // fuseki:allowedUsers - AuthPolicy authPolicy = FusekiConfig.allowedUsers(endpoint); + AuthPolicy authPolicy = allowedUsers(endpoint); // fuseki:name RDFNode epNameR = getZeroOrOne(endpoint, pEndpointName); @@ -578,7 +580,7 @@ public class FusekiConfig { // fuseki:serviceQuery [ fuseki:name "sparql" ; fuseki:allowedUsers (..) ]; private static void accEndpointOldStyle(Collection<Endpoint> endpoints, Operation operation, Resource svc, Property property) { String p = "<"+property.getURI()+">"; - ResultSet rs = BuildLib.query("SELECT * { ?svc " + p + " ?ep}", svc.getModel(), "svc", svc); + ResultSet rs = BuildLibM.query("SELECT * { ?svc " + p + " ?ep}", svc.getModel(), "svc", svc); for (; rs.hasNext(); ) { QuerySolution soln = rs.next(); // No policy yet - set below if one is found. @@ -630,7 +632,7 @@ public class FusekiConfig { endpoints.add(endpoint); } - public static Dataset getDataset(Resource datasetDesc, DatasetDescriptionMap dsDescMap) { + public static Dataset getDataset(Resource datasetDesc, DatasetDescriptionMapM dsDescMap) { // check if this one already built // This is absolute and does not require a NamedDatasetAssembler and to have a ja:name. // ja:name/NamedDatasetAssembler must be used if the service datasets need to 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 47974435d9..6d479743fc 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 @@ -27,7 +27,8 @@ import org.apache.jena.rdf.model.ResourceFactory; public class FusekiVocab { - public static String NS = "http://jena.apache.org/fuseki#"; + // Keep in-step with FusekiVocabG (same constants, but as nodes). + public static final String NS = "http://jena.apache.org/fuseki#"; public static final Resource tServer = resource("Server"); diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/FusekiVocabG.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/FusekiVocabG.java new file mode 100644 index 0000000000..44d07aa118 --- /dev/null +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/FusekiVocabG.java @@ -0,0 +1,111 @@ +/* + * 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.FusekiException; +import org.apache.jena.graph.Node; +import org.apache.jena.graph.NodeFactory; +import org.apache.jena.irix.IRIException; +import org.apache.jena.irix.IRIx; + +/** + * Fuseki Vocabulary - using {@link Node Nodes}. + */ +public class FusekiVocabG +{ + // Keep in-step with FusekiVocab (same constants, but as model resources and properties). + private static final String NS = FusekiVocab.NS; + public static String getURI() { return NS; } + + public static final Node tServer = resource("Server"); + + public static final Node fusekiService = resource("Service"); + + public static final Node pServices = property("services"); + + // Server endpoints. + public static final Node pServerPing = property("pingEP"); + public static final Node pServerStats = property("statsEP"); + public static final Node pServerMetrics = property("metricsEP"); + public static final Node pServerCompact = property("compactEP"); + + // Server features + // Fuseki main - servlet context. + public static final Node pServerContextPath = property("contextPath"); + + // Endpoint description. + public static final Node pServiceName = property("name"); + public static final Node pEndpointName = property("name"); + public static final Node pPasswordFile = property("passwd"); + public static final Node pRealm = property("realm"); + public static final Node pAuth = property("auth"); + public static final Node pEndpoint = property("endpoint"); + public static final Node pOperation = property("operation"); + public static final Node pAllowedUsers = property("allowedUsers"); + public static final Node pTimeout = property("timeout"); + public static final Node pImplementation = property("implementation"); + public static final Node pQueryLimit = property("queryLimit"); + public static final Node pUnionDefaultGraph = property("unionDefaultGraph"); + public static final Node pAllowTimeoutOverride = property("allowTimeoutOverride"); + public static final Node pMaximumTimeoutOverride = property("maximumTimeoutOverride"); + public static final Node pDataset = property("dataset"); + + // Endpoint description - old style. + public static final Node pServiceQueryEP = property("serviceQuery"); + public static final Node pServiceUpdateEP = property("serviceUpdate"); + public static final Node pServiceUploadEP = property("serviceUpload"); + public static final Node pServiceShaclEP = property("serviceShacl"); + public static final Node pServiceReadWriteGraphStoreEP = property("serviceReadWriteGraphStore"); + public static final Node pServiceReadGraphStoreEP = property("serviceReadGraphStore"); + // No longer used. +// public static final Node pServiceReadWriteQuadsEP = property("serviceReadWriteQuads"); +// public static final Node pServiceReadQuadsEP = property("serviceReadQuads"); + + // Operation names : the standard operations. + // "alt" names are the same but using "_" not "-". + public static final Node opQuery = resource("query"); + public static final Node opUpdate = resource("update"); + public static final Node opUpload = resource("upload"); + public static final Node opGSP_r = resource("gsp-r"); + public static final Node opGSP_r_alt = resource("gsp_r"); + public static final Node opGSP_rw = resource("gsp-rw"); + public static final Node opGSP_rw_alt = resource("gsp_rw"); + public static final Node opNoOp = resource("no-op"); + public static final Node opNoOp_alt = resource("no_op"); + public static final Node opShacl = resource("shacl"); + public static final Node opPatch = resource("patch"); + + public static final Node opPREFIXES_R = resource("prefixes-r"); + public static final Node opPREFIXES_RW = resource("prefixes-rw"); + + private static Node resource(String localname) { return NodeFactory.createURI(iri(localname)); } + private static Node property(String localname) { return NodeFactory.createURI(iri(localname)); } + + private static String iri(String localname) { + String uri = NS + localname; + try { + IRIx iri = IRIx.create(uri); + if ( ! iri.isReference() ) + throw new FusekiException("Bad IRI (relative): "+uri); + return uri; + } catch (IRIException ex) { + throw new FusekiException("Bad IRI: "+uri); + } + } +} diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/Operation.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/Operation.java index 6c833c113f..d066b7a123 100644 --- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/Operation.java +++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/Operation.java @@ -29,7 +29,6 @@ import org.apache.jena.graph.Node; import org.apache.jena.graph.NodeFactory; import org.apache.jena.irix.IRIException; import org.apache.jena.irix.IRIx; -import org.apache.jena.rdf.model.Resource; /** * Operations are symbol to look up in the {@link OperationRegistry#operationToHandler} map. The name @@ -38,8 +37,6 @@ import org.apache.jena.rdf.model.Resource; */ public class Operation { - private static String NS = FusekiVocab.NS; - /** Create/intern. Maps short name to operation. */ static private Map<Node, Operation> mgr = new HashMap<>(); @@ -77,26 +74,26 @@ public class Operation { return new Operation(id, shortName, lowercase(shortName), description); } - public static final Operation Query = alloc(FusekiVocab.opQuery.asNode(), "query", "SPARQL Query"); - public static final Operation Update = alloc(FusekiVocab.opUpdate.asNode(), "update", "SPARQL Update"); - public static final Operation GSP_R = alloc(FusekiVocab.opGSP_r.asNode(), "gsp-r", "Graph Store Protocol (Read)"); - public static final Operation GSP_RW = alloc(FusekiVocab.opGSP_rw.asNode(), "gsp-rw", "Graph Store Protocol"); + public static final Operation Query = alloc(FusekiVocabG.opQuery, "query", "SPARQL Query"); + public static final Operation Update = alloc(FusekiVocabG.opUpdate, "update", "SPARQL Update"); + public static final Operation GSP_R = alloc(FusekiVocabG.opGSP_r, "gsp-r", "Graph Store Protocol (Read)"); + public static final Operation GSP_RW = alloc(FusekiVocabG.opGSP_rw, "gsp-rw", "Graph Store Protocol"); - public static final Operation Shacl = alloc(FusekiVocab.opShacl.asNode(), "SHACL", "SHACL Validation"); - public static final Operation Upload = alloc(FusekiVocab.opUpload.asNode(), "upload", "File Upload"); - public static final Operation Patch = alloc(FusekiVocab.opPatch.asNode(), "patch", "RDF Patch"); + public static final Operation Shacl = alloc(FusekiVocabG.opShacl, "SHACL", "SHACL Validation"); + public static final Operation Upload = alloc(FusekiVocabG.opUpload, "upload", "File Upload"); + public static final Operation Patch = alloc(FusekiVocabG.opPatch, "patch", "RDF Patch"); - public static final Operation NoOp = alloc(FusekiVocab.opNoOp.asNode(), "no-op", "No Op"); + public static final Operation NoOp = alloc(FusekiVocabG.opNoOp, "no-op", "No Op"); - public static final Operation PREFIXES_R = alloc(FusekiVocab.opPREFIXES_R.asNode(), "prefixes-r", "Read prefixes"); - public static final Operation PREFIXES_RW = alloc(FusekiVocab.opPREFIXES_RW.asNode(), "prefixes-rw", "Read-write prefixes"); + public static final Operation PREFIXES_R = alloc(FusekiVocabG.opPREFIXES_R, "prefixes-r", "Read prefixes"); + public static final Operation PREFIXES_RW = alloc(FusekiVocabG.opPREFIXES_RW, "prefixes-rw", "Read-write prefixes"); static { // Not everyone will remember "_" vs "-" so ... - altName(FusekiVocab.opNoOp_alt, FusekiVocab.opNoOp); - altName(FusekiVocab.opGSP_r_alt, FusekiVocab.opGSP_r); - altName(FusekiVocab.opGSP_rw_alt, FusekiVocab.opGSP_rw); + altName(FusekiVocabG.opNoOp_alt, FusekiVocabG.opNoOp); + altName(FusekiVocabG.opGSP_r_alt, FusekiVocabG.opGSP_r); + altName(FusekiVocabG.opGSP_rw_alt, FusekiVocabG.opGSP_rw); } // -- Object @@ -162,10 +159,6 @@ public class Operation { return name; } - private static void altName(Resource altName, Resource properName) { - altName(altName.asNode(), properName.asNode()); - } - private static void altName(Node altName, Node properName) { Operation op = mgr.get(properName); mgr.put(altName, op); diff --git a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiServer.java b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiServer.java index a63361f5f4..3e00e103d5 100644 --- a/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiServer.java +++ b/jena-fuseki2/jena-fuseki-main/src/main/java/org/apache/jena/fuseki/main/FusekiServer.java @@ -28,6 +28,7 @@ import java.util.*; import java.util.function.Function; import java.util.function.Predicate; + import jakarta.servlet.Filter; import jakarta.servlet.ServletContext; import jakarta.servlet.http.HttpServlet; @@ -57,14 +58,12 @@ import org.apache.jena.graph.Graph; import org.apache.jena.graph.Node; import org.apache.jena.query.Dataset; import org.apache.jena.rdf.model.*; -import org.apache.jena.shared.JenaException; import org.apache.jena.sparql.core.DatasetGraph; import org.apache.jena.sparql.core.assembler.AssemblerUtils; import org.apache.jena.sparql.util.Context; -import org.apache.jena.sparql.util.NotUniqueException; -import org.apache.jena.sparql.util.graph.GraphUtils; import org.apache.jena.sys.JenaSystem; import org.apache.jena.system.G; +import org.apache.jena.system.RDFDataException; import org.apache.jena.web.HttpSC; import org.eclipse.jetty.ee10.servlet.DefaultServlet; import org.eclipse.jetty.ee10.servlet.FilterHolder; @@ -796,19 +795,7 @@ public class FusekiServer { */ public Builder parseConfig(Model model) { requireNonNull(model, "model"); - Resource server = FusekiConfig.findServer(model); - processConfigServerLevel(server); - - // Process server and services, whether via server ja:services or, if absent, by finding by type. - - // Context is only set, not deleted, in a configuration file. - Context settings = new Context(); - List<DataAccessPoint> x = FusekiConfig.processServerConfiguration(model, settings); - - // Side effect - sets global context. - Fuseki.getContext().putAll(settings); - // Can further modify the services in the configuration file. - x.forEach(dap->addDataAccessPoint(dap)); + parseConfiguration(model.getGraph()); configModel = model; return this; } @@ -821,9 +808,31 @@ public class FusekiServer { * a text index. */ public Builder parseConfig(Graph graph) { - return parseConfig(ModelFactory.createModelForGraph(graph)); + requireNonNull(graph, "graph"); + parseConfiguration(graph); + configModel = ModelFactory.createModelForGraph(graph); + return this; } + public void parseConfiguration(Graph graph) { + requireNonNull(graph, "graph"); + + Node server = FusekiConfig.findServer(graph); + processConfigServerLevel(graph, server); + + // Process server and services, whether via server ja:services or, if absent, by finding by type. + + // Context is only set, not deleted, in a configuration file. + Context settings = new Context(); + List<DataAccessPoint> x = FusekiConfig.processServerConfiguration(graph, settings); + + // Side effect - sets global context. + Fuseki.getContext().putAll(settings); + // Can further modify the services in the configuration file. + x.forEach(dap->addDataAccessPoint(dap)); + } + + /** Add a {@link DataAccessPoint} as a builder. */ private Builder addDataAccessPoint(DataAccessPoint dap) { if ( isRegistered(dap.getName()) ) @@ -852,29 +861,37 @@ public class FusekiServer { private void processConfigServerLevel(Resource server) { if ( server == null ) return; + processConfigServerLevel(server.getModel().getGraph(), server.asNode()); + } - if ( server.hasProperty(FusekiVocab.pServerContextPath) ) - contextPath(argString(server, FusekiVocab.pServerContextPath, "/")); - enablePing(argBoolean(server, FusekiVocab.pServerPing, false)); - enableStats(argBoolean(server, FusekiVocab.pServerStats, false)); - enableMetrics(argBoolean(server, FusekiVocab.pServerMetrics, false)); - enableCompact(argBoolean(server, FusekiVocab.pServerCompact, false)); - - processConfAuthentication(server); + private void processConfigServerLevel(Graph config, Node server) { + if ( server == null ) + return; - serverAuth = FusekiConfig.allowedUsers(server); + if ( G.hasProperty(config, server, FusekiVocabG.pServerContextPath) ) + contextPath(argString(config, server, FusekiVocabG.pServerContextPath, "/")); + enablePing(argBoolean(config, server, FusekiVocabG.pServerPing, false)); + enableStats(argBoolean(config, server, FusekiVocabG.pServerStats, false)); + enableMetrics(argBoolean(config, server, FusekiVocabG.pServerMetrics, false)); + enableCompact(argBoolean(config, server, FusekiVocabG.pServerCompact, false)); + processConfAuthentication(config, server); + serverAuth = FusekiConfig.allowedUsers(config, server); } /** Process password file, auth and realm settings on the server description. **/ private void processConfAuthentication(Resource server) { - String passwdFile = GraphUtils.getAsStringValue(server, FusekiVocab.pPasswordFile); + + } + + private void processConfAuthentication(Graph config, Node server) { + String passwdFile = getAsString(config, server, FusekiVocabG.pPasswordFile); if ( passwdFile != null ) passwordFile(passwdFile); - String realmStr = GraphUtils.getAsStringValue(server, FusekiVocab.pRealm); + String realmStr = getAsString(config, server, FusekiVocabG.pRealm); if ( realmStr != null ) realm(realmStr); - String authStr = GraphUtils.getAsStringValue(server, FusekiVocab.pAuth); + String authStr = getAsString(config, server, FusekiVocabG.pAuth); if ( authStr != null ) { AuthScheme authScheme = AuthScheme.scheme(authStr); switch (authScheme) { @@ -889,39 +906,49 @@ public class FusekiServer { } } - private static boolean argBoolean(Resource r, Property p, boolean dftValue) { - try { GraphUtils.atmostOneProperty(r, p); } - catch (NotUniqueException ex) { - throw new FusekiConfigException(ex.getMessage()); - } - Statement stmt = r.getProperty(p); - if ( stmt == null ) - return dftValue; + private static boolean argBoolean(Graph graph, Node r, Node p, boolean dftValue) { try { - return stmt.getBoolean(); - } catch (JenaException ex) { - throw new FusekiConfigException("Not a boolean for '"+p+"' : "+stmt.getObject()); + Boolean bool = getAsBoolean(graph, r, p); + if ( bool == null ) + return dftValue; + return bool; + } catch (RDFDataException ex) { + throw new FusekiConfigException("Boolean argument "+r+" "+p+" "+ex.getMessage()); } } - private static String argString(Resource r, Property p, String dftValue) { - try { GraphUtils.atmostOneProperty(r, p); } - catch (NotUniqueException ex) { - throw new FusekiConfigException(ex.getMessage()); - } - Statement stmt = r.getProperty(p); - if ( stmt == null ) - return dftValue; + private static String argString(Graph graph, Node r, Node p, String dftValue) { try { - Node n = stmt.getObject().asLiteral().asNode(); - if ( ! G.isString(n) ) - throw new FusekiConfigException("Not a string for '"+p+"' : "+stmt.getObject()); - return n.getLiteralLexicalForm(); - } catch (JenaException ex) { - throw new FusekiConfigException("Not a string for '"+p+"' : "+stmt.getObject()); + String str = getAsString(graph, r, p); + if ( str == null ) + return dftValue; + return str; + } catch (RDFDataException ex) { + throw new FusekiConfigException("String argument "+r+" "+p+" "+ex.getMessage()); } } + /** URI or xsd:string as java string. **/ + private static String getAsString(Graph config, Node server, Node property) { + Node n = G.getZeroOrOneSP(config, server, property); + if ( n == null ) + return null; + if ( n.isURI() ) + return n.getURI(); + if ( G.isString(n) ) + return G.asString(n); + throw new FusekiConfigException("Not a URI or a string"); + } + + private static Boolean getAsBoolean(Graph config, Node server, Node property) { + Node n = G.getZeroOrOneSP(config, server, property); + if ( n == null ) + return null; + if ( G.isBoolean(n) ) { + return G.asBoolean(n); + } + throw new FusekiConfigException("Not a boolean"); + } /** * Choose the HTTP authentication scheme. diff --git a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/access/TestAuthorized.java b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/access/TestAuthorized.java index 4b436e5652..752e4a08c4 100644 --- a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/access/TestAuthorized.java +++ b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/access/TestAuthorized.java @@ -23,19 +23,21 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import org.junit.Test; + import org.apache.jena.atlas.logging.LogCtl; import org.apache.jena.fuseki.Fuseki; import org.apache.jena.fuseki.auth.Auth; import org.apache.jena.fuseki.auth.AuthPolicy; import org.apache.jena.fuseki.build.FusekiConfig; -import org.apache.jena.rdf.model.Model; -import org.apache.jena.rdf.model.Resource; +import org.apache.jena.graph.Graph; +import org.apache.jena.graph.Node; +import org.apache.jena.graph.NodeFactory; import org.apache.jena.riot.RDFDataMgr; -import org.junit.Test; public class TestAuthorized { - static Model model = RDFDataMgr.loadModel("testing/Access/allowedUsers.ttl"); + private static Graph allowedUsersGraph = RDFDataMgr.loadGraph("testing/Access/allowedUsers.ttl"); @Test public void auth_anon() { AuthPolicy auth = Auth.ANY_ANON; @@ -89,21 +91,25 @@ public class TestAuthorized { assertFalse(auth.isAllowed("user3")); } + private static Node node(String uri) { + return NodeFactory.createURI(uri); + } + @Test public void auth_parse_no_info_1() { - Resource r = model.createResource("http://example/notInData"); - AuthPolicy auth = FusekiConfig.allowedUsers(r); + Node r = node("<http://example/notInData>"); + AuthPolicy auth = FusekiConfig.allowedUsers(allowedUsersGraph, r); assertNull(auth); } @Test public void auth_parse_no_info_2() { - Resource r = model.createResource("http://example/none"); - AuthPolicy auth = FusekiConfig.allowedUsers(r); + Node r = node("http://example/none"); + AuthPolicy auth = FusekiConfig.allowedUsers(allowedUsersGraph, r); assertNull(auth); } @Test public void auth_parse_1() { - Resource r = model.createResource("http://example/r1"); - AuthPolicy auth = FusekiConfig.allowedUsers(r); + Node r = node("http://example/r1"); + AuthPolicy auth = FusekiConfig.allowedUsers(allowedUsersGraph, r); assertNotNull(auth); assertFalse(auth.isAllowed(null)); assertTrue(auth.isAllowed("user1")); @@ -112,8 +118,8 @@ public class TestAuthorized { } @Test public void auth_parse_2() { - Resource r = model.createResource("http://example/r2"); - AuthPolicy auth = FusekiConfig.allowedUsers(r); + Node r = node("http://example/r2"); + AuthPolicy auth = FusekiConfig.allowedUsers(allowedUsersGraph, r); assertNotNull(auth); assertFalse(auth.isAllowed(null)); assertTrue(auth.isAllowed("user1")); @@ -122,8 +128,8 @@ public class TestAuthorized { } @Test public void auth_parse_loggedIn() { - Resource r = model.createResource("http://example/rLoggedIn"); - AuthPolicy auth = FusekiConfig.allowedUsers(r); + Node r = node("http://example/rLoggedIn"); + AuthPolicy auth = FusekiConfig.allowedUsers(allowedUsersGraph, r); assertNotNull(auth); assertFalse(auth.isAllowed(null)); assertTrue(auth.isAllowed("user1")); diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/mgt/ActionDatasets.java b/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/mgt/ActionDatasets.java index ce4f319ad3..109332f8ad 100644 --- a/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/mgt/ActionDatasets.java +++ b/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/mgt/ActionDatasets.java @@ -197,8 +197,7 @@ public class ActionDatasets extends ActionContainerItem { RDFDataMgr.write(outCopy, modelData, Lang.TURTLE); } - // Need to be in Resource space at this point. - DataAccessPoint dataAccessPoint = FusekiConfig.buildDataAccessPoint(subject, registry); + DataAccessPoint dataAccessPoint = FusekiConfig.buildDataAccessPoint(subject.getModel().getGraph(), subject.asNode(), registry); if ( dataAccessPoint == null ) { FmtLog.error(action.log, "Failed to build DataAccessPoint: datasetPath = %s; DataAccessPoint name = %s", datasetPath, dataAccessPoint); ServletOps.errorBadRequest("Failed to build DataAccessPoint"); diff --git a/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/webapp/FusekiWebapp.java b/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/webapp/FusekiWebapp.java index 19ae12803b..cfe6d69731 100644 --- a/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/webapp/FusekiWebapp.java +++ b/jena-fuseki2/jena-fuseki-webapp/src/main/java/org/apache/jena/fuseki/webapp/FusekiWebapp.java @@ -37,7 +37,6 @@ import org.apache.jena.cmd.CmdException; import org.apache.jena.dboe.sys.Names; import org.apache.jena.fuseki.Fuseki; import org.apache.jena.fuseki.FusekiConfigException; -import org.apache.jena.fuseki.build.DatasetDescriptionMap; import org.apache.jena.fuseki.build.FusekiConfig; import org.apache.jena.fuseki.cmd.FusekiArgs; import org.apache.jena.fuseki.mgt.Template; @@ -282,7 +281,6 @@ public class FusekiWebapp private static DataAccessPoint configFromTemplate(String templateFile, String datasetPath, boolean allowUpdate, Map<String, String> params) { - DatasetDescriptionMap registry = new DatasetDescriptionMap(); // ---- Setup if ( params == null ) { params = new HashMap<>(); @@ -311,7 +309,7 @@ public class FusekiWebapp Lang lang = RDFLanguages.filenameToLang(templateFile, Lang.TTL); Model model = RDFParser.fromString(str, lang).base(datasetPath).toModel(); - List<DataAccessPoint> defns = FusekiConfig.servicesAndDatasets(model); + List<DataAccessPoint> defns = FusekiConfig.servicesAndDatasets(model.getGraph()); if ( defns.size() != 1 ) { if ( defns.isEmpty() ) ServletOps.errorBadRequest("No description of a Fuseki service"); diff --git a/jena-fuseki2/jena-fuseki-webapp/src/test/java/org/apache/jena/fuseki/TestBuilder.java b/jena-fuseki2/jena-fuseki-webapp/src/test/java/org/apache/jena/fuseki/TestBuilder.java index 334acb74cc..14d7a995d0 100644 --- a/jena-fuseki2/jena-fuseki-webapp/src/test/java/org/apache/jena/fuseki/TestBuilder.java +++ b/jena-fuseki2/jena-fuseki-webapp/src/test/java/org/apache/jena/fuseki/TestBuilder.java @@ -21,15 +21,16 @@ package org.apache.jena.fuseki; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; +import org.junit.Test; + import org.apache.jena.fuseki.build.DatasetDescriptionMap; import org.apache.jena.fuseki.build.FusekiConfig; -import org.apache.jena.query.Dataset; import org.apache.jena.rdf.model.Model; import org.apache.jena.rdf.model.ModelFactory; import org.apache.jena.rdf.model.Resource; +import org.apache.jena.sparql.core.DatasetGraph; import org.apache.jena.tdb1.assembler.VocabTDB1; import org.apache.jena.vocabulary.RDF; -import org.junit.Test; public class TestBuilder { @@ -49,17 +50,17 @@ public class TestBuilder { } @Test public void testVerifySameDatasetObjectForSameDescription() { - - Dataset ds1 = FusekiConfig.getDataset(dsDesc1, registry); - Dataset ds2 = FusekiConfig.getDataset(dsDesc1, registry); + DatasetDescriptionMap registry = new DatasetDescriptionMap(); + DatasetGraph ds1 = FusekiConfig.getDataset(dsDesc1.getModel().getGraph(), dsDesc1.asNode(), registry); + DatasetGraph ds2 = FusekiConfig.getDataset(dsDesc1.getModel().getGraph(), dsDesc1.asNode(), registry); assertEquals(ds1, ds2); } @Test public void testVerifyDifferentDatasetObjectsForDifferentDescriptions() { - - Dataset ds1 = FusekiConfig.getDataset(dsDesc1, registry); - Dataset ds2 = FusekiConfig.getDataset(dsDesc2, registry); + DatasetDescriptionMap registry = new DatasetDescriptionMap(); + DatasetGraph ds1 = FusekiConfig.getDataset(dsDesc1.getModel().getGraph(), dsDesc1.asNode(), registry); + DatasetGraph ds2 = FusekiConfig.getDataset(dsDesc2.getModel().getGraph(), dsDesc2.asNode(), registry); assertNotEquals(ds1, ds2); } }
