package com.github.kristofa.brave.resteasy3;
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 javax.servlet.ServletContext;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.container.AsyncResponse;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.container.Suspended;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher;
import org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap;
import org.jboss.resteasy.plugins.spring.SpringContextLoaderListener;
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.web.context.support.AnnotationConfigWebApplicationContext;
public class ITBraveTracingFeature_Server extends ITServletContainer {
@Override @Test public void reportsClientAddress() {
throw new AssumptionViolatedException("ContainerRequestContext doesn't include remote address");
}
/**
* {@link ContainerResponseFilter} has no means to handle uncaught exceptions. Unless you provide
* a catch-all exception mapper, requests that result in unhandled exceptions will leak until they
* are eventually flushed.
*/
@Provider
public static class CatchAllExceptions implements ExceptionMapper<Exception> {
@Override
public Response toResponse(Exception e) {
if (e instanceof WebApplicationException) {
return ((WebApplicationException) e).getResponse();
}
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Internal error")
.type("text/plain")
.build();
}
}
@Path("")
public static class TestResource {
final LocalTracer localTracer;
@Autowired
public TestResource(Brave brave) {
this.localTracer = brave.localTracer();
}
@GET
@Path("foo")
public Response get() {
return Response.status(200).build();
}
@GET
@Path("child")
public Response child() {
localTracer.startNewSpan("child", "child");
localTracer.finishSpan();
return Response.status(200).build();
}
@GET
@Path("childAsync")
public void childAsync(@Suspended AsyncResponse response) throws IOException {
new Thread(() -> {
localTracer.startNewSpan("child", "child");
localTracer.finishSpan();
response.resume(Response.status(200).build());
}).start();
}
@GET
@Path("disconnect")
public Response disconnect() throws IOException {
throw new IOException();
}
@GET
@Path("disconnectAsync")
public void disconnectAsync(@Suspended AsyncResponse response) throws IOException {
new Thread(() ->{
response.resume(new IOException());
}).start();
}
}
@Override
public void init(ServletContextHandler handler, Brave brave, SpanNameProvider spanNameProvider) {
AnnotationConfigWebApplicationContext appContext =
new AnnotationConfigWebApplicationContext() {
// overriding this allows us to register dependencies of BraveTracingFeatureConfiguration
// 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(TestResource.class); // the test resource
appContext.register(CatchAllExceptions.class);
appContext.register(BraveTracingFeatureConfiguration.class); // generic tracing setup
// resteasy + spring configuration, programmatically as opposed to using web.xml
handler.addServlet(new ServletHolder(new HttpServletDispatcher()), "/*");
handler.addEventListener(new ResteasyBootstrap());
handler.addEventListener(new SpringContextLoaderListener(appContext));
}
}