diff --git a/instrumentation/dropwizard/dropwizard-testing/src/test/groovy/DropwizardAsyncTest.groovy b/instrumentation/dropwizard/dropwizard-testing/src/test/groovy/DropwizardAsyncTest.groovy deleted file mode 100644 index 8333aee4af42..000000000000 --- a/instrumentation/dropwizard/dropwizard-testing/src/test/groovy/DropwizardAsyncTest.groovy +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.dropwizard.Application -import io.dropwizard.Configuration -import io.dropwizard.setup.Bootstrap -import io.dropwizard.setup.Environment - -import javax.ws.rs.GET -import javax.ws.rs.HeaderParam -import javax.ws.rs.Path -import javax.ws.rs.PathParam -import javax.ws.rs.QueryParam -import javax.ws.rs.container.AsyncResponse -import javax.ws.rs.container.Suspended -import javax.ws.rs.core.Response -import java.util.concurrent.Executors - -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.PATH_PARAM -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS - -class DropwizardAsyncTest extends DropwizardTest { - - Class testApp() { - AsyncTestApp - } - - Class testResource() { - AsyncServiceResource - } - - @Override - boolean verifyServerSpanEndTime() { - // server spans are ended inside of the JAX-RS controller spans - return false - } - - static class AsyncTestApp extends Application { - @Override - void initialize(Bootstrap bootstrap) { - } - - @Override - void run(Configuration configuration, Environment environment) { - environment.jersey().register(AsyncServiceResource) - } - } - - @Path("/") - static class AsyncServiceResource { - final executor = Executors.newSingleThreadExecutor() - - @GET - @Path("success") - void success(@Suspended final AsyncResponse asyncResponse) { - executor.execute { - controller(SUCCESS) { - asyncResponse.resume(Response.status(SUCCESS.status).entity(SUCCESS.body).build()) - } - } - } - - @GET - @Path("query") - Response query_param(@QueryParam("some") String param, @Suspended final AsyncResponse asyncResponse) { - executor.execute { - controller(QUERY_PARAM) { - asyncResponse.resume(Response.status(QUERY_PARAM.status).entity("some=$param".toString()).build()) - } - } - } - - @GET - @Path("redirect") - void redirect(@Suspended final AsyncResponse asyncResponse) { - executor.execute { - controller(REDIRECT) { - asyncResponse.resume(Response.status(REDIRECT.status).location(new URI(REDIRECT.body)).build()) - } - } - } - - @GET - @Path("error-status") - void error(@Suspended final AsyncResponse asyncResponse) { - executor.execute { - controller(ERROR) { - asyncResponse.resume(Response.status(ERROR.status).entity(ERROR.body).build()) - } - } - } - - @GET - @Path("exception") - void exception(@Suspended final AsyncResponse asyncResponse) { - executor.execute { - controller(EXCEPTION) { - def ex = new Exception(EXCEPTION.body) - asyncResponse.resume(ex) - throw ex - } - } - } - - @GET - @Path("path/{id}/param") - void path_param(@PathParam("id") int param, @Suspended final AsyncResponse asyncResponse) { - executor.execute { - controller(PATH_PARAM) { - asyncResponse.resume(Response.status(PATH_PARAM.status).entity(param.toString()).build()) - } - } - } - - @GET - @Path("child") - void indexed_child(@QueryParam("id") String param, @Suspended final AsyncResponse asyncResponse) { - controller(INDEXED_CHILD) { - INDEXED_CHILD.collectSpanAttributes { it == "id" ? param : null } - asyncResponse.resume(Response.status(INDEXED_CHILD.status).entity(INDEXED_CHILD.body).build()) - } - } - - @GET - @Path("captureHeaders") - void capture_headers(@HeaderParam("X-Test-Request") String header, - @Suspended final AsyncResponse asyncResponse) { - controller(CAPTURE_HEADERS) { - asyncResponse.resume(Response.status(CAPTURE_HEADERS.status) - .header("X-Test-Response", header) - .entity(CAPTURE_HEADERS.body) - .build()) - } - } - } -} diff --git a/instrumentation/dropwizard/dropwizard-testing/src/test/groovy/DropwizardTest.groovy b/instrumentation/dropwizard/dropwizard-testing/src/test/groovy/DropwizardTest.groovy deleted file mode 100644 index 197d6181b2cb..000000000000 --- a/instrumentation/dropwizard/dropwizard-testing/src/test/groovy/DropwizardTest.groovy +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.dropwizard.Application -import io.dropwizard.Configuration -import io.dropwizard.setup.Bootstrap -import io.dropwizard.setup.Environment -import io.dropwizard.testing.ConfigOverride -import io.dropwizard.testing.DropwizardTestSupport -import io.opentelemetry.api.trace.StatusCode -import io.opentelemetry.instrumentation.api.internal.HttpConstants -import io.opentelemetry.instrumentation.test.AgentTestTrait -import io.opentelemetry.instrumentation.test.asserts.TraceAssert -import io.opentelemetry.instrumentation.test.base.HttpServerTest -import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint -import io.opentelemetry.instrumentation.test.utils.PortUtils -import io.opentelemetry.sdk.trace.data.SpanData - -import javax.ws.rs.GET -import javax.ws.rs.HeaderParam -import javax.ws.rs.Path -import javax.ws.rs.PathParam -import javax.ws.rs.QueryParam -import javax.ws.rs.core.Response - -import static io.opentelemetry.api.trace.SpanKind.INTERNAL -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.NOT_FOUND -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.PATH_PARAM -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS - -class DropwizardTest extends HttpServerTest implements AgentTestTrait { - - @Override - DropwizardTestSupport startServer(int port) { - println "Port: $port" - def testSupport = new DropwizardTestSupport(testApp(), - null, - ConfigOverride.config("server.applicationConnectors[0].port", "$port"), - ConfigOverride.config("server.adminConnectors[0].port", PortUtils.findOpenPort().toString())) - testSupport.before() - return testSupport - } - - Class testApp() { - TestApp - } - - Class testResource() { - ServiceResource - } - - @Override - void stopServer(DropwizardTestSupport testSupport) { - testSupport.after() - } - - // this override is needed because dropwizard reports peer ip as the client ip - @Override - String sockPeerAddr(ServerEndpoint endpoint) { - TEST_CLIENT_IP - } - - @Override - boolean hasHandlerSpan(ServerEndpoint endpoint) { - endpoint != NOT_FOUND - } - - @Override - boolean hasResponseSpan(ServerEndpoint endpoint) { - endpoint == NOT_FOUND - } - - @Override - boolean testPathParam() { - true - } - - @Override - String expectedHttpRoute(ServerEndpoint endpoint, String method) { - if (method == HttpConstants._OTHER) { - return getContextPath() + "/*" - } - switch (endpoint) { - case NOT_FOUND: - return getContextPath() + "/*" - case PATH_PARAM: - return getContextPath() + "/path/{id}/param" - default: - return super.expectedHttpRoute(endpoint, method) - } - } - - @Override - int getResponseCodeOnNonStandardHttpMethod() { - 405 - } - - @Override - void handlerSpan(TraceAssert trace, int index, Object parent, String method = "GET", ServerEndpoint endpoint = SUCCESS) { - trace.span(index) { - name "${this.testResource().simpleName}.${endpoint.name().toLowerCase()}" - kind INTERNAL - if (endpoint == EXCEPTION) { - status StatusCode.ERROR - errorEvent(Exception, EXCEPTION.body) - } - childOf((SpanData) parent) - } - } - - @Override - void responseSpan(TraceAssert trace, int index, Object parent, String method, ServerEndpoint endpoint) { - sendErrorSpan(trace, index, parent) - } - - static class TestApp extends Application { - @Override - void initialize(Bootstrap bootstrap) { - } - - @Override - void run(Configuration configuration, Environment environment) { - environment.jersey().register(ServiceResource) - } - } - - @Path("/ignored1") - static interface TestInterface {} - - @Path("/ignored2") - static abstract class AbstractClass implements TestInterface { - - @GET - @Path("success") - Response success() { - controller(SUCCESS) { - Response.status(SUCCESS.status).entity(SUCCESS.body).build() - } - } - - @GET - @Path("query") - Response query_param(@QueryParam("some") String param) { - controller(QUERY_PARAM) { - Response.status(QUERY_PARAM.status).entity("some=$param".toString()).build() - } - } - - @GET - @Path("redirect") - Response redirect() { - controller(REDIRECT) { - Response.status(REDIRECT.status).location(new URI(REDIRECT.body)).build() - } - } - } - - @Path("/ignored3") - static class ParentClass extends AbstractClass { - - @GET - @Path("error-status") - Response error() { - controller(ERROR) { - Response.status(ERROR.status).entity(ERROR.body).build() - } - } - - @GET - @Path("exception") - Response exception() { - controller(EXCEPTION) { - throw new Exception(EXCEPTION.body) - } - return null - } - - @GET - @Path("path/{id}/param") - Response path_param(@PathParam("id") int param) { - controller(PATH_PARAM) { - Response.status(PATH_PARAM.status).entity(param.toString()).build() - } - } - - @GET - @Path("child") - Response indexed_child(@QueryParam("id") String param) { - controller(INDEXED_CHILD) { - INDEXED_CHILD.collectSpanAttributes { it == "id" ? param : null } - Response.status(INDEXED_CHILD.status).entity(INDEXED_CHILD.body).build() - } - } - - @GET - @Path("captureHeaders") - Response capture_headers(@HeaderParam("X-Test-Request") String header) { - controller(CAPTURE_HEADERS) { - Response.status(CAPTURE_HEADERS.status) - .header("X-Test-Response", header) - .entity(CAPTURE_HEADERS.body) - .build() - } - } - } - - @Path("/") - static class ServiceResource extends ParentClass {} -} diff --git a/instrumentation/dropwizard/dropwizard-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/dropwizard/DropwizardAsyncTest.java b/instrumentation/dropwizard/dropwizard-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/dropwizard/DropwizardAsyncTest.java new file mode 100644 index 000000000000..c94fcb84ba6e --- /dev/null +++ b/instrumentation/dropwizard/dropwizard-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/dropwizard/DropwizardAsyncTest.java @@ -0,0 +1,179 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.dropwizard; + +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.PATH_PARAM; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS; + +import io.dropwizard.Application; +import io.dropwizard.Configuration; +import io.dropwizard.setup.Bootstrap; +import io.dropwizard.setup.Environment; +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.QueryParam; +import javax.ws.rs.container.AsyncResponse; +import javax.ws.rs.container.Suspended; +import javax.ws.rs.core.Response; + +class DropwizardAsyncTest extends DropwizardTest { + + @Override + Class> testApp() { + return AsyncTestApp.class; + } + + @Override + Class testResource() { + return AsyncServiceResource.class; + } + + @Override + protected void configure(HttpServerTestOptions options) { + super.configure(options); + // server spans are ended inside the JAX-RS controller spans + options.setVerifyServerSpanEndTime(false); + } + + public static class AsyncTestApp extends Application { + @Override + public void initialize(Bootstrap bootstrap) {} + + @Override + public void run(Configuration configuration, Environment environment) { + environment.jersey().register(AsyncServiceResource.class); + } + } + + @Path("/") + public static class AsyncServiceResource { + final ExecutorService executor = Executors.newSingleThreadExecutor(); + + @GET + @Path("success") + public void success(@Suspended AsyncResponse asyncResponse) { + executor.execute( + () -> + controller( + SUCCESS, + () -> + asyncResponse.resume( + Response.status(SUCCESS.getStatus()).entity(SUCCESS.getBody()).build()))); + } + + @GET + @Path("query") + public void queryParam( + @QueryParam("some") String param, @Suspended AsyncResponse asyncResponse) { + executor.execute( + () -> + controller( + QUERY_PARAM, + () -> + asyncResponse.resume( + Response.status(QUERY_PARAM.getStatus()) + .entity("some=" + param) + .build()))); + } + + @GET + @Path("redirect") + public void redirect(@Suspended AsyncResponse asyncResponse) { + executor.execute( + () -> { + try { + controller( + REDIRECT, + () -> + asyncResponse.resume( + Response.status(REDIRECT.getStatus()) + .location(new URI(REDIRECT.getBody())) + .build())); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + }); + } + + @GET + @Path("error-status") + public void error(@Suspended AsyncResponse asyncResponse) { + executor.execute( + () -> + controller( + ERROR, + () -> + asyncResponse.resume( + Response.status(ERROR.getStatus()).entity(ERROR.getBody()).build()))); + } + + @GET + @Path("exception") + public void exception(@Suspended AsyncResponse asyncResponse) { + executor.execute( + () -> + controller( + EXCEPTION, + () -> { + IllegalStateException ex = new IllegalStateException(EXCEPTION.getBody()); + asyncResponse.resume(ex); + throw ex; + })); + } + + @GET + @Path("path/{id}/param") + public void pathParam(@PathParam("id") int param, @Suspended AsyncResponse asyncResponse) { + executor.execute( + () -> + controller( + PATH_PARAM, + () -> + asyncResponse.resume( + Response.status(PATH_PARAM.getStatus()).entity(param).build()))); + } + + @GET + @Path("child") + public void indexedChild( + @QueryParam("id") String param, @Suspended AsyncResponse asyncResponse) { + controller( + INDEXED_CHILD, + () -> { + INDEXED_CHILD.collectSpanAttributes(id -> id.equals("id") ? param : null); + asyncResponse.resume( + Response.status(INDEXED_CHILD.getStatus()).entity(INDEXED_CHILD.getBody()).build()); + }); + } + + @GET + @Path("captureHeaders") + public void captureHeaders( + @HeaderParam("X-Test-Request") String header, @Suspended AsyncResponse asyncResponse) { + controller( + CAPTURE_HEADERS, + () -> + asyncResponse.resume( + Response.status(CAPTURE_HEADERS.getStatus()) + .header("X-Test-Response", header) + .entity(CAPTURE_HEADERS.getBody()) + .build())); + } + } +} diff --git a/instrumentation/dropwizard/dropwizard-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/dropwizard/DropwizardTest.java b/instrumentation/dropwizard/dropwizard-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/dropwizard/DropwizardTest.java new file mode 100644 index 000000000000..2a01f94d8fe7 --- /dev/null +++ b/instrumentation/dropwizard/dropwizard-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/dropwizard/DropwizardTest.java @@ -0,0 +1,237 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.dropwizard; + +import static io.opentelemetry.api.trace.SpanKind.INTERNAL; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.NOT_FOUND; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.PATH_PARAM; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS; +import static org.assertj.core.api.Assertions.assertThat; + +import io.dropwizard.Application; +import io.dropwizard.Configuration; +import io.dropwizard.setup.Bootstrap; +import io.dropwizard.setup.Environment; +import io.dropwizard.testing.ConfigOverride; +import io.dropwizard.testing.DropwizardTestSupport; +import io.opentelemetry.instrumentation.api.internal.HttpConstants; +import io.opentelemetry.instrumentation.test.utils.PortUtils; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest; +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions; +import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint; +import io.opentelemetry.sdk.testing.assertj.SpanDataAssert; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.sdk.trace.data.StatusData; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Locale; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.Response; +import org.junit.jupiter.api.extension.RegisterExtension; + +class DropwizardTest extends AbstractHttpServerTest> { + + @RegisterExtension + static final InstrumentationExtension testing = HttpServerInstrumentationExtension.forAgent(); + + @Override + protected DropwizardTestSupport setupServer() { + DropwizardTestSupport testSupport = + new DropwizardTestSupport<>( + testApp(), + null, + ConfigOverride.config("server.applicationConnectors[0].port", String.valueOf(port)), + ConfigOverride.config( + "server.adminConnectors[0].port", String.valueOf(PortUtils.findOpenPort()))); + testSupport.before(); + return testSupport; + } + + @Override + protected void stopServer(DropwizardTestSupport server) { + server.after(); + } + + @Override + protected void configure(HttpServerTestOptions options) { + super.configure(options); + // this override is needed because dropwizard reports peer ip as the client ip + options.setSockPeerAddr(serverEndpoint -> TEST_CLIENT_IP); + options.setHasHandlerSpan(endpoint -> endpoint != NOT_FOUND); + options.setHasResponseSpan(endpoint -> endpoint == NOT_FOUND); + options.setTestPathParam(true); + options.setResponseCodeOnNonStandardHttpMethod(405); + options.setExpectedException(new IllegalStateException(EXCEPTION.getBody())); + + options.setExpectedHttpRoute( + (endpoint, method) -> { + if (HttpConstants._OTHER.equals(method)) { + return getContextPath() + "/*"; + } + + if (NOT_FOUND.equals(endpoint)) { + return getContextPath() + "/*"; + } else if (PATH_PARAM.equals(endpoint)) { + return getContextPath() + "/path/{id}/param"; + } else { + return super.expectedHttpRoute(endpoint, method); + } + }); + } + + Class> testApp() { + return TestApp.class; + } + + Class testResource() { + return ServiceResource.class; + } + + @Override + protected SpanDataAssert assertHandlerSpan( + SpanDataAssert span, String method, ServerEndpoint endpoint) { + span.hasName(testResource().getSimpleName() + "." + getEndpointName(endpoint)) + .hasKind(INTERNAL); + if (EXCEPTION.equals(endpoint)) { + span.hasStatus(StatusData.error()) + .hasException(new IllegalStateException(EXCEPTION.getBody())); + } + return span; + } + + private static String getEndpointName(ServerEndpoint endpoint) { + if (QUERY_PARAM.equals(endpoint)) { + return "queryParam"; + } else if (PATH_PARAM.equals(endpoint)) { + return "pathParam"; + } else if (CAPTURE_HEADERS.equals(endpoint)) { + return "captureHeaders"; + } else if (INDEXED_CHILD.equals(endpoint)) { + return "indexedChild"; + } + return endpoint.name().toLowerCase(Locale.ROOT); + } + + @Override + protected SpanDataAssert assertResponseSpan( + SpanDataAssert span, SpanData parentSpan, String method, ServerEndpoint endpoint) { + span.satisfies(spanData -> assertThat(spanData.getName()).endsWith(".sendError")) + .hasKind(INTERNAL); + return span; + } + + public static class TestApp extends Application { + public TestApp() {} + + @Override + public void initialize(Bootstrap bootstrap) {} + + @Override + public void run(Configuration configuration, Environment environment) { + environment.jersey().register(ServiceResource.class); + } + } + + @Path("/ignored1") + interface TestInterface {} + + @Path("/ignored2") + public abstract static class AbstractClass implements TestInterface { + + @GET + @Path("success") + public Response success() { + return controller( + SUCCESS, () -> Response.status(SUCCESS.getStatus()).entity(SUCCESS.getBody()).build()); + } + + @GET + @Path("query") + public Response queryParam(@QueryParam("some") String param) { + return controller( + QUERY_PARAM, + () -> Response.status(QUERY_PARAM.getStatus()).entity("some=" + param).build()); + } + + @GET + @Path("redirect") + public Response redirect() throws URISyntaxException { + return controller( + REDIRECT, + () -> + Response.status(REDIRECT.getStatus()).location(new URI(REDIRECT.getBody())).build()); + } + } + + @Path("/ignored3") + public static class ParentClass extends AbstractClass { + + @GET + @Path("error-status") + public Response error() { + return controller( + ERROR, () -> Response.status(ERROR.getStatus()).entity(ERROR.getBody()).build()); + } + + @GET + @Path("exception") + public Response exception() throws Exception { + return controller( + EXCEPTION, + () -> { + throw new IllegalStateException(EXCEPTION.getBody()); + }); + } + + @GET + @Path("path/{id}/param") + public Response pathParam(@PathParam("id") int param) { + return controller( + PATH_PARAM, + () -> Response.status(PATH_PARAM.getStatus()).entity(String.valueOf(param)).build()); + } + + @GET + @Path("child") + public Response indexedChild(@QueryParam("id") String param) { + return controller( + INDEXED_CHILD, + () -> { + INDEXED_CHILD.collectSpanAttributes(id -> id.equals("id") ? param : null); + return Response.status(INDEXED_CHILD.getStatus()) + .entity(INDEXED_CHILD.getBody()) + .build(); + }); + } + + @GET + @Path("captureHeaders") + public Response captureHeaders(@HeaderParam("X-Test-Request") String header) { + return controller( + CAPTURE_HEADERS, + () -> + Response.status(CAPTURE_HEADERS.getStatus()) + .header("X-Test-Response", header) + .entity(CAPTURE_HEADERS.getBody()) + .build()); + } + } + + @Path("/") + public static class ServiceResource extends ParentClass {} +}