diff --git a/tools/pom.xml b/tools/pom.xml
index 6842c44b1f9..faf23eb0c5a 100644
--- a/tools/pom.xml
+++ b/tools/pom.xml
@@ -19,6 +19,7 @@
workbench
runtime
runtime-osgi
+ server-jar
diff --git a/tools/server-jar/pom.xml b/tools/server-jar/pom.xml
new file mode 100644
index 00000000000..45fb65aa6ca
--- /dev/null
+++ b/tools/server-jar/pom.xml
@@ -0,0 +1,100 @@
+
+
+ 4.0.0
+
+ org.eclipse.rdf4j
+ rdf4j-tools
+ 5.1.3-SNAPSHOT
+
+ rdf4j-http-server-jar
+ RDF4J: HTTP server - self-contained executable
+ A self-contained executable of the RDF4J Server
+
+ 2.7.18
+
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${spring.boot.version}
+ pom
+ import
+
+
+
+
+
+ ${project.groupId}
+ rdf4j-http-server-spring
+ ${project.version}
+
+
+ ${project.groupId}
+ rdf4j-config
+ ${project.version}
+
+
+ org.eclipse.rdf4j
+ rdf4j-tools-federation
+ ${project.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework
+ spring-jcl
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-core
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-websocket
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-jetty
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.slf4j
+ slf4j-api
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring.boot.version}
+
+ org.eclipse.rdf4j.server.Application
+
+
+
+
+ repackage
+
+
+
+
+
+
+
diff --git a/tools/server-jar/src/main/java/org/eclipse/rdf4j/server/Application.java b/tools/server-jar/src/main/java/org/eclipse/rdf4j/server/Application.java
new file mode 100644
index 00000000000..e9583fb5671
--- /dev/null
+++ b/tools/server-jar/src/main/java/org/eclipse/rdf4j/server/Application.java
@@ -0,0 +1,17 @@
+package org.eclipse.rdf4j.server;
+
+import org.eclipse.rdf4j.common.annotation.Experimental;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.PropertySource;
+
+@Experimental
+@SpringBootApplication
+@PropertySource(
+ value = "classpath:org/eclipse/rdf4j/http/server/application.properties", ignoreResourceNotFound = true, name = "org.springframework.context.support.PropertySourcesPlaceholderConfigurer"
+)
+public class Application {
+ public static void main(final String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+}
diff --git a/tools/server-jar/src/main/java/org/eclipse/rdf4j/server/MvcConfiguration.java b/tools/server-jar/src/main/java/org/eclipse/rdf4j/server/MvcConfiguration.java
new file mode 100644
index 00000000000..72c5ebc5213
--- /dev/null
+++ b/tools/server-jar/src/main/java/org/eclipse/rdf4j/server/MvcConfiguration.java
@@ -0,0 +1,17 @@
+package org.eclipse.rdf4j.server;
+
+import java.util.List;
+
+import org.eclipse.rdf4j.http.server.ProtocolExceptionResolver;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.lang.NonNull;
+import org.springframework.web.servlet.HandlerExceptionResolver;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+public class MvcConfiguration implements WebMvcConfigurer {
+ @Override
+ public void configureHandlerExceptionResolvers(@NonNull final List resolvers) {
+ resolvers.add(new ProtocolExceptionResolver());
+ }
+}
diff --git a/tools/server-jar/src/main/java/org/eclipse/rdf4j/server/ProtocolControllers.java b/tools/server-jar/src/main/java/org/eclipse/rdf4j/server/ProtocolControllers.java
new file mode 100644
index 00000000000..03ddff91c9e
--- /dev/null
+++ b/tools/server-jar/src/main/java/org/eclipse/rdf4j/server/ProtocolControllers.java
@@ -0,0 +1,15 @@
+package org.eclipse.rdf4j.server;
+
+import org.eclipse.rdf4j.http.server.protocol.ProtocolController;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.lang.NonNull;
+
+@Configuration
+public class ProtocolControllers {
+ @NonNull
+ @Bean(name = "rdf4jProtocolController")
+ public ProtocolController rdf4jProtocolController() {
+ return new ProtocolController();
+ }
+}
diff --git a/tools/server-jar/src/main/java/org/eclipse/rdf4j/server/RepositoryControllers.java b/tools/server-jar/src/main/java/org/eclipse/rdf4j/server/RepositoryControllers.java
new file mode 100644
index 00000000000..b521288a5a6
--- /dev/null
+++ b/tools/server-jar/src/main/java/org/eclipse/rdf4j/server/RepositoryControllers.java
@@ -0,0 +1,126 @@
+package org.eclipse.rdf4j.server;
+
+import org.eclipse.rdf4j.http.server.repository.RepositoryController;
+import org.eclipse.rdf4j.http.server.repository.RepositoryInterceptor;
+import org.eclipse.rdf4j.http.server.repository.RepositoryListController;
+import org.eclipse.rdf4j.http.server.repository.config.ConfigController;
+import org.eclipse.rdf4j.http.server.repository.contexts.ContextsController;
+import org.eclipse.rdf4j.http.server.repository.graph.GraphController;
+import org.eclipse.rdf4j.http.server.repository.namespaces.NamespaceController;
+import org.eclipse.rdf4j.http.server.repository.namespaces.NamespacesController;
+import org.eclipse.rdf4j.http.server.repository.size.SizeController;
+import org.eclipse.rdf4j.http.server.repository.statements.StatementsController;
+import org.eclipse.rdf4j.http.server.repository.transaction.TransactionController;
+import org.eclipse.rdf4j.http.server.repository.transaction.TransactionStartController;
+import org.eclipse.rdf4j.repository.manager.RepositoryManager;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.lang.NonNull;
+import org.springframework.lang.Nullable;
+import org.springframework.web.context.annotation.RequestScope;
+
+@Configuration
+public class RepositoryControllers {
+
+ @NonNull
+ @Autowired
+ @Bean(name = "rdf4jRepositoryInterceptor")
+ @RequestScope
+ public RepositoryInterceptor rdf4jRepositoryInterceptor(
+ @NonNull @Qualifier("rdf4jRepositoryManager") final RepositoryManager repositoryManager) {
+ final RepositoryInterceptor interceptor = new RepositoryInterceptor();
+ interceptor.setRepositoryManager(repositoryManager);
+
+ return interceptor;
+ }
+
+ @NonNull
+ @Autowired
+ @Bean(name = "rdf4jRepositoryController")
+ public RepositoryController repositoryController(
+ @NonNull @Qualifier("rdf4jRepositoryManager") final RepositoryManager repositoryManager) {
+ final RepositoryController controller = new RepositoryController();
+ controller.setRepositoryManager(repositoryManager);
+
+ return controller;
+ }
+
+ @NonNull
+ @Autowired
+ @Bean(name = "rdf4jRepositoryListController")
+ public RepositoryListController repositoryListController(
+ @NonNull @Qualifier("rdf4jRepositoryManager") final RepositoryManager repositoryManager) {
+ final RepositoryListController controller = new RepositoryListController();
+ controller.setRepositoryManager(repositoryManager);
+
+ return controller;
+ }
+
+ @NonNull
+ @Autowired
+ @Bean(name = "rdf4jRepositoryConfigController")
+ public ConfigController rdf4jRepositoryConfigController(
+ @NonNull @Qualifier("rdf4jRepositoryManager") final RepositoryManager repositoryManager) {
+ final ConfigController controller = new ConfigController();
+ controller.setRepositoryManager(repositoryManager);
+
+ return controller;
+ }
+
+ @NonNull
+ @Bean(name = "rdf4jRepositoryContextsController")
+ public ContextsController rdf4jRepositoryContextsController() {
+ return new ContextsController();
+ }
+
+ @NonNull
+ @Bean(name = "rdf4jRepositoryNamespacesController")
+ public NamespacesController rdf4jRepositoryNamespacesController() {
+ return new NamespacesController();
+ }
+
+ @NonNull
+ @Bean(name = "rdf4jRepositoryNamespaceController")
+ public NamespaceController rdf4jRepositoryNamespaceController() {
+ return new NamespaceController();
+ }
+
+ @NonNull
+ @Bean(name = "rdf4jRepositorySizeController")
+ public SizeController rdf4jRepositorySizeController() {
+ return new SizeController();
+ }
+
+ @NonNull
+ @Bean(name = "rdf4jRepositoryStatementsController")
+ public StatementsController rdf4jRepositoryStatementsController() {
+ return new StatementsController();
+ }
+
+ @NonNull
+ @Bean(name = "rdf4jRepositoryGraphController")
+ public GraphController rdf4jRepositoryGraphController() {
+ return new GraphController();
+ }
+
+ @NonNull
+ @Bean(name = "rdf4jRepositoryTransactionController")
+ public TransactionController rdf4jRepositoryTransactionController() {
+ return new TransactionController();
+ }
+
+ @NonNull
+ @Bean(name = "rdf4jRepositoryTransactionStartController")
+ public TransactionStartController rdf4jRepositoryTransactionStartController(
+ @Nullable @Value("${rdf4j.externalurl:#{null}}") final String externalUrl
+ ) {
+ final TransactionStartController transactionStartController = new TransactionStartController();
+ transactionStartController.setExternalUrl(externalUrl);
+
+ return transactionStartController;
+ }
+
+}
diff --git a/tools/server-jar/src/main/java/org/eclipse/rdf4j/server/Routes.java b/tools/server-jar/src/main/java/org/eclipse/rdf4j/server/Routes.java
new file mode 100644
index 00000000000..2f0fe9b4b4d
--- /dev/null
+++ b/tools/server-jar/src/main/java/org/eclipse/rdf4j/server/Routes.java
@@ -0,0 +1,106 @@
+package org.eclipse.rdf4j.server;
+
+import static org.eclipse.rdf4j.http.server.repository.RepositoryInterceptor.REPOSITORY_KEY;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.eclipse.rdf4j.http.server.protocol.ProtocolController;
+import org.eclipse.rdf4j.http.server.protocol.ProtocolInterceptor;
+import org.eclipse.rdf4j.http.server.repository.RepositoryController;
+import org.eclipse.rdf4j.http.server.repository.RepositoryInterceptor;
+import org.eclipse.rdf4j.http.server.repository.RepositoryListController;
+import org.eclipse.rdf4j.http.server.repository.config.ConfigController;
+import org.eclipse.rdf4j.http.server.repository.contexts.ContextsController;
+import org.eclipse.rdf4j.http.server.repository.graph.GraphController;
+import org.eclipse.rdf4j.http.server.repository.namespaces.NamespaceController;
+import org.eclipse.rdf4j.http.server.repository.namespaces.NamespacesController;
+import org.eclipse.rdf4j.http.server.repository.size.SizeController;
+import org.eclipse.rdf4j.http.server.repository.statements.StatementsController;
+import org.eclipse.rdf4j.http.server.repository.transaction.TransactionController;
+import org.eclipse.rdf4j.http.server.repository.transaction.TransactionStartController;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.lang.NonNull;
+import org.springframework.web.servlet.HandlerMapping;
+import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
+
+@Configuration
+public class Routes {
+ @NonNull
+ @Autowired
+ @Bean(name = "rdf4jProtocolUrlMapping")
+ public HandlerMapping rdf4jProtocolUrlMapping(
+ @NonNull @Qualifier("rdf4jProtocolController") final ProtocolController protocolController) {
+ final Map urlMap = new LinkedHashMap<>(1);
+
+ urlMap.put("/protocol", protocolController);
+
+ final SimpleUrlHandlerMapping mappings = new SimpleUrlHandlerMapping();
+ mappings.setUrlMap(urlMap);
+ mappings.setOrder(0);
+ mappings.setAlwaysUseFullPath(true);
+ mappings.setInterceptors(new ProtocolInterceptor());
+
+ return mappings;
+ }
+
+ @NonNull
+ @Autowired
+ @Bean(name = "rdf4jRepositoryListUrlMapping")
+ public HandlerMapping rdf4jRepositoryListUrlMapping(
+ @NonNull @Qualifier("rdf4jRepositoryListController") final RepositoryListController repositoryListController) {
+ final Map urlMap = new LinkedHashMap<>(1);
+
+ urlMap.put("/repositories", repositoryListController);
+
+ final SimpleUrlHandlerMapping mappings = new SimpleUrlHandlerMapping();
+ mappings.setUrlMap(urlMap);
+ mappings.setOrder(1);
+ mappings.setAlwaysUseFullPath(true);
+
+ return mappings;
+ }
+
+ @NonNull
+ @Autowired
+ @Bean(name = "rdf4jRepositoryUrlMapping")
+ public HandlerMapping rdf4jRepositoryUrlMapping(
+ @NonNull @Qualifier("rdf4jRepositoryController") final RepositoryController repositoryController,
+ @NonNull @Qualifier("rdf4jRepositoryConfigController") final ConfigController repositoryConfigController,
+ @NonNull @Qualifier("rdf4jRepositoryContextsController") final ContextsController contextsController,
+ @NonNull @Qualifier("rdf4jRepositoryNamespacesController") final NamespacesController namespacesController,
+ @NonNull @Qualifier("rdf4jRepositoryNamespaceController") final NamespaceController namespaceController,
+ @NonNull @Qualifier("rdf4jRepositorySizeController") final SizeController sizeController,
+ @NonNull @Qualifier("rdf4jRepositoryStatementsController") final StatementsController statementsController,
+ @NonNull @Qualifier("rdf4jRepositoryGraphController") final GraphController graphController,
+ @NonNull @Qualifier("rdf4jRepositoryTransactionController") final TransactionController transactionController,
+ @NonNull @Qualifier("rdf4jRepositoryTransactionStartController") final TransactionStartController transactionStartController,
+ @NonNull @Qualifier("rdf4jRepositoryInterceptor") final RepositoryInterceptor repositoryInterceptor
+ ) {
+ final Map urlMap = new LinkedHashMap<>(11);
+
+ final String repositoryPrefix = "/repositories/{" + REPOSITORY_KEY + "}";
+ urlMap.put(repositoryPrefix + "/namespaces/{nsPrefix}", namespaceController);
+ urlMap.put(repositoryPrefix + "/namespaces", namespacesController);
+ urlMap.put(repositoryPrefix + "/config", repositoryConfigController);
+ urlMap.put(repositoryPrefix + "/contexts", contextsController);
+ urlMap.put(repositoryPrefix + "/statements", statementsController);
+ urlMap.put(repositoryPrefix + "/rdf-graphs", contextsController);
+ urlMap.put(repositoryPrefix + "/rdf-graphs/{graph}", graphController);
+ urlMap.put(repositoryPrefix + "/size", sizeController);
+ urlMap.put(repositoryPrefix + "/transactions", transactionStartController);
+ urlMap.put(repositoryPrefix + "/transactions/{xid}", transactionController);
+ urlMap.put(repositoryPrefix, repositoryController);
+
+ final SimpleUrlHandlerMapping mappings = new SimpleUrlHandlerMapping();
+ mappings.setUrlMap(urlMap);
+ mappings.setOrder(2);
+ mappings.setAlwaysUseFullPath(true);
+ mappings.setInterceptors(repositoryInterceptor);
+
+ return mappings;
+ }
+}
diff --git a/tools/server-jar/src/main/java/org/eclipse/rdf4j/server/ServiceConfiguration.java b/tools/server-jar/src/main/java/org/eclipse/rdf4j/server/ServiceConfiguration.java
new file mode 100644
index 00000000000..df19f8028ec
--- /dev/null
+++ b/tools/server-jar/src/main/java/org/eclipse/rdf4j/server/ServiceConfiguration.java
@@ -0,0 +1,54 @@
+package org.eclipse.rdf4j.server;
+
+import java.util.Objects;
+
+import org.eclipse.rdf4j.common.app.AppConfiguration;
+import org.eclipse.rdf4j.repository.manager.LocalRepositoryManager;
+import org.eclipse.rdf4j.repository.manager.RepositoryManager;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;
+import org.springframework.context.MessageSource;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Scope;
+import org.springframework.context.support.ResourceBundleMessageSource;
+import org.springframework.lang.NonNull;
+
+@Configuration
+public class ServiceConfiguration {
+ @NonNull
+ @Bean(name = "commonAppConfig", initMethod = "init", destroyMethod = "destroy")
+ public AppConfiguration commonAppConfig() {
+ final AppConfiguration config = new AppConfiguration();
+ config.setApplicationId("Server");
+ config.setLongName("RDF4J Server");
+
+ return config;
+ }
+
+ @NonNull
+ @Autowired
+ @Bean(name = "rdf4jRepositoryManager", initMethod = "init", destroyMethod = "shutDown")
+ @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
+ public RepositoryManager rdf4jRepositoryManager(
+ @NonNull @Qualifier("commonAppConfig") final AppConfiguration appConfig) {
+ Objects.requireNonNull(appConfig, "Application config was not properly initialized!");
+
+ return new LocalRepositoryManager(appConfig.getDataDir());
+ }
+
+ @NonNull
+ @Bean(name = "messageSource")
+ public MessageSource messageSource() {
+ final ResourceBundleMessageSource bundle = new ResourceBundleMessageSource();
+
+ bundle.setBasenames(
+ "org.eclipse.rdf4j.http.server.messages",
+ "org.eclipse.rdf4j.common.webapp.system.messages",
+ "org.eclipse.rdf4j.common.webapp.messages"
+ );
+
+ return bundle;
+ }
+}
diff --git a/tools/server-jar/src/main/resources/logback.xml b/tools/server-jar/src/main/resources/logback.xml
new file mode 100644
index 00000000000..962f9889af5
--- /dev/null
+++ b/tools/server-jar/src/main/resources/logback.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/RepositoryInterceptor.java b/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/RepositoryInterceptor.java
index 98d50d996dc..bed6c0aebf2 100644
--- a/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/RepositoryInterceptor.java
+++ b/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/RepositoryInterceptor.java
@@ -12,6 +12,7 @@
import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
+import java.util.Map;
import java.util.Objects;
import javax.servlet.http.HttpServletRequest;
@@ -30,6 +31,7 @@
import org.eclipse.rdf4j.rio.helpers.BasicParserSettings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.web.servlet.HandlerMapping;
/**
* Interceptor for repository requests. Should not be a singleton bean! Configure as inner bean in openrdf-servlet.xml
@@ -47,7 +49,7 @@ public class RepositoryInterceptor extends ServerInterceptor {
private static final String REPOSITORY_ID_KEY = "repositoryID";
- private static final String REPOSITORY_KEY = "repository";
+ public static final String REPOSITORY_KEY = "repository";
/*-----------*
* Variables *
@@ -80,6 +82,17 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons
}
}
+ if (repositoryID == null) {
+ final Object pathVariables = request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
+
+ // noinspection unchecked
+ repositoryID = ((Map) pathVariables).get(REPOSITORY_KEY);
+ }
+
+ if (repositoryID != null) {
+ logger.debug("repositoryID is '{}'", repositoryID);
+ }
+
ProtocolUtil.logRequestParameters(request);
return super.preHandle(request, respons, handler);
diff --git a/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/namespaces/NamespaceController.java b/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/namespaces/NamespaceController.java
index 070dfd5b55d..85cce6268f7 100644
--- a/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/namespaces/NamespaceController.java
+++ b/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/namespaces/NamespaceController.java
@@ -35,6 +35,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContextException;
+import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;
@@ -55,7 +56,15 @@ public NamespaceController() throws ApplicationContextException {
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception {
String pathInfoStr = request.getPathInfo();
- String prefix = pathInfoStr.substring(pathInfoStr.lastIndexOf('/') + 1);
+ final String prefix;
+ if (pathInfoStr != null) {
+ prefix = pathInfoStr.substring(pathInfoStr.lastIndexOf('/') + 1);
+ } else {
+ final Object pathVariables = request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
+
+ // noinspection unchecked
+ prefix = ((Map) pathVariables).get("nsPrefix");
+ }
String reqMethod = request.getMethod();
diff --git a/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/transaction/TransactionController.java b/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/transaction/TransactionController.java
index f0a2b46612b..83c88b38b8a 100644
--- a/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/transaction/TransactionController.java
+++ b/tools/server-spring/src/main/java/org/eclipse/rdf4j/http/server/repository/transaction/TransactionController.java
@@ -91,6 +91,7 @@
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.ApplicationContextException;
+import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.mvc.AbstractController;
@@ -215,7 +216,6 @@ private UUID getTransactionID(HttpServletRequest request) throws ClientHTTPExcep
if (pathInfo.length == 3) {
try {
txnID = UUID.fromString(pathInfo[2]);
- logger.debug("txnID is '{}'", txnID);
} catch (IllegalArgumentException e) {
throw new ClientHTTPException(SC_BAD_REQUEST, "not a valid transaction id: " + pathInfo[2]);
}
@@ -224,6 +224,20 @@ private UUID getTransactionID(HttpServletRequest request) throws ClientHTTPExcep
}
}
+ if (txnID == null) {
+ final Object pathVariables = request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
+
+ // noinspection unchecked
+ final String xidStr = ((Map) pathVariables).get("xid");
+ try {
+ txnID = UUID.fromString(xidStr);
+ } catch (IllegalArgumentException e) {
+ throw new ClientHTTPException(SC_BAD_REQUEST, "not a valid transaction id: " + xidStr);
+ }
+ }
+
+ logger.debug("txnID is '{}'", txnID);
+
return txnID;
}