diff --git a/instrumentation/httpclient/pom.xml b/instrumentation/httpclient/pom.xml
index f334e362f4..5460ded090 100644
--- a/instrumentation/httpclient/pom.xml
+++ b/instrumentation/httpclient/pom.xml
@@ -12,6 +12,8 @@
${project.basedir}/../..
+ 1.6
+ java16
diff --git a/instrumentation/jaxrs2/pom.xml b/instrumentation/jaxrs2/pom.xml
index 93e176258c..b8ce3e0406 100644
--- a/instrumentation/jaxrs2/pom.xml
+++ b/instrumentation/jaxrs2/pom.xml
@@ -12,6 +12,8 @@
${project.basedir}/../..
+ 1.6
+ java16
diff --git a/instrumentation/mysql/pom.xml b/instrumentation/mysql/pom.xml
index e23972dc8b..60cc49b1e8 100644
--- a/instrumentation/mysql/pom.xml
+++ b/instrumentation/mysql/pom.xml
@@ -12,6 +12,8 @@
${project.basedir}/../..
+ 1.6
+ java16
diff --git a/instrumentation/servlet/pom.xml b/instrumentation/servlet/pom.xml
index 48bcc70bd6..7c3298702b 100644
--- a/instrumentation/servlet/pom.xml
+++ b/instrumentation/servlet/pom.xml
@@ -12,6 +12,8 @@
${project.basedir}/../..
+ 1.6
+ java16
diff --git a/instrumentation/spring-webmvc/README.md b/instrumentation/spring-webmvc/README.md
index ea8e777ce1..139f41e575 100644
--- a/instrumentation/spring-webmvc/README.md
+++ b/instrumentation/spring-webmvc/README.md
@@ -1,8 +1,8 @@
# brave-instrumentation-spring-webmvc
-This module contains a tracing interceptor for [Spring WebMVC](https://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html)
-`TracingHandlerInterceptor` extracts trace state from incoming requests.
-Then, it reports Zipkin how long each request takes, along with relevant
-tags like the http url.
+This module contains tracing interceptors for [Spring WebMVC](https://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html)
+`TracingAsyncHandlerInterceptor` and `TracingHandlerInterceptor` extract trace state from incoming
+requests. Then, they report Zipkin how long each request takes, along with relevant tags like the
+http url.
## Configuration
@@ -11,19 +11,20 @@ it is in place before proceeding. Here's an example in [XML](https://github.com/
Then, configure `TracingHandlerInterceptor` in either XML or Java.
+Here's an example of using the synchronous handler, which works with Spring 2.5+
```xml
-
-
+
```
+Here's an example of the asynchronous-capable handler, which works with Spring 3+
```java
@Configuration
@EnableWebMvc
class TracingConfig extends WebMvcConfigurerAdapter {
@Bean AsyncHandlerInterceptor tracingInterceptor(HttpTracing httpTracing) {
- return TracingHandlerInterceptor.create(httpTracing);
+ return TracingAsyncHandlerInterceptor.create(httpTracing);
}
@Autowired
diff --git a/instrumentation/spring-webmvc/src/it/spring25/README.md b/instrumentation/spring-webmvc/src/it/spring25/README.md
new file mode 100644
index 0000000000..fe9edff997
--- /dev/null
+++ b/instrumentation/spring-webmvc/src/it/spring25/README.md
@@ -0,0 +1,2 @@
+# nozipkin1
+This tests that Tracing can be initialized without a runtime dependency on `io.zipkin.java:zipkin`
diff --git a/instrumentation/spring-webmvc/src/it/spring25/pom.xml b/instrumentation/spring-webmvc/src/it/spring25/pom.xml
new file mode 100644
index 0000000000..06112f8f55
--- /dev/null
+++ b/instrumentation/spring-webmvc/src/it/spring25/pom.xml
@@ -0,0 +1,74 @@
+
+
+ 4.0.0
+
+ @project.groupId@
+ spring-webmvc-spring25
+ @project.version@
+ spring-webmvc-spring25
+
+
+ 1.8
+ 1.8
+
+
+
+
+ javax.servlet
+ servlet-api
+ 2.5
+ provided
+
+
+
+ org.springframework
+ spring-webmvc
+ 2.5.6
+ provided
+
+
+
+ org.eclipse.jetty
+ jetty-servlet
+ @jetty-servlet25.version@
+
+
+
+ ${project.groupId}
+ brave-instrumentation-spring-webmvc
+ ${project.version}
+
+
+
+ ${project.groupId}
+ brave-instrumentation-http-tests
+ ${project.version}
+
+
+
+ junit
+ junit
+ @junit.version@
+
+
+
+ org.assertj
+ assertj-core
+ @assertj.version@
+
+
+
+
+
+
+ maven-failsafe-plugin
+ @maven-failsafe-plugin.version@
+
+ true
+
+
+
+
+
diff --git a/instrumentation/spring-webmvc/src/it/spring25/src/test/java/brave/spring/webmvc25/ITTracingHandlerInterceptor.java b/instrumentation/spring-webmvc/src/it/spring25/src/test/java/brave/spring/webmvc25/ITTracingHandlerInterceptor.java
new file mode 100644
index 0000000000..3d1ccf45f0
--- /dev/null
+++ b/instrumentation/spring-webmvc/src/it/spring25/src/test/java/brave/spring/webmvc25/ITTracingHandlerInterceptor.java
@@ -0,0 +1,90 @@
+package brave.spring.webmvc25;
+
+import brave.Tracer;
+import brave.http.HttpTracing;
+import brave.http.ITServletContainer;
+import brave.spring.webmvc.TracingHandlerInterceptor;
+import java.io.IOException;
+import javax.servlet.http.HttpServletResponse;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.junit.Test;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.context.WebApplicationContext;
+import org.springframework.web.context.support.StaticWebApplicationContext;
+import org.springframework.web.servlet.DispatcherServlet;
+import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter;
+import org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping;
+
+import static org.springframework.web.servlet.DispatcherServlet.HANDLER_ADAPTER_BEAN_NAME;
+import static org.springframework.web.servlet.DispatcherServlet.HANDLER_MAPPING_BEAN_NAME;
+
+public class ITTracingHandlerInterceptor extends ITServletContainer {
+
+ @Controller
+ public static class TestController {
+ final Tracer tracer;
+
+ @Autowired public TestController(HttpTracing httpTracing) {
+ this.tracer = httpTracing.tracing().tracer();
+ }
+
+ @RequestMapping(value = "/foo")
+ public void foo(HttpServletResponse response) throws IOException {
+ response.getWriter().write("foo");
+ }
+
+ @RequestMapping(value = "/badrequest")
+ public void badrequest(HttpServletResponse response) throws IOException {
+ response.sendError(400);
+ response.flushBuffer();
+ }
+
+ @RequestMapping(value = "/child")
+ public void child() {
+ tracer.nextSpan().name("child").start().finish();
+ }
+
+ @RequestMapping(value = "/exception")
+ public void disconnect() throws IOException {
+ throw new IOException();
+ }
+ }
+
+ // TODO: investigate why the status isn't being added as a tag
+ @Override @Test(expected = AssertionError.class)
+ public void addsStatusCode_badRequest() throws Exception {
+ super.addsStatusCode_badRequest();
+ }
+
+ @Override public void init(ServletContextHandler handler) {
+ StaticWebApplicationContext wac = new StaticWebApplicationContext();
+ wac.getBeanFactory()
+ .registerSingleton("testController", new TestController(httpTracing)); // the test resource
+
+ DefaultAnnotationHandlerMapping mapping = new DefaultAnnotationHandlerMapping();
+ mapping.setInterceptors(new Object[] {TracingHandlerInterceptor.create(httpTracing)});
+ mapping.setApplicationContext(wac);
+
+ wac.getBeanFactory().registerSingleton(HANDLER_MAPPING_BEAN_NAME, mapping);
+ wac.getBeanFactory().registerSingleton(HANDLER_ADAPTER_BEAN_NAME, new AnnotationMethodHandlerAdapter());
+
+ handler.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac);
+ handler.addServlet(new ServletHolder(new DispatcherServlet() {
+ {
+ wac.refresh();
+ setDetectAllHandlerMappings(false);
+ setDetectAllHandlerAdapters(false);
+ setPublishEvents(false);
+ }
+
+ @Override protected WebApplicationContext initWebApplicationContext() throws BeansException {
+ onRefresh(wac);
+ return wac;
+ }
+ }), "/*");
+ }
+}
diff --git a/instrumentation/spring-webmvc/src/main/java/brave/spring/webmvc/TracingAsyncHandlerInterceptor.java b/instrumentation/spring-webmvc/src/main/java/brave/spring/webmvc/TracingAsyncHandlerInterceptor.java
new file mode 100644
index 0000000000..b140b5e3c6
--- /dev/null
+++ b/instrumentation/spring-webmvc/src/main/java/brave/spring/webmvc/TracingAsyncHandlerInterceptor.java
@@ -0,0 +1,42 @@
+package brave.spring.webmvc;
+
+import brave.Tracing;
+import brave.http.HttpTracing;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.servlet.AsyncHandlerInterceptor;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+/**
+ * Tracing interceptor for Spring Web MVC, which can be used as both an {@link
+ * AsyncHandlerInterceptor} or a normal {@link HandlerInterceptor}.
+ */
+public final class TracingAsyncHandlerInterceptor extends HandlerInterceptorAdapter {
+ public static AsyncHandlerInterceptor create(Tracing tracing) {
+ return new TracingAsyncHandlerInterceptor(HttpTracing.create(tracing));
+ }
+
+ public static AsyncHandlerInterceptor create(HttpTracing httpTracing) {
+ return new TracingAsyncHandlerInterceptor(httpTracing);
+ }
+
+ final HandlerInterceptor delegate;
+
+ @Autowired TracingAsyncHandlerInterceptor(HttpTracing httpTracing) { // internal
+ delegate = TracingHandlerInterceptor.create(httpTracing);
+ }
+
+ @Override
+ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o)
+ throws Exception {
+ return delegate.preHandle(request, response, o);
+ }
+
+ @Override
+ public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
+ Object o, Exception ex) throws Exception {
+ delegate.afterCompletion(request, response, o, ex);
+ }
+}
diff --git a/instrumentation/spring-webmvc/src/main/java/brave/spring/webmvc/TracingHandlerInterceptor.java b/instrumentation/spring-webmvc/src/main/java/brave/spring/webmvc/TracingHandlerInterceptor.java
index aac46616e2..e06f1cc6f5 100644
--- a/instrumentation/spring-webmvc/src/main/java/brave/spring/webmvc/TracingHandlerInterceptor.java
+++ b/instrumentation/spring-webmvc/src/main/java/brave/spring/webmvc/TracingHandlerInterceptor.java
@@ -14,13 +14,14 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.AsyncHandlerInterceptor;
import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
/**
* Tracing interceptor for Spring Web MVC, which can be used as both an {@link
* AsyncHandlerInterceptor} or a normal {@link HandlerInterceptor}.
*/
-public final class TracingHandlerInterceptor extends HandlerInterceptorAdapter {
+public final class TracingHandlerInterceptor implements HandlerInterceptor {
static final Propagation.Getter GETTER =
new Propagation.Getter() {
@Override public String get(HttpServletRequest carrier, String key) {
@@ -32,11 +33,11 @@ public final class TracingHandlerInterceptor extends HandlerInterceptorAdapter {
}
};
- public static AsyncHandlerInterceptor create(Tracing tracing) {
+ public static HandlerInterceptor create(Tracing tracing) {
return new TracingHandlerInterceptor(HttpTracing.create(tracing));
}
- public static AsyncHandlerInterceptor create(HttpTracing httpTracing) {
+ public static HandlerInterceptor create(HttpTracing httpTracing) {
return new TracingHandlerInterceptor(httpTracing);
}
@@ -61,6 +62,11 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons
return true;
}
+ @Override
+ public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
+ ModelAndView modelAndView) throws Exception {
+ }
+
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object o, Exception ex) {
diff --git a/instrumentation/spring-webmvc/src/test/java/brave/spring/webmvc/ITTracingAsyncHandlerInterceptor.java b/instrumentation/spring-webmvc/src/test/java/brave/spring/webmvc/ITTracingAsyncHandlerInterceptor.java
new file mode 100644
index 0000000000..92727b1575
--- /dev/null
+++ b/instrumentation/spring-webmvc/src/test/java/brave/spring/webmvc/ITTracingAsyncHandlerInterceptor.java
@@ -0,0 +1,99 @@
+package brave.spring.webmvc;
+
+import brave.Tracer;
+import brave.http.HttpTracing;
+import brave.http.ITServletContainer;
+import java.io.IOException;
+import java.util.concurrent.Callable;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
+import org.springframework.web.servlet.AsyncHandlerInterceptor;
+import org.springframework.web.servlet.DispatcherServlet;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+
+public class ITTracingAsyncHandlerInterceptor extends ITServletContainer {
+
+ @Controller static class TestController {
+ final Tracer tracer;
+
+ @Autowired TestController(HttpTracing httpTracing) {
+ this.tracer = httpTracing.tracing().tracer();
+ }
+
+ @RequestMapping(value = "/foo")
+ public ResponseEntity foo() throws IOException {
+ return new ResponseEntity<>(HttpStatus.OK);
+ }
+
+ @RequestMapping(value = "/badrequest")
+ public ResponseEntity badrequest() throws IOException {
+ return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
+ }
+
+ @RequestMapping(value = "/child")
+ public ResponseEntity child() {
+ tracer.nextSpan().name("child").start().finish();
+ return new ResponseEntity<>(HttpStatus.OK);
+ }
+
+ @RequestMapping(value = "/async")
+ public Callable> async() throws IOException {
+ return () -> new ResponseEntity<>(HttpStatus.OK);
+ }
+
+ @RequestMapping(value = "/exception")
+ public ResponseEntity disconnect() throws IOException {
+ throw new IOException();
+ }
+
+ @RequestMapping(value = "/exceptionAsync")
+ public Callable> disconnectAsync() throws IOException {
+ return () -> {
+ throw new IOException();
+ };
+ }
+ }
+
+ @Configuration
+ @EnableWebMvc
+ static class TracingConfig extends WebMvcConfigurerAdapter {
+ @Bean AsyncHandlerInterceptor tracingInterceptor(HttpTracing httpTracing) {
+ return TracingAsyncHandlerInterceptor.create(httpTracing);
+ }
+
+ @Autowired
+ private AsyncHandlerInterceptor tracingInterceptor;
+
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+ registry.addInterceptor(tracingInterceptor);
+ }
+ }
+
+ @Override public void init(ServletContextHandler handler) {
+ AnnotationConfigWebApplicationContext appContext =
+ new AnnotationConfigWebApplicationContext() {
+ // overriding this allows us to register dependencies of TracingHandlerInterceptor
+ // without passing static state to a configuration class.
+ @Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) {
+ beanFactory.registerSingleton("httpTracing", httpTracing);
+ super.loadBeanDefinitions(beanFactory);
+ }
+ };
+
+ appContext.register(TestController.class); // the test resource
+ appContext.register(TracingConfig.class); // generic tracing setup
+ handler.addServlet(new ServletHolder(new DispatcherServlet(appContext)), "/*");
+ }
+}
diff --git a/instrumentation/spring-webmvc/src/test/java/brave/spring/webmvc/ITTracingHandlerInterceptor.java b/instrumentation/spring-webmvc/src/test/java/brave/spring/webmvc/ITTracingHandlerInterceptor.java
index f60089d623..94e08968db 100644
--- a/instrumentation/spring-webmvc/src/test/java/brave/spring/webmvc/ITTracingHandlerInterceptor.java
+++ b/instrumentation/spring-webmvc/src/test/java/brave/spring/webmvc/ITTracingHandlerInterceptor.java
@@ -16,8 +16,8 @@
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
-import org.springframework.web.servlet.AsyncHandlerInterceptor;
import org.springframework.web.servlet.DispatcherServlet;
+import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@@ -68,12 +68,12 @@ public Callable> disconnectAsync() throws IOException {
@Configuration
@EnableWebMvc
static class TracingConfig extends WebMvcConfigurerAdapter {
- @Bean AsyncHandlerInterceptor tracingInterceptor(HttpTracing httpTracing) {
+ @Bean HandlerInterceptor tracingInterceptor(HttpTracing httpTracing) {
return TracingHandlerInterceptor.create(httpTracing);
}
@Autowired
- private AsyncHandlerInterceptor tracingInterceptor;
+ private HandlerInterceptor tracingInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
diff --git a/instrumentation/spring-webmvc/src/test/java/brave/spring/webmvc/TracingAsyncHandlerInterceptorAutowireTest.java b/instrumentation/spring-webmvc/src/test/java/brave/spring/webmvc/TracingAsyncHandlerInterceptorAutowireTest.java
new file mode 100644
index 0000000000..d0a6d2a7a8
--- /dev/null
+++ b/instrumentation/spring-webmvc/src/test/java/brave/spring/webmvc/TracingAsyncHandlerInterceptorAutowireTest.java
@@ -0,0 +1,35 @@
+package brave.spring.webmvc;
+
+import brave.Tracing;
+import brave.http.HttpTracing;
+import org.junit.Test;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+public class TracingAsyncHandlerInterceptorAutowireTest {
+
+ @Configuration static class HttpTracingConfiguration {
+ @Bean HttpTracing httpTracing() {
+ return HttpTracing.create(Tracing.newBuilder().build());
+ }
+ }
+
+ // NOTE: while bean configuration via @Import works with Spring 4, it does not with Spring 3
+ @Configuration @Import(HttpTracingConfiguration.class)
+ static class BeanConfiguration {
+ @Bean HandlerInterceptor tracingInterceptor(HttpTracing httpTracing) {
+ return TracingAsyncHandlerInterceptor.create(httpTracing);
+ }
+ }
+
+ @Test public void autowiredWithBeanConfig() {
+ AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
+ ctx.register(BeanConfiguration.class);
+ ctx.refresh();
+
+ ctx.getBean(HandlerInterceptor.class);
+ }
+}