package com.github.kristofa.brave.spring; import com.github.kristofa.brave.Brave; import com.github.kristofa.brave.LocalTracer; import com.github.kristofa.brave.http.ITServletContainer; import com.github.kristofa.brave.http.SpanNameProvider; import java.io.IOException; import java.util.concurrent.Callable; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.junit.AssumptionViolatedException; import org.junit.Test; 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.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 ITServletHandlerInterceptor extends ITServletContainer { @Override @Test public void addsStatusCodeWhenNotOk() throws Exception { throw new AssumptionViolatedException("TODO: fix error reporting"); } @Controller static class TestController { final LocalTracer localTracer; @Autowired TestController(Brave brave) { this.localTracer = brave.localTracer(); } @RequestMapping(value = "/foo") public ResponseEntity<Void> foo() throws IOException { return new ResponseEntity<>(HttpStatus.OK); } @RequestMapping(value = "/child") public ResponseEntity<Void> child() { localTracer.startNewSpan("child", "child"); localTracer.finishSpan(); return new ResponseEntity<>(HttpStatus.OK); } @RequestMapping(value = "/childAsync") public Callable<ResponseEntity<Void>> childAsync() throws IOException { return () -> { localTracer.startNewSpan("child", "child"); localTracer.finishSpan(); return new ResponseEntity<>(HttpStatus.OK); }; } @RequestMapping(value = "/disconnect") public ResponseEntity<Void> disconnect() throws IOException { throw new IOException(); } @RequestMapping(value = "/disconnectAsync") public Callable<ResponseEntity<Void>> disconnectAsync() throws IOException { return () -> { throw new IOException(); }; } } @Configuration @EnableWebMvc static class TracingConfig extends WebMvcConfigurerAdapter { @Bean ServletHandlerInterceptor tracingInterceptor(SpanNameProvider spanNameProvider, Brave brave) { return ServletHandlerInterceptor.builder(brave).spanNameProvider(spanNameProvider).build(); } @Autowired private ServletHandlerInterceptor interceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(interceptor); } } @Override public void init(ServletContextHandler handler, Brave brave, SpanNameProvider spanNameProvider) { AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext() { // overriding this allows us to register dependencies of ServletHandlerInterceptor // without passing static state to a configuration class. @Override protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) { beanFactory.registerSingleton("brave", brave); beanFactory.registerSingleton("spanNameProvider", spanNameProvider); super.loadBeanDefinitions(beanFactory); } }; appContext.register(TestController.class); // the test resource appContext.register(TracingConfig.class); // generic tracing setup handler.addServlet(new ServletHolder(new DispatcherServlet(appContext)), "/*"); } }