Merge pull request #745 from afs/graph-target
JENA-1897: Extract and share GSPTarget -> GraphTarget
diff --git a/jena-arq/src/main/java/org/apache/jena/riot/system/IRIResolver.java b/jena-arq/src/main/java/org/apache/jena/riot/system/IRIResolver.java
index a9cc216..3ab2625 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/system/IRIResolver.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/system/IRIResolver.java
@@ -274,10 +274,13 @@
return exceptions(globalResolver.resolve(uriStr));
}
- /*
- * No exception thrown by this method.
+ /**
+ * Resolve a string against a base.
+ * <p>
+ * No exceptions thrown by this method; the application should test the returned
+ * IRI for violations with {@link IRI#hasViolation(boolean)}.
*/
- static private IRI resolveIRI(String relStr, String baseStr) {
+ public static IRI resolveIRI(String relStr, String baseStr) {
IRI i = iriFactory().create(relStr);
if (i.isAbsolute())
// removes excess . segments
diff --git a/jena-arq/src/main/java/org/apache/jena/riot/web/HttpNames.java b/jena-arq/src/main/java/org/apache/jena/riot/web/HttpNames.java
index d340dd8..286b740 100644
--- a/jena-arq/src/main/java/org/apache/jena/riot/web/HttpNames.java
+++ b/jena-arq/src/main/java/org/apache/jena/riot/web/HttpNames.java
@@ -26,7 +26,7 @@
public static final String hAcceptEncoding = "Accept-Encoding" ;
public static final String hAcceptCharset = "Accept-Charset" ;
public static final String hAcceptRanges = "Accept-Ranges" ;
-
+
public static final String hAllow = "Allow" ;
public static final String hAuthorization = "Authorization";
public static final String hContentEncoding = "Content-Encoding" ;
@@ -41,7 +41,7 @@
public static final String hLocation = "Location" ;
public static final String hVary = "Vary" ;
public static final String charset = "charset" ;
-
+
// CORS:
// http://www.w3.org/TR/cors/ http://esw.w3.org/CORS_Enabled
public static final String hAccessControlAllowOrigin = "Access-Control-Allow-Origin" ;
@@ -53,8 +53,8 @@
public static final String hOrigin = "Origin" ;
public static final String hAccessControlRequestMethod = "Access-Control-Request-Method" ;
public static final String hAccessControlRequestHeaders = "Access-Control-Request-Headers" ;
-
- // Fuseki parameter names
+
+ // GSP parameter names
public static final String paramGraph = "graph" ;
public static final String paramGraphDefault = "default" ;
@@ -62,7 +62,7 @@
public static final String paramQueryRef = "query-ref" ;
public static final String paramDefaultGraphURI = "default-graph-uri" ;
public static final String paramNamedGraphURI = "named-graph-uri" ;
-
+
public static final String paramStyleSheet = "stylesheet" ;
public static final String paramAccept = "accept" ;
public static final String paramOutput1 = "output" ; // See Yahoo! developer: http://developer.yahoo.net/common/json.html
@@ -87,6 +87,8 @@
public static final String HEADER_IFMODSINCE = "If-Modified-Since";
public static final String HEADER_LASTMOD = "Last-Modified";
-
- public static final String valueDefault = "default" ;
+
+ // Special names for GSP targets (use in ?graph=)
+ public static final String graphTargetDefault = "default" ;
+ public static final String graphTargetUnion = "union" ;
}
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/GSPLib.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/GSPLib.java
index 0c1ac15..b8c45d6 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/GSPLib.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/GSPLib.java
@@ -18,9 +18,39 @@
package org.apache.jena.fuseki.servlets;
+import java.util.Map;
+
import javax.servlet.http.HttpServletRequest;
+import org.apache.jena.riot.web.HttpNames;
+
public class GSPLib {
+
+ /** Test whether the operation has either of the GSP parameters. */
+ public static boolean hasGSPParams(HttpAction action) {
+ if ( action.request.getQueryString() == null )
+ return false;
+ boolean hasParamGraphDefault = action.request.getParameter(HttpNames.paramGraphDefault) != null;
+ if ( hasParamGraphDefault )
+ return true;
+ boolean hasParamGraph = action.request.getParameter(HttpNames.paramGraph) != null;
+ if ( hasParamGraph )
+ return true;
+ return false;
+ }
+
+ /** Test whether the operation has exactly one GSP parameter and no other parameters. */
+ public static boolean hasGSPParamsStrict(HttpAction action) {
+ if ( action.request.getQueryString() == null )
+ return false;
+ Map<String, String[]> params = action.request.getParameterMap();
+ if ( params.size() != 1 )
+ return false;
+ boolean hasParamGraphDefault = GSPLib.hasExactlyOneValue(action, HttpNames.paramGraphDefault);
+ boolean hasParamGraph = GSPLib.hasExactlyOneValue(action, HttpNames.paramGraph);
+ // Java XOR
+ return hasParamGraph ^ hasParamGraphDefault;
+ }
/** Check whether there is exactly one HTTP header value */
public static boolean hasExactlyOneValue(HttpAction action, String name) {
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/GSPTarget.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/GSPTarget.java
deleted file mode 100644
index df30e50..0000000
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/GSPTarget.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.jena.fuseki.servlets;
-
-import org.apache.jena.graph.Graph;
-import org.apache.jena.graph.Node;
-import org.apache.jena.sparql.core.DatasetGraph;
-
-/** Target of GSP operations */
-final class GSPTarget {
- final boolean isDefault;
- final DatasetGraph dsg;
- private Graph _graph;
- final String name;
- final Node graphName;
-
- static GSPTarget createNamed(DatasetGraph dsg, String name, Node graphName) {
- return new GSPTarget(false, dsg, name, graphName);
- }
-
- static GSPTarget createDefault(DatasetGraph dsg) {
- return new GSPTarget(true, dsg, null, null);
- }
-
- /**
- * Create a new Target which is like the original but aimed at a different
- * DatasetGraph
- */
- static GSPTarget retarget(GSPTarget target, DatasetGraph dsg) {
- GSPTarget target2 = new GSPTarget(target, dsg);
- target2._graph = null;
- return target2;
- }
-
- private GSPTarget(boolean isDefault, DatasetGraph dsg, String name, Node graphName) {
- this.isDefault = isDefault;
- this.dsg = dsg;
- this._graph = null;
- this.name = name;
- this.graphName = graphName;
-
- if ( isDefault ) {
- if ( name != null || graphName != null )
- throw new IllegalArgumentException("Inconsistent: default and a graph name/node");
- } else {
- if ( name == null || graphName == null )
- throw new IllegalArgumentException("Inconsistent: not default and/or no graph name/node");
- }
- }
-
- private GSPTarget(GSPTarget other, DatasetGraph dsg) {
- this.isDefault = other.isDefault;
- this.dsg = dsg; // other.dsg;
- this._graph = other._graph;
- this.name = other.name;
- this.graphName = other.graphName;
- }
-
- /**
- * Get a graph for the action - this may create a graph in the dataset - this is
- * not a test for graph existence
- */
- public Graph graph() {
- if ( !isGraphSet() ) {
- if ( isDefault )
- _graph = dsg.getDefaultGraph();
- else
- _graph = dsg.getGraph(graphName);
- }
- return _graph;
- }
-
- public boolean exists() {
- if ( isDefault )
- return true;
- return dsg.containsGraph(graphName);
- }
-
- public boolean isGraphSet() {
- return _graph != null;
- }
-
- @Override
- public String toString() {
- if ( isDefault )
- return "default";
- return name;
- }
-}
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/GSP_Base.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/GSP_Base.java
index f2118cf..8b4b916 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/GSP_Base.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/GSP_Base.java
@@ -22,13 +22,7 @@
import javax.servlet.http.HttpServletRequest;
-import org.apache.jena.fuseki.Fuseki;
-import org.apache.jena.graph.Node;
-import org.apache.jena.graph.NodeFactory;
-import org.apache.jena.riot.RiotException;
-import org.apache.jena.riot.system.IRIResolver;
import org.apache.jena.riot.web.HttpNames;
-import org.apache.jena.sparql.core.DatasetGraph;
public abstract class GSP_Base extends ActionREST {
@@ -85,58 +79,4 @@
ServletOps.errorBadRequest("Multiple parameters '" + h + "'");
}
}
-
- protected final static GSPTarget determineTarget(DatasetGraph dsg, HttpAction action) {
- // Inside a transaction.
- if ( dsg == null )
- ServletOps.errorOccurred("Internal error : No action graph (not in a transaction?)");
-// if ( ! dsg.isInTransaction() )
-// ServletOps.errorOccurred("Internal error : No transaction");
-
- boolean dftGraph = GSPLib.getOneOnly(action.request, HttpNames.paramGraphDefault) != null;
- String uri = GSPLib.getOneOnly(action.request, HttpNames.paramGraph);
-
- if ( !dftGraph && uri == null ) {
- // No params - direct naming.
- if ( !Fuseki.GSP_DIRECT_NAMING )
- ServletOps.errorBadRequest("Neither default graph nor named graph specified");
-
- // Direct naming.
- String directName = action.request.getRequestURL().toString();
- if ( action.request.getRequestURI().equals(action.getDatasetName()) )
- // No name (should have been a quads operations).
- ServletOps.errorBadRequest("Neither default graph nor named graph specified and no direct name");
- Node gn = NodeFactory.createURI(directName);
- return namedTarget(dsg, directName);
- }
-
- if ( dftGraph )
- return GSPTarget.createDefault(dsg);
-
- // Named graph
- if ( uri.equals(HttpNames.valueDefault) )
- // But "named" default
- return GSPTarget.createDefault(dsg);
-
- // Strictly, a bit naughty on the URI resolution. But more sensible.
- // Base is dataset.
-
- String base = action.request.getRequestURL().toString(); // wholeRequestURL(request);
- // Make sure it ends in "/", ie. dataset as container.
- if ( action.request.getQueryString() != null && !base.endsWith("/") )
- base = base + "/";
- String absUri = null;
- try {
- absUri = IRIResolver.resolveString(uri, base);
- } catch (RiotException ex) {
- // Bad IRI
- ServletOps.errorBadRequest("Bad IRI: " + ex.getMessage());
- }
- return namedTarget(dsg, absUri);
- }
-
- private static GSPTarget namedTarget(DatasetGraph dsg, String graphName) {
- Node gn = NodeFactory.createURI(graphName);
- return GSPTarget.createNamed(dsg, graphName, gn);
- }
}
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/GSP_R.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/GSP_R.java
index 606ab0f..7d0d2a0 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/GSP_R.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/GSP_R.java
@@ -19,6 +19,7 @@
package org.apache.jena.fuseki.servlets;
import static java.lang.String.format;
+import static org.apache.jena.fuseki.servlets.GraphTarget.determineTargetGSP;
import java.io.IOException;
@@ -102,15 +103,13 @@
action.id, mediaType.getContentType(), mediaType.getCharset(), lang.getName()));
try {
DatasetGraph dsg = decideDataset(action);
- GSPTarget target = determineTarget(dsg, action);
+ GraphTarget target = determineTargetGSP(dsg, action);
if ( action.log.isDebugEnabled() )
action.log.debug("GET->"+target);
boolean exists = target.exists();
if ( ! exists )
- ServletOps.errorNotFound("No such graph: <"+target.name+">");
+ ServletOps.errorNotFound("No such graph: "+target.label());
Graph g = target.graph();
- if ( ! target.isDefault && g.isEmpty() )
- ServletOps.errorNotFound("No such graph: <"+target.name+">");
// If we want to set the Content-Length, we need to buffer.
//response.setContentLength(??);
String ct = lang.getContentType().toHeaderString();
@@ -165,12 +164,12 @@
action.beginRead();
try {
DatasetGraph dsg = decideDataset(action);
- GSPTarget target = determineTarget(dsg, action);
+ GraphTarget target = determineTargetGSP(dsg, action);
if ( action.log.isDebugEnabled() )
action.log.debug("HEAD->"+target);
boolean exists = target.exists();
if ( ! exists )
- ServletOps.errorNotFound("No such graph: <"+target.name+">");
+ ServletOps.errorNotFound("No such graph: "+target.label());
ServletOps.success(action);
} finally { action.endRead(); }
}
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/GSP_RW.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/GSP_RW.java
index 48ac2d2..b96b4f5 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/GSP_RW.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/GSP_RW.java
@@ -18,12 +18,10 @@
package org.apache.jena.fuseki.servlets;
+import static org.apache.jena.fuseki.servlets.GraphTarget.determineTargetGSP;
import static org.apache.jena.riot.WebContent.ctMultipartMixed;
import static org.apache.jena.riot.WebContent.matchContentType;
-import java.util.Map;
-import java.util.function.Supplier;
-
import org.apache.jena.atlas.web.ContentType;
import org.apache.jena.fuseki.system.FusekiNetLib;
import org.apache.jena.fuseki.system.Upload;
@@ -46,7 +44,7 @@
@Override
protected void doOptions(HttpAction action) {
ActionLib.setCommonHeadersForOptions(action.response);
- if ( hasGSPParams(action) )
+ if ( GSPLib.hasGSPParams(action) )
action.response.setHeader(HttpNames.hAllow, "GET,HEAD,OPTIONS,PUT,DELETE,POST");
else
action.response.setHeader(HttpNames.hAllow, "GET,HEAD,OPTIONS,PUT,POST");
@@ -85,23 +83,22 @@
protected void execPutQuads(HttpAction action) { doPutPostQuads(action, true); }
- protected void execDelete(Supplier<DatasetGraph> dataset, HttpAction action) {
- }
-
- public void execDeleteGSP(HttpAction action) {
+ protected void execDeleteGSP(HttpAction action) {
action.beginWrite();
boolean haveCommited = false;
try {
DatasetGraph dsg = decideDataset(action);
- GSPTarget target = determineTarget(dsg, action);
+ GraphTarget target = determineTargetGSP(dsg, action);
if ( action.log.isDebugEnabled() )
action.log.debug("DELETE->"+target);
+ if ( target.isUnion() )
+ ServletOps.errorBadRequest("Can't delete the union graph");
boolean existedBefore = target.exists();
if ( !existedBefore ) {
// Commit, not abort, because locking "transactions" don't support abort.
action.commit();
haveCommited = true;
- ServletOps.errorNotFound("No such graph: "+target.name);
+ ServletOps.errorNotFound("No such graph: "+target.label());
}
deleteGraph(dsg, action);
action.commit();
@@ -118,32 +115,6 @@
ServletOps.errorMethodNotAllowed("DELETE");
}
- /** Test whether the operation has either of the GSP parameters. */
- public static boolean hasGSPParams(HttpAction action) {
- if ( action.request.getQueryString() == null )
- return false;
- boolean hasParamGraphDefault = action.request.getParameter(HttpNames.paramGraphDefault) != null;
- if ( hasParamGraphDefault )
- return true;
- boolean hasParamGraph = action.request.getParameter(HttpNames.paramGraph) != null;
- if ( hasParamGraph )
- return true;
- return false;
- }
-
- /** Test whether the operation has exactly one GSP parameter and no other parameters. */
- public static boolean hasGSPParamsStrict(HttpAction action) {
- if ( action.request.getQueryString() == null )
- return false;
- Map<String, String[]> params = action.request.getParameterMap();
- if ( params.size() != 1 )
- return false;
- boolean hasParamGraphDefault = GSPLib.hasExactlyOneValue(action, HttpNames.paramGraphDefault);
- boolean hasParamGraph = GSPLib.hasExactlyOneValue(action, HttpNames.paramGraph);
- // Java XOR
- return hasParamGraph ^ hasParamGraphDefault;
- }
-
protected void doPutPostGSP(HttpAction action, boolean overwrite) {
ContentType ct = ActionLib.getContentType(action);
if ( ct == null )
@@ -177,9 +148,11 @@
action.beginWrite();
try {
DatasetGraph dsg = decideDataset(action);
- GSPTarget target = determineTarget(dsg, action);
+ GraphTarget target = determineTargetGSP(dsg, action);
if ( action.log.isDebugEnabled() )
action.log.debug(action.request.getMethod().toUpperCase()+"->"+target);
+ if ( target.isUnion() )
+ ServletOps.errorBadRequest("Can't delete the union graph");
boolean existedBefore = target.exists();
Graph g = target.graph();
if ( overwrite && existedBefore )
@@ -234,13 +207,15 @@
action.beginWrite();
try {
DatasetGraph dsg = decideDataset(action);
- GSPTarget target = determineTarget(dsg, action);
+ GraphTarget target = determineTargetGSP(dsg, action);
if ( action.log.isDebugEnabled() )
action.log.debug(" ->"+target);
+ if ( target.isUnion() )
+ ServletOps.errorBadRequest("Can't delete the union graph");
boolean existedBefore = target.exists();
if ( overwrite && existedBefore )
clearGraph(target);
- FusekiNetLib.addDataInto(graphTmp, target.dsg, target.graphName);
+ FusekiNetLib.addDataInto(graphTmp, target.dataset(), target.graphName());
details.setExistedBefore(existedBefore);
action.commit();
return details;
@@ -259,15 +234,15 @@
* The default graph is cleared, not removed.
*/
protected static void deleteGraph(DatasetGraph dsg, HttpAction action) {
- GSPTarget target = determineTarget(dsg, action);
- if ( target.isDefault )
+ GraphTarget target = determineTargetGSP(dsg, action);
+ if ( target.isDefault() )
clearGraph(target);
else
- dsg.removeGraph(target.graphName);
+ target.dataset().removeGraph(target.graphName());
}
/** Clear a graph - this leaves the storage choice and setup in-place */
- protected static void clearGraph(GSPTarget target) {
+ protected static void clearGraph(GraphTarget target) {
Graph g = target.graph();
g.getPrefixMapping().clearNsPrefixMap();
g.clear();
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/GraphTarget.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/GraphTarget.java
new file mode 100644
index 0000000..4010bf6
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/GraphTarget.java
@@ -0,0 +1,226 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jena.fuseki.servlets;
+
+import static java.lang.String.format;
+
+import org.apache.jena.atlas.logging.FmtLog;
+import org.apache.jena.fuseki.Fuseki;
+import org.apache.jena.fuseki.server.Endpoint;
+import org.apache.jena.graph.Graph;
+import org.apache.jena.graph.Node;
+import org.apache.jena.graph.NodeFactory;
+import org.apache.jena.iri.IRI;
+import org.apache.jena.riot.RiotException;
+import org.apache.jena.riot.out.NodeFmtLib;
+import org.apache.jena.riot.system.IRIResolver;
+import org.apache.jena.riot.web.HttpNames;
+import org.apache.jena.sparql.core.DatasetGraph;
+
+/**
+ * Target of GSP operations.<br/>
+ * Extensions: "?graph=union" and "?graph=default"
+ *
+ */
+public class GraphTarget {
+
+ public final static GraphTarget determineTarget(DatasetGraph dsg, HttpAction action) {
+ return determineTarget(dsg, action, false);
+ }
+
+ /** With GSP direct naming. */
+ public final static GraphTarget determineTargetGSP(DatasetGraph dsg, HttpAction action) {
+ return determineTarget(dsg, action, Fuseki.GSP_DIRECT_NAMING);
+ }
+
+ private final static GraphTarget determineTarget(DatasetGraph dsg, HttpAction action, boolean allowDirectNaming) {
+ // Inside a transaction.
+ if ( dsg == null )
+ ServletOps.errorOccurred("Internal error : No action graph (not in a transaction?)");
+// if ( ! dsg.isInTransaction() )
+// ServletOps.errorOccurred("Internal error : No transaction");
+
+ boolean dftGraph = GSPLib.getOneOnly(action.request, HttpNames.paramGraphDefault) != null;
+ String uri = GSPLib.getOneOnly(action.request, HttpNames.paramGraph);
+
+ if ( !dftGraph && uri == null ) {
+ // No params - direct naming?
+ if ( ! allowDirectNaming )
+ ServletOps.errorBadRequest("Neither default graph nor named graph specified");
+
+ // Direct naming.
+ String directName = action.request.getRequestURL().toString();
+ if ( action.request.getRequestURI().equals(action.getDatasetName()) )
+ // No name (should have been a quads operations).
+ ServletOps.errorBadRequest("Neither default graph nor named graph specified and no direct name");
+ Node gn = NodeFactory.createURI(directName);
+ return createNamed(dsg, gn);
+ }
+
+ if ( dftGraph )
+ return createDefault(dsg);
+ // Named graph as default
+ if ( uri.equals(HttpNames.graphTargetDefault) )
+ // But "named" default
+ return createDefault(dsg);
+ // Named graph - union
+ if ( uri.equals(HttpNames.graphTargetUnion) )
+ return createUnion(dsg);
+
+ String absUri = resolve0(uri, action);
+ return createNamed(dsg, absUri);
+ }
+
+
+ // Resolving a relative URI in ?graph= is a bit murky.
+ // Whether to use the base is the dataset or the dataset+service endpoint name.
+ // How to find the dataset URL when service can be on the dataset directly or a named endpoint.
+ // And will it match in the dataset named graphs anyway?
+
+ /** Check URI, require it to be absolute. */
+ private static String resolve0(String uri, HttpAction action) {
+ IRI iri = IRIResolver.parseIRI(uri);
+ if ( iri.hasViolation(false) ) {
+ action.log.warn(format("[%d] Bad URI <%s> : %s",
+ action.id, uri, iri.violations(false).next().getShortMessage()));
+ }
+ if ( ! iri.isAbsolute() ) {
+ action.log.warn(format("[%d] URI is not abolute: <%s>", action.id, uri));
+ }
+ return uri;
+ }
+
+ /** Resolve URI, Calculate a base URI as the dataset URI and resolve uri against that.*/
+ private static String resolve(String uri, HttpAction action) {
+ // Strictly, a bit naughty on the URI resolution, but more sensible.
+ // Make the base the URI of the dataset.
+ // Strictly, the base includes service and query string but that is unhelpful.
+ // wholeRequestURL(request);
+ String base = action.request.getRequestURL().toString();
+ Endpoint ep = action.getEndpoint();
+ if ( ! ep.isUnnamed() && base.endsWith(ep.getName()) ) {
+ // Remove endpoint name
+ base = base.substring(0, base.length()-ep.getName().length());
+ }
+ // Make sure it ends in "/", treating the dataset as a container.
+ if ( !base.endsWith("/") )
+ base = base + "/";
+ try {
+ IRI abs = IRIResolver.resolveIRI(uri, base);
+ if ( abs.hasViolation(false) ) {
+ FmtLog.warn(Fuseki.actionLog, "Bad URI: '"+uri+"' : "+abs.violations(false).next().getShortMessage());
+ }
+ return abs.toString();
+ } catch (RiotException ex) {
+ // Bad IRI
+ ServletOps.errorBadRequest("Bad IRI: " + ex.getMessage());
+ return null;
+ }
+ }
+
+ final private boolean isDefault;
+ final private boolean isUnion;
+ final private DatasetGraph dsg;
+ final private Node graphName;
+
+ static GraphTarget createNamed(DatasetGraph dsg, String graphName) {
+ return createNamed(dsg, NodeFactory.createURI(graphName));
+ }
+
+ static GraphTarget createNamed(DatasetGraph dsg, Node graphName) {
+ return new GraphTarget(false, false, dsg, graphName);
+ }
+
+ static GraphTarget createDefault(DatasetGraph dsg) {
+ return new GraphTarget(true, false, dsg, null);
+ }
+
+ static GraphTarget createUnion(DatasetGraph dsg) {
+ return new GraphTarget(false, true, dsg, null);
+ }
+
+ /**
+ * Create a new GraphTarget which is like the original but aimed at a different
+ * DatasetGraph
+ */
+ static GraphTarget retarget(GraphTarget target, DatasetGraph dsg) {
+ GraphTarget target2 = new GraphTarget(target, dsg);
+ return target2;
+ }
+
+ private GraphTarget(boolean isDefault, boolean isUnion, DatasetGraph dsg, Node graphName) {
+ if ( ! isUnion && !isDefault && graphName == null )
+ throw new IllegalArgumentException("Inconsistent: not default, union nor graph name");
+ else if ( isDefault && graphName != null )
+ throw new IllegalArgumentException("Inconsistent: default and a graph name");
+ else if ( isUnion && graphName != null )
+ throw new IllegalArgumentException("Inconsistent: union and a graph name");
+ else if ( isDefault && isUnion )
+ throw new IllegalArgumentException("Inconsistent: default and union graph");
+
+ this.isDefault = isDefault;
+ this.isUnion = isUnion;
+ this.dsg = dsg;
+ this.graphName = graphName;
+ }
+
+ private GraphTarget(GraphTarget other, DatasetGraph dsg) {
+ this.dsg = dsg; // other.dsg; // Retarget
+ this.isDefault = other.isDefault;
+ this.isUnion = other.isUnion;
+ this.graphName = other.graphName;
+ }
+
+ public DatasetGraph dataset() { return dsg; }
+ public boolean isDefault() { return isDefault; }
+ public boolean isUnion() { return isUnion; }
+ public Node graphName() { return graphName; }
+
+ /**
+ * Get a graph for the action - this is not a test for graph existence.
+ * May return null or an empty graph.
+ */
+ public Graph graph() {
+ if ( isDefault )
+ return dsg.getDefaultGraph();
+ if ( isUnion )
+ return dsg.getUnionGraph();
+ return dsg.getGraph(graphName);
+ }
+
+ public boolean exists() {
+ if ( isDefault || isUnion )
+ return true;
+ Graph g = graph();
+ return g != null && ! g.isEmpty();
+ }
+
+ public String label() {
+ if ( isDefault )
+ return "default";
+ if ( isUnion )
+ return "union";
+ return NodeFmtLib.str(graphName);
+ }
+
+ @Override
+ public String toString() {
+ return "target:"+label();
+ }
+}
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SHACL_Validation.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SHACL_Validation.java
index d22f40a..3fa1a2e 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SHACL_Validation.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SHACL_Validation.java
@@ -19,20 +19,16 @@
package org.apache.jena.fuseki.servlets;
import static java.lang.String.format;
-import static org.apache.jena.fuseki.servlets.ActionLib.getOneHeader;
+import static org.apache.jena.fuseki.servlets.GraphTarget.determineTarget;
import org.apache.jena.atlas.web.MediaType;
import org.apache.jena.fuseki.DEF;
import org.apache.jena.graph.Graph;
-import org.apache.jena.graph.Node;
-import org.apache.jena.graph.NodeFactory;
import org.apache.jena.riot.Lang;
import org.apache.jena.riot.RDFLanguages;
-import org.apache.jena.riot.web.HttpNames;
import org.apache.jena.shacl.ShaclValidator;
import org.apache.jena.shacl.Shapes;
import org.apache.jena.shacl.ValidationReport;
-import org.apache.jena.sparql.core.DatasetGraph;
import org.apache.jena.web.HttpSC;
/**
@@ -55,7 +51,10 @@
action.beginRead();
try {
- Graph data = determineTarget(action.getActiveDSG(), action);
+ GraphTarget target = determineTarget(action.getActiveDSG(), action);
+ if ( ! target.exists() )
+ ServletOps.errorNotFound("No data graph: "+target.label());
+ Graph data = target.graph();
Graph shapesGraph = ActionLib.readFromRequest(action, Lang.TTL);
Shapes shapes = Shapes.parse(shapesGraph);
ValidationReport report = ShaclValidator.get().validate(shapesGraph, data);
@@ -71,23 +70,4 @@
action.endRead();
}
}
-
- protected final static Graph determineTarget(DatasetGraph dsg, HttpAction action) {
- boolean dftGraph = getOneHeader(action.request, HttpNames.paramGraphDefault) != null ;
- String graphName = getOneHeader(action.request, HttpNames.paramGraph) ;
- if ( dftGraph && graphName != null )
- ServletOps.errorBadRequest("Both default graph and named graph specified") ;
- if ( dftGraph )
- graphName = HttpNames.valueDefault;
- if ( graphName == null )
- graphName = HttpNames.valueDefault;
- // ?graph=
- if ( graphName.equals(HttpNames.valueDefault ) )
- return dsg.getDefaultGraph();
- if ( graphName.equals("union") )
- return dsg.getUnionGraph();
- Node gn = NodeFactory.createURI(graphName);
- return dsg.getGraph(gn);
- }
-
}
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Upload.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Upload.java
index 62b2f39..b2358e7 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Upload.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/SPARQL_Upload.java
@@ -128,7 +128,7 @@
Node gn = null;
if ( graphName != null ) {
- gn = graphName.equals(HttpNames.valueDefault)
+ gn = graphName.equals(HttpNames.graphTargetDefault)
? Quad.defaultGraphNodeGenerated
: NodeFactory.createURI(graphName);
}
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/system/Upload.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/system/Upload.java
index 8e7878c..ae619fd 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/system/Upload.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/system/Upload.java
@@ -206,7 +206,7 @@
String value = Streams.asString(input, "UTF-8");
if ( fieldName.equals(HttpNames.paramGraph) ) {
graphName = value;
- if ( graphName != null && !graphName.equals("") && !graphName.equals(HttpNames.valueDefault) ) {
+ if ( graphName != null && !graphName.equals("") && !graphName.equals(HttpNames.graphTargetDefault) ) {
// -- Check IRI with additional checks.
IRI iri = IRIResolver.parseIRI(value);
if ( iri.hasViolation(false) )
@@ -272,7 +272,7 @@
}
if ( graphName == null || graphName.equals("") )
- graphName = HttpNames.valueDefault;
+ graphName = HttpNames.graphTargetDefault;
if ( isQuads )
graphName = null;
return new UploadDetailsWithName(graphName, dsgTmp, count);
diff --git a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestFusekiShaclValidation.java b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestFusekiShaclValidation.java
index 9ea2b44..cc73682 100644
--- a/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestFusekiShaclValidation.java
+++ b/jena-fuseki2/jena-fuseki-main/src/test/java/org/apache/jena/fuseki/main/TestFusekiShaclValidation.java
@@ -75,14 +75,9 @@
public void shacl_default_graph() {
try ( RDFConnection conn = RDFConnectionFactory.connect(serverURL+"/ds")) {
conn.put(DIR+"data1.ttl");
-
ValidationReport report = validateReport(serverURL+"/ds/shacl?graph=default", DIR+"shapes1.ttl");
assertNotNull(report);
assertEquals(2, report.getEntries().size());
-
- ValidationReport report2 = validateReport(serverURL+"/ds/shacl?graph=urn:x:noGraph", DIR+"shapes1.ttl");
- assertNotNull(report);
- assertEquals(0, report2.getEntries().size());
conn.update("CLEAR ALL");
}
}
@@ -91,10 +86,13 @@
public void shacl_no_data_graph() {
try ( RDFConnection conn = RDFConnectionFactory.connect(serverURL+"/ds")) {
conn.put(DIR+"data1.ttl");
- ValidationReport report = validateReport(serverURL+"/ds/shacl?graph=urn:x:noGraph", DIR+"shapes1.ttl");
- assertNotNull(report);
- assertEquals(0, report.getEntries().size());
- conn.update("CLEAR ALL");
+ try {
+ FusekiTestLib.expect404(()->{
+ ValidationReport report = validateReport(serverURL+"/ds/shacl?graph=urn:abc:noGraph", DIR+"shapes1.ttl");
+ });
+ } finally {
+ conn.update("CLEAR ALL");
+ }
}
}
diff --git a/jena-fuseki2/jena-fuseki-webapp/src/test/java/org/apache/jena/fuseki/TestAdmin.java b/jena-fuseki2/jena-fuseki-webapp/src/test/java/org/apache/jena/fuseki/TestAdmin.java
index 260a103..5b73ba3 100644
--- a/jena-fuseki2/jena-fuseki-webapp/src/test/java/org/apache/jena/fuseki/TestAdmin.java
+++ b/jena-fuseki2/jena-fuseki-webapp/src/test/java/org/apache/jena/fuseki/TestAdmin.java
@@ -535,13 +535,13 @@
*/
private static boolean waitForTasksToFinish(int pauseMillis, int maxWaitMillis) {
// Wait for them to finish.
- // Divide into
+ // Divide into chunks
if ( pauseMillis > 0 )
Lib.sleep(pauseMillis);
int waited = 0;
final int INTERVALS = 10;
for (int i = 0 ; i < INTERVALS ; i++ ) {
- System.err.println("Wait: "+i);
+ //System.err.println("Wait: "+i);
List<String> x = runningTasks();
if ( x.isEmpty() )
return true;