You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by an...@apache.org on 2022/02/16 08:47:07 UTC
[jena] branch main updated: JENA-2281: Routing all request forms
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
The following commit(s) were added to refs/heads/main by this push:
new 039d26c JENA-2281: Routing all request forms
new 1b2b9fa Merge pull request #1198 from afs/dispatch
039d26c is described below
commit 039d26ce6cc198fd4b5c9b5dd9cf115cfeebd3d8
Author: Andy Seaborne <an...@apache.org>
AuthorDate: Tue Feb 15 15:59:09 2022 +0000
JENA-2281: Routing all request forms
---
.../fuseki/server/DataAccessPointRegistry.java | 6 +-
.../org/apache/jena/fuseki/server/Dispatcher.java | 70 +++++++--
.../apache/jena/fuseki/servlets/ActionExecLib.java | 174 +++++++++++----------
.../org/apache/jena/fuseki/servlets/ActionLib.java | 49 ++----
.../org/apache/jena/fuseki/servlets/UploadRDF.java | 10 ++
.../java/org/apache/jena/fuseki/TS_FusekiCore.java | 4 +-
.../jena/fuseki/server/TestDispatchOnURI.java | 137 ++++++++++++++++
7 files changed, 320 insertions(+), 130 deletions(-)
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/DataAccessPointRegistry.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/DataAccessPointRegistry.java
index 60df646..b50b3fc 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/DataAccessPointRegistry.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/DataAccessPointRegistry.java
@@ -36,7 +36,11 @@ import org.apache.jena.fuseki.metrics.FusekiRequestsMetrics;
*/
public class DataAccessPointRegistry extends Registry<String, DataAccessPoint>
{
- private MeterRegistry meterRegistry;
+ private final MeterRegistry meterRegistry;
+
+ public DataAccessPointRegistry() {
+ this.meterRegistry = null;
+ }
public DataAccessPointRegistry(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/Dispatcher.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/Dispatcher.java
index d338f84..98a44c0 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/Dispatcher.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/server/Dispatcher.java
@@ -73,27 +73,73 @@ public class Dispatcher {
* This function does not throw exceptions.
*/
public static boolean dispatch(HttpServletRequest request, HttpServletResponse response) {
+ DataAccessPointRegistry registry = DataAccessPointRegistry.get(request.getServletContext());
+ DataAccessPoint dap = locateDataAccessPoint(request, registry);
+ if ( dap == null ) {
+ if ( LogDispatch )
+ LOG.debug("No dispatch for '"+request.getRequestURI()+"'");
+ return false;
+ }
+ return process(dap, request, response);
+ }
+
+ /**
+ * The request may be /path/dataset/sparql or /path/dataset, or even /.
+ * <p>
+ * If the servlet context path is the "/path", then that was removed in ActionLib.actionURI.
+ * But the dataset name may have a path within the servlet context.
+ * <p>
+ * The second form looks like dataset="path" and service="dataset"
+ * We don't know the service until we find the DataAccessPoint.
+ * For /dataset/sparql or /dataset, there is not problem. The latter is too short to be a named service.
+ * <p>
+ * This function chooses the DataAccessPoint.
+ * There may not be an endpoint and operation to handle the request.
+ */
+ private static DataAccessPoint locateDataAccessPoint(HttpServletRequest request, DataAccessPointRegistry registry) {
// Path component of the URI, without context path
String uri = ActionLib.actionURI(request);
- String datasetUri = ActionLib.mapRequestToDataset(uri);
-
if ( LogDispatch ) {
LOG.info("Filter: Request URI = " + request.getRequestURI());
LOG.info("Filter: Action URI = " + uri);
- LOG.info("Filter: Dataset URI = " + datasetUri);
}
+ DataAccessPoint dap = locateDataAccessPoint(uri, registry);
+ // At this point, we are going to dispatch to the DataAccessPoint.
+ // It still may not have a handler for the service on this dataset.
+ // See #chooseProcessor(HttpAction) for locating the endpoint.
+ return dap;
+ }
+
+ /*package:testing*/ static DataAccessPoint locateDataAccessPoint(String uri, DataAccessPointRegistry registry) {
+ // Direct match.
+ if ( registry.isRegistered(uri) )
+ // Cases: /, /dataset and /path/dataset where /path is not the servlet context path.
+ return registry.get(uri);
+ // Remove possible service endpoint name.
+ String datasetUri = removeFinalComponent(uri);
+
+ // Requests should at least have "/".
if ( datasetUri == null )
- return false;
+ return null;
- DataAccessPointRegistry registry = DataAccessPointRegistry.get(request.getServletContext());
- if ( !registry.isRegistered(datasetUri) ) {
- if ( LogDispatch )
- LOG.debug("No dispatch for '"+datasetUri+"'");
- return false;
+ if ( registry.isRegistered(datasetUri) )
+ // Cases: /dataset/sparql and /path/dataset/sparql
+ return registry.get(datasetUri);
+
+ return null;
+ }
+
+ /** Remove the final component of a path - return a valid URI path. */
+ private static String removeFinalComponent(String uri) {
+ int i = uri.lastIndexOf('/');
+ if ( i == -1 )
+ return null;
+ if ( i == 0 ) {
+ // /pathComponent - return a valid URI path.
+ return "/";
}
- DataAccessPoint dap = registry.get(datasetUri);
- return process(dap, request, response);
+ return uri.substring(0, i);
}
/**
@@ -121,7 +167,7 @@ public class Dispatcher {
/**
* Find the ActionProcessor or return null if there can't determine one.
*
- * This function sends the appropriate HTTP error response.
+ * This function sends the appropriate HTTP error response on failure to choose an endpoint.
*
* Returning null indicates an HTTP error response, and the HTTP response has been done.
*
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionExecLib.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionExecLib.java
index 0f86022..d590317 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionExecLib.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionExecLib.java
@@ -73,97 +73,30 @@ public class ActionExecLib {
* <li>completion/error statistics,</li>
* <li>{@link #finishRequest(HttpAction)}
* </ul>
- * Common process for handling HTTP requests with logging and Java error handling.
+ * Common process for handling HTTP requests with logging and Java error
+ * handling. This is the case where the ActionProcessor is defined by or is the
+ * servlet directly outside the Fuseki dispatch process ({@link ServletAction}
+ * for special case like {@link SPARQL_QueryGeneral} which directly holds the {@link ActionProcessor}
+ * and {@link ServletProcessor} for administration actions.
+ * <p>
+ * Return false if the ActionProcessor is null.
+ *
* @param action
* @param processor
*/
- public static boolean execAction(HttpAction action, ActionProcessor processor) {
+ public static void execAction(HttpAction action, ActionProcessor processor) {
boolean b = execAction(action, ()->processor);
if ( !b )
ServletOps.errorNotFound("Not found: "+action.getActionURI());
- return true;
}
- /** execAction, allowing for a choice of {@link ActionProcessor} within the logging and error handling. */
+ /**
+ * execAction, allowing for a choice of {@link ActionProcessor} within the logging and error handling.
+ * Return false if there was no ActionProcessor to handle the action.
+ */
public static boolean execAction(HttpAction action, Supplier<ActionProcessor> processor) {
try {
- logRequest(action);
- action.setStartTime();
- initResponse(action);
- HttpServletResponse response = action.getResponse();
-
- startRequest(action);
-
- try {
- // Get the processor inside the startRequest - error handling - finishRequest sequence.
- ActionProcessor proc = processor.get();
- if ( proc == null ) {
- // Only for the logging.
- finishRequest(action);
- logNoResponse(action);
- archiveHttpAction(action);
- // Can't find the URL (the /dataset/service case) - not handled here.
- return false;
- }
- proc.process(action);
- } catch (QueryCancelledException ex) {
- // To put in the action timeout, need (1) global, (2) dataset and (3) protocol settings.
- // See
- // global -- cxt.get(ARQ.queryTimeout)
- // dataset -- dataset.getContect(ARQ.queryTimeout)
- // protocol -- SPARQL_Query.setAnyTimeouts
- String message = "Query timed out";
- ServletOps.responseSendError(response, HttpSC.SERVICE_UNAVAILABLE_503, message);
- } catch (OperationDeniedException ex) {
- if ( ex.getMessage() == null )
- FmtLog.info(action.log, "[%d] OperationDeniedException", action.id);
- else
- FmtLog.info(action.log, "[%d] OperationDeniedException: %s", action.id, ex.getMessage());
- ServletOps.responseSendError(response, HttpSC.FORBIDDEN_403);
- } catch (ActionErrorException ex) {
- if ( ex.getCause() != null )
- FmtLog.warn(action.log, ex, "[%d] ActionErrorException with cause", action.id);
- // Log message done by printResponse in a moment.
- if ( ex.getMessage() != null )
- ServletOps.responseSendError(response, ex.getRC(), ex.getMessage());
- else
- ServletOps.responseSendError(response, ex.getRC());
- } catch (HttpException ex) {
- int sc = ex.getStatusCode();
- if ( sc <= 0 )
- // -1: Connection problem.
- sc = 400;
- // Some code is passing up its own HttpException.
- if ( ex.getMessage() == null )
- ServletOps.responseSendError(response, sc);
- else
- ServletOps.responseSendError(response, sc, ex.getMessage());
- } catch (QueryExceptionHTTP ex) {
- // SERVICE failure.
- int sc = ex.getStatusCode();
- if ( sc <= 0 )
- // -1: Connection problem. "Bad Gateway"
- sc = 502;
- if ( ex.getMessage() == null )
- ServletOps.responseSendError(response, sc);
- else
- ServletOps.responseSendError(response, sc, ex.getMessage());
- } catch (RuntimeIOException ex) {
- FmtLog.warn(action.log, /*ex,*/ "[%d] Runtime IO Exception (client left?) RC = %d : %s", action.id, HttpSC.INTERNAL_SERVER_ERROR_500, ex.getMessage());
- ServletOps.responseSendError(response, HttpSC.INTERNAL_SERVER_ERROR_500, ex.getMessage());
- } catch (Throwable ex) {
- // This should not happen.
- //ex.printStackTrace(System.err);
- FmtLog.warn(action.log, ex, "[%d] RC = %d : %s", action.id, HttpSC.INTERNAL_SERVER_ERROR_500, ex.getMessage());
- ServletOps.responseSendError(response, HttpSC.INTERNAL_SERVER_ERROR_500, ex.getMessage());
- } finally {
- action.setFinishTime();
- finishRequest(action);
- }
- // Handled - including sending back errors.
- logResponse(action);
- archiveHttpAction(action);
- return true;
+ return execActionSub(action, processor);
} catch (Throwable th) {
// This really should not catch anything.
FmtLog.error(action.log, th, "Internal error");
@@ -171,6 +104,85 @@ public class ActionExecLib {
}
}
+ private static boolean execActionSub(HttpAction action, Supplier<ActionProcessor> processor) {
+ logRequest(action);
+ action.setStartTime();
+ initResponse(action);
+ HttpServletResponse response = action.getResponse();
+
+ startRequest(action);
+ try {
+ // Get the processor inside the startRequest - error handling - finishRequest sequence.
+ ActionProcessor proc = processor.get();
+ if ( proc == null ) {
+ // Only for the logging.
+ finishRequest(action);
+ logNoResponse(action);
+ archiveHttpAction(action);
+ // Can't find the URL (the /dataset/service case) - not handled here.
+ return false;
+ }
+ proc.process(action);
+ } catch (QueryCancelledException ex) {
+ // To put in the action timeout, need (1) global, (2) dataset and (3) protocol settings.
+ // See
+ // global -- cxt.get(ARQ.queryTimeout)
+ // dataset -- dataset.getContect(ARQ.queryTimeout)
+ // protocol -- SPARQL_Query.setAnyTimeouts
+ String message = "Query timed out";
+ ServletOps.responseSendError(response, HttpSC.SERVICE_UNAVAILABLE_503, message);
+ } catch (OperationDeniedException ex) {
+ if ( ex.getMessage() == null )
+ FmtLog.info(action.log, "[%d] OperationDeniedException", action.id);
+ else
+ FmtLog.info(action.log, "[%d] OperationDeniedException: %s", action.id, ex.getMessage());
+ ServletOps.responseSendError(response, HttpSC.FORBIDDEN_403);
+ } catch (ActionErrorException ex) {
+ if ( ex.getCause() != null )
+ FmtLog.warn(action.log, ex, "[%d] ActionErrorException with cause", action.id);
+ // Log message done by printResponse in a moment.
+ if ( ex.getMessage() != null )
+ ServletOps.responseSendError(response, ex.getRC(), ex.getMessage());
+ else
+ ServletOps.responseSendError(response, ex.getRC());
+ } catch (HttpException ex) {
+ int sc = ex.getStatusCode();
+ if ( sc <= 0 )
+ // -1: Connection problem.
+ sc = 400;
+ // Some code is passing up its own HttpException.
+ if ( ex.getMessage() == null )
+ ServletOps.responseSendError(response, sc);
+ else
+ ServletOps.responseSendError(response, sc, ex.getMessage());
+ } catch (QueryExceptionHTTP ex) {
+ // SERVICE failure.
+ int sc = ex.getStatusCode();
+ if ( sc <= 0 )
+ // -1: Connection problem. "Bad Gateway"
+ sc = 502;
+ if ( ex.getMessage() == null )
+ ServletOps.responseSendError(response, sc);
+ else
+ ServletOps.responseSendError(response, sc, ex.getMessage());
+ } catch (RuntimeIOException ex) {
+ FmtLog.warn(action.log, /*ex,*/ "[%d] Runtime IO Exception (client left?) RC = %d : %s", action.id, HttpSC.INTERNAL_SERVER_ERROR_500, ex.getMessage());
+ ServletOps.responseSendError(response, HttpSC.INTERNAL_SERVER_ERROR_500, ex.getMessage());
+ } catch (Throwable ex) {
+ // This should not happen.
+ //ex.printStackTrace(System.err);
+ FmtLog.warn(action.log, ex, "[%d] RC = %d : %s", action.id, HttpSC.INTERNAL_SERVER_ERROR_500, ex.getMessage());
+ ServletOps.responseSendError(response, HttpSC.INTERNAL_SERVER_ERROR_500, ex.getMessage());
+ } finally {
+ action.setFinishTime();
+ finishRequest(action);
+ }
+ // Handled - including sending back errors.
+ logResponse(action);
+ archiveHttpAction(action);
+ return true;
+ }
+
/**
* Helper method which gets a unique request ID and appends it as a header to the
* response
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionLib.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionLib.java
index 084e4ee..b5421b0 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionLib.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/ActionLib.java
@@ -59,45 +59,23 @@ import org.apache.jena.web.HttpSC;
/** Operations related to servlets */
public class ActionLib {
- /**
- * Get the datasets from an {@link HttpAction}
- * that assumes the form /dataset/service.
- * @param action the request
- * @return the dataset
- */
- public static String mapRequestToDataset(HttpAction action) {
- String uri = action.getActionURI();
- return mapRequestToDataset(uri);
- }
-
- /** Map request to uri in the registry.
- * A possible implementation for mapRequestToDataset(String)
- * that assumes the form /dataset/service
- * Returning null means no mapping found.
- * The URI must be the action URI (no contact path)
- */
- public static String mapRequestToDataset(String uri) {
- // Chop off trailing part - the service selector
- // e.g. /dataset/sparql => /dataset
- int i = uri.lastIndexOf('/');
- if ( i == -1 )
- return null;
- if ( i == 0 ) {
- // started with '/' - leave.
- return uri;
- }
- return uri.substring(0, i);
+ /** Calculate the operation, given action and data access point */
+ public static String mapRequestToEndpointName(HttpAction action, DataAccessPoint dataAccessPoint) {
+ String uri = action.getActionURI();
+ return mapRequestToEndpointName(uri, dataAccessPoint);
}
- /** Calculate the operation, given action and data access point */
- public static String mapRequestToEndpointName(HttpAction action, DataAccessPoint dsRef) {
- if ( dsRef == null )
+ /** Calculate the operation, given request URI and data access point */
+ public static String mapRequestToEndpointName(String uri, DataAccessPoint dataAccessPoint) {
+ if ( dataAccessPoint == null )
return "";
- String uri = action.getActionURI();
- String name = dsRef.getName();
+ String name = dataAccessPoint.getName();
if ( name.length() >= uri.length() )
return "";
+ if ( name.equals("/") )
+ // Case "/" and uri "/service"
+ return uri.substring(1);
return uri.substring(name.length()+1); // Skip the separating "/"
}
@@ -105,7 +83,7 @@ public class ActionLib {
* Implementation of mapRequestToDataset(String) that looks for the longest match
* in the registry. This includes use in direct naming GSP.
*/
- public static String mapRequestToDatasetLongest$(String uri, DataAccessPointRegistry registry) {
+ public static String unused_mapRequestToDatasetLongest(String uri, DataAccessPointRegistry registry) {
if ( uri == null )
return null;
@@ -167,16 +145,17 @@ public class ActionLib {
// ServletContext cxt = this.getServletContext();
// Log.info(this, "ServletContext path = '"+cxt.getContextPath()+"'");
+ String uri = request.getRequestURI();
ServletContext servletCxt = request.getServletContext();
if ( servletCxt == null )
return request.getRequestURI();
String contextPath = servletCxt.getContextPath();
- String uri = request.getRequestURI();
if ( contextPath == null )
return uri;
if ( contextPath.isEmpty())
return uri;
+
String x = uri;
if ( uri.startsWith(contextPath) )
x = uri.substring(contextPath.length());
diff --git a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/UploadRDF.java b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/UploadRDF.java
index c251a9f..6f922e9 100644
--- a/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/UploadRDF.java
+++ b/jena-fuseki2/jena-fuseki-core/src/main/java/org/apache/jena/fuseki/servlets/UploadRDF.java
@@ -18,6 +18,8 @@
package org.apache.jena.fuseki.servlets;
+import static java.lang.String.format;
+
import java.util.function.Function;
import org.apache.jena.atlas.web.ContentType;
@@ -120,12 +122,20 @@ public class UploadRDF extends ActionREST {
} catch (RiotException ex) {
// Parse error
action.abortSilent();
+ if ( ex.getMessage() != null )
+ action.log.info(format("[%d] Data error: %s", action.id, ex.getMessage()));
+ else
+ action.log.info(format("[%d] Data error", action.id), ex);
ServletOps.errorBadRequest(ex.getMessage());
} catch (OperationDeniedException ex) {
action.abortSilent();
throw ex;
} catch (ActionErrorException ex) {
action.abortSilent();
+ if ( ex.getMessage() != null )
+ action.log.info(format("[%d] Upload error: %s", action.id, ex.getMessage()));
+ else
+ action.log.info(format("[%d] Upload error", action.id), ex);
throw ex;
} catch (Exception ex) {
// Something else went wrong. Backout.
diff --git a/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/TS_FusekiCore.java b/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/TS_FusekiCore.java
index bab7afc..a3b1ce9 100644
--- a/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/TS_FusekiCore.java
+++ b/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/TS_FusekiCore.java
@@ -18,6 +18,7 @@
package org.apache.jena.fuseki;
+import org.apache.jena.fuseki.server.TestDispatchOnURI;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
@@ -25,7 +26,8 @@ import org.junit.runners.Suite.SuiteClasses;
// Most testing needs a server.
@RunWith(Suite.class)
@SuiteClasses({
- TestValidators.class
+ TestValidators.class,
+ TestDispatchOnURI.class
})
public class TS_FusekiCore {}
diff --git a/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/server/TestDispatchOnURI.java b/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/server/TestDispatchOnURI.java
new file mode 100644
index 0000000..abad6c0
--- /dev/null
+++ b/jena-fuseki2/jena-fuseki-core/src/test/java/org/apache/jena/fuseki/server/TestDispatchOnURI.java
@@ -0,0 +1,137 @@
+/*
+ * 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+import org.apache.jena.fuseki.servlets.ActionLib;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/*
+ * Test the request URI part of dispatch.
+ * This covers finding the DataAccessPoint.
+ * A request may still fail due to no endpoint or suitable processor;
+ * this isn't covered in these unit tests.
+ */
+public class TestDispatchOnURI {
+
+ private static DataAccessPointRegistry registryNoRoot;
+ private static DataAccessPointRegistry registryWithRoot;
+
+ @BeforeClass public static void beforeClass() {
+ registryNoRoot = new DataAccessPointRegistry();
+ DataService dSrv1 = DataService.newBuilder()
+ .addEndpoint(Operation.Query)
+ .addEndpoint(Operation.Query, "spook")
+ .build();
+ registryNoRoot.register(new DataAccessPoint("ds", dSrv1));
+ DataService dSrv2 = DataService.newBuilder()
+ .addEndpoint(Operation.Query)
+ .build();
+ registryNoRoot.register(new DataAccessPoint("/path/dataset", dSrv2));
+ registryNoRoot.register(new DataAccessPoint("/path1/path2/dataset", dSrv2));
+
+ registryWithRoot = new DataAccessPointRegistry(registryNoRoot);
+ registryWithRoot.register(new DataAccessPoint("/", dSrv1));
+ }
+
+ @Test public void dispatch_1() {
+ testDispatch("/ds", registryWithRoot, "/ds", "");
+ }
+
+ @Test public void dispatch_2() {
+ // Request URI dispatch does not consider existence of a suitable endpoint.
+ testDispatch("/ds/does-not-exist", registryWithRoot, "/ds", "does-not-exist");
+ }
+
+ @Test public void dispatch_3() {
+ testDispatch("/ds/spook", registryWithRoot, "/ds", "spook");
+ }
+
+ @Test public void dispatch_root_1() {
+ testDispatch("/", registryWithRoot, "/", "");
+ }
+
+ @Test public void dispatch_root_2() {
+ testDispatch("/sparql", registryWithRoot, "/", "sparql");
+ }
+
+ // endpoint names can only be path components.
+ @Test public void no_dispatch_1() {
+ testNoDispatch("/ds/abc/def", registryWithRoot);
+ }
+
+ @Test public void no_dispatch_2() {
+ testNoDispatch("/x404", registryNoRoot);
+ }
+
+ @Test public void no_dispatch_3() {
+ testNoDispatch("/", registryNoRoot);
+ }
+
+ @Test public void no_dispatch_4() {
+ testNoDispatch("/x404/sparql", registryWithRoot);
+ }
+
+ @Test public void no_dispatch_5() {
+ testNoDispatch("/anotherPath/dataset", registryWithRoot);
+ }
+
+ @Test public void dispatch_path_1() {
+ testDispatch("/path/dataset", registryWithRoot, "/path/dataset", "");
+ }
+
+ @Test public void dispatch_path_2() {
+ testDispatch("/path/dataset/sparql", registryWithRoot, "/path/dataset", "sparql");
+ }
+
+ @Test public void dispatch_path_3() {
+ testDispatch("/path/dataset/does-not-exist", registryWithRoot, "/path/dataset", "does-not-exist");
+ }
+
+ @Test public void dispatch_path_4() {
+ testDispatch("/path1/path2/dataset", registryWithRoot, "/path1/path2/dataset", "");
+ }
+
+ @Test public void dispatch_path_5() {
+ testDispatch("/path1/path2/dataset/sparql", registryWithRoot, "/path1/path2/dataset", "sparql");
+ }
+
+ private void testNoDispatch(String requestURI, DataAccessPointRegistry registry) {
+ DataAccessPoint dap = Dispatcher.locateDataAccessPoint(requestURI, registry);
+ assertNull("Expect no dispatch for "+requestURI, dap);
+ }
+
+ private void testDispatch(String requestURI, DataAccessPointRegistry registry, String expectedDataset, String expectedEndpoint) {
+ DataAccessPoint dap = Dispatcher.locateDataAccessPoint(requestURI, registry);
+ if ( dap == null ) {
+ if ( expectedDataset != null )
+ fail("No DataAccessPoint: expected to find a match: "+requestURI+" -> ("+expectedDataset+", "+expectedEndpoint+")");
+ return;
+ }
+ // The request URI part of dispatch choice in Dispatcher.chooseProcessor(HttpAction action)
+ String ep = ActionLib.mapRequestToEndpointName(requestURI, dap);
+ assertNotNull(ep);
+ assertEquals("Endpoint", expectedEndpoint, ep);
+ }
+}