diff --git a/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/AsyncServlet.groovy b/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/AsyncServlet.groovy deleted file mode 100644 index 8e27dcc8ce..0000000000 --- a/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/AsyncServlet.groovy +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.tomcat.v7_0 - -import io.opentelemetry.instrumentation.test.base.HttpServerTest -import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint -import javax.servlet.ServletException -import javax.servlet.annotation.WebServlet -import javax.servlet.http.HttpServlet -import javax.servlet.http.HttpServletRequest -import javax.servlet.http.HttpServletResponse -import java.util.concurrent.CountDownLatch - -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.QUERY_PARAM -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS - -@WebServlet(asyncSupported = true) -class AsyncServlet extends HttpServlet { - @Override - protected void service(HttpServletRequest req, HttpServletResponse resp) { - ServerEndpoint endpoint = ServerEndpoint.forPath(req.servletPath) - def latch = new CountDownLatch(1) - def context = req.startAsync() - if (endpoint == EXCEPTION) { - context.setTimeout(5000) - } - context.start { - try { - HttpServerTest.controller(endpoint) { - resp.contentType = "text/plain" - switch (endpoint) { - case SUCCESS: - resp.status = endpoint.status - resp.writer.print(endpoint.body) - break - case INDEXED_CHILD: - endpoint.collectSpanAttributes { req.getParameter(it) } - resp.status = endpoint.status - break - case QUERY_PARAM: - resp.status = endpoint.status - resp.writer.print(req.queryString) - break - case REDIRECT: - resp.sendRedirect(endpoint.body) - break - case CAPTURE_HEADERS: - resp.setHeader("X-Test-Response", req.getHeader("X-Test-Request")) - resp.status = endpoint.status - resp.writer.print(endpoint.body) - break - case ERROR: - resp.status = endpoint.status - resp.writer.print(endpoint.body) - break - case EXCEPTION: - resp.status = endpoint.status - def writer = resp.writer - writer.print(endpoint.body) - writer.close() - throw new ServletException(endpoint.body) - } - } - } finally { - // complete at the end so the server span will end after the controller span - if (endpoint != EXCEPTION) { - context.complete() - } - latch.countDown() - } - } - latch.await() - } -} diff --git a/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/ThreadPoolExecutorTest.groovy b/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/ThreadPoolExecutorTest.groovy deleted file mode 100644 index 517b8ad71e..0000000000 --- a/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/ThreadPoolExecutorTest.groovy +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.tomcat.v7_0 - -import io.opentelemetry.api.trace.SpanKind -import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification -import org.apache.tomcat.util.threads.TaskQueue -import org.apache.tomcat.util.threads.ThreadPoolExecutor - -import java.util.concurrent.CountDownLatch -import java.util.concurrent.TimeUnit -import java.util.concurrent.atomic.AtomicBoolean - -class ThreadPoolExecutorTest extends AgentInstrumentationSpecification { - - // Test that PropagatedContext isn't cleared when ThreadPoolExecutor.execute fails with - // RejectedExecutionException - def "test tomcat thread pool"() { - setup: - def reject = new AtomicBoolean() - def queue = new TaskQueue() { - @Override - boolean offer(Runnable o) { - // TaskQueue.offer returns false when parent.getPoolSize() < parent.getMaximumPoolSize() - // here we simulate the same condition to trigger RejectedExecutionException handling in - // tomcat ThreadPoolExecutor - if (reject.get()) { - reject.set(false) - return false - } - return super.offer(o) - } - } - def pool = new ThreadPoolExecutor(1, 1, 0, TimeUnit.MILLISECONDS, queue) - queue.setParent(pool) - - CountDownLatch latch = new CountDownLatch(1) - - runWithSpan("parent") { - pool.execute(new Runnable() { - @Override - void run() { - runWithSpan("child1") { - latch.await() - } - } - }) - - reject.set(true) - pool.execute(new Runnable() { - @Override - void run() { - runWithSpan("child2") { - latch.await() - } - } - }) - } - - latch.countDown() - - expect: - assertTraces(1) { - trace(0, 3) { - span(0) { - name "parent" - kind SpanKind.INTERNAL - hasNoParent() - } - span(1) { - name "child1" - kind SpanKind.INTERNAL - childOf span(0) - } - span(2) { - name "child2" - kind SpanKind.INTERNAL - childOf span(0) - } - } - } - - cleanup: - pool.shutdown() - pool.awaitTermination(10, TimeUnit.SECONDS) - } -} diff --git a/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TomcatAsyncTest.groovy b/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TomcatAsyncTest.groovy deleted file mode 100644 index 1db9939430..0000000000 --- a/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TomcatAsyncTest.groovy +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.tomcat.v7_0 - -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 org.apache.catalina.Context -import org.apache.catalina.startup.Tomcat -import org.apache.tomcat.JarScanFilter -import org.apache.tomcat.JarScanType -import spock.lang.Unroll - -import javax.servlet.Servlet -import javax.servlet.ServletException -import java.nio.file.Files - -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.AUTH_REQUIRED -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.QUERY_PARAM -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS - -@Unroll -class TomcatAsyncTest extends HttpServerTest implements AgentTestTrait { - - @Override - Tomcat startServer(int port) { - def tomcatServer = new Tomcat() - - def baseDir = Files.createTempDirectory("tomcat").toFile() - baseDir.deleteOnExit() - tomcatServer.setBaseDir(baseDir.getAbsolutePath()) - - tomcatServer.setPort(port) - tomcatServer.getConnector().enableLookups = true // get localhost instead of 127.0.0.1 - - File applicationDir = new File(baseDir, "/webapps/ROOT") - if (!applicationDir.exists()) { - applicationDir.mkdirs() - applicationDir.deleteOnExit() - } - Context servletContext = tomcatServer.addWebapp(contextPath, applicationDir.getAbsolutePath()) - // Speed up startup by disabling jar scanning: - servletContext.getJarScanner().setJarScanFilter(new JarScanFilter() { - @Override - boolean check(JarScanType jarScanType, String jarName) { - return false - } - }) - - setupServlets(servletContext) - - tomcatServer.start() - - return tomcatServer - } - - @Override - void stopServer(Tomcat server) { - server.stop() - server.destroy() - } - - @Override - String getContextPath() { - return "/tomcat-context" - } - - @Override - String getMetricsInstrumentationName() { - // with async requests the span is started in one instrumentation (server instrumentation) - // but ended from another (servlet instrumentation) - "io.opentelemetry.servlet-3.0" - } - - protected void setupServlets(Context context) { - def servlet = servlet() - - addServlet(context, SUCCESS.path, servlet) - addServlet(context, QUERY_PARAM.path, servlet) - addServlet(context, ERROR.path, servlet) - addServlet(context, EXCEPTION.path, servlet) - addServlet(context, REDIRECT.path, servlet) - addServlet(context, AUTH_REQUIRED.path, servlet) - addServlet(context, CAPTURE_HEADERS.path, servlet) - addServlet(context, INDEXED_CHILD.path, servlet) - } - - void addServlet(Context servletContext, String path, Class servlet) { - String name = UUID.randomUUID() - Tomcat.addServlet(servletContext, name, servlet.newInstance()) - servletContext.addServletMappingDecoded(path, name) - } - - Class servlet() { - AsyncServlet - } - - @Override - String expectedHttpRoute(ServerEndpoint endpoint, String method) { - if (method == HttpConstants._OTHER) { - return getContextPath() + endpoint.path - } - switch (endpoint) { - case NOT_FOUND: - return getContextPath() + "/*" - default: - return super.expectedHttpRoute(endpoint, method) - } - } - - @Override - Throwable expectedException() { - new ServletException(EXCEPTION.body) - } - - @Override - boolean hasResponseSpan(ServerEndpoint endpoint) { - endpoint == NOT_FOUND || endpoint == REDIRECT - } - - @Override - void responseSpan(TraceAssert trace, int index, Object parent, String method, ServerEndpoint endpoint) { - switch (endpoint) { - case REDIRECT: - redirectSpan(trace, index, parent) - break - case NOT_FOUND: - sendErrorSpan(trace, index, parent) - break - } - } -} diff --git a/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TomcatHandlerTest.groovy b/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TomcatHandlerTest.groovy deleted file mode 100644 index 586ae411d7..0000000000 --- a/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TomcatHandlerTest.groovy +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.javaagent.instrumentation.tomcat.v7_0 - -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 org.apache.catalina.Context -import org.apache.catalina.connector.Request -import org.apache.catalina.connector.Response -import org.apache.catalina.core.StandardHost -import org.apache.catalina.startup.Tomcat -import org.apache.catalina.valves.ErrorReportValve - -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.AUTH_ERROR -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.AUTH_REQUIRED -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_PARAMETERS -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.LOGIN -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 TomcatHandlerTest extends HttpServerTest implements AgentTestTrait { - - private static final List serverEndpointsList = Arrays.asList(SUCCESS, REDIRECT, ERROR, EXCEPTION, NOT_FOUND, CAPTURE_HEADERS, CAPTURE_PARAMETERS, QUERY_PARAM, PATH_PARAM, AUTH_REQUIRED, LOGIN, AUTH_ERROR, INDEXED_CHILD) - - def "Tomcat starts"() { - expect: - getServer() != null - } - - @Override - String getContextPath() { - return "/app" - } - - @Override - boolean hasResponseCustomizer(ServerEndpoint endpoint) { - true - } - - @Override - boolean testCapturedRequestParameters() { - true - } - - @Override - String expectedHttpRoute(ServerEndpoint endpoint, String method) { - if (method == HttpConstants._OTHER) { - return getContextPath() + endpoint.path - } - return super.expectedHttpRoute(endpoint, method) - } - - @Override - Tomcat startServer(int port) { - Tomcat tomcat = new Tomcat() - tomcat.setBaseDir(File.createTempDir().absolutePath) - tomcat.setPort(port) - tomcat.getConnector() - - Context ctx = tomcat.addContext(getContextPath(), new File(".").getAbsolutePath()) - - Tomcat.addServlet(ctx, "testServlet", new TestServlet()) - - // Mapping servlet to /* will result in all requests have a name of just a context. - serverEndpointsList.stream() - .filter { it != NOT_FOUND } - .forEach { - ctx.addServletMappingDecoded(it.path, "testServlet") - } - - (tomcat.host as StandardHost).errorReportValveClass = ErrorHandlerValve.name - - tomcat.start() - - return tomcat - } - - @Override - void stopServer(Tomcat tomcat) { - tomcat.getServer().stop() - } - - @Override - boolean hasResponseSpan(ServerEndpoint endpoint) { - endpoint == REDIRECT || endpoint == ERROR || endpoint == NOT_FOUND - } - - @Override - void responseSpan(TraceAssert trace, int index, Object parent, String method, ServerEndpoint endpoint) { - switch (endpoint) { - case REDIRECT: - redirectSpan(trace, index, parent) - break - case ERROR: - case NOT_FOUND: - sendErrorSpan(trace, index, parent) - break - } - } -} - -class ErrorHandlerValve extends ErrorReportValve { - @Override - protected void report(Request request, Response response, Throwable t) { - if (response.getStatus() < 400 || response.getContentWritten() > 0 || !response.isError()) { - return - } - try { - response.writer.print(t ? t.cause.message : response.message) - } catch (IOException ignored) { - // Ignore exception when writing exception message to response fails on IO - same as is done - // by the superclass itself and by other built-in ErrorReportValve implementations. - } - } -} diff --git a/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/AsyncServlet.java b/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/AsyncServlet.java new file mode 100644 index 0000000000..16cc92a906 --- /dev/null +++ b/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/AsyncServlet.java @@ -0,0 +1,82 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.tomcat.v7_0; + +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.QUERY_PARAM; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS; + +import io.opentelemetry.instrumentation.test.base.HttpServerTest; +import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint; +import java.io.PrintWriter; +import java.util.concurrent.CountDownLatch; +import javax.servlet.AsyncContext; +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +@WebServlet(asyncSupported = true) +class AsyncServlet extends HttpServlet { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) { + ServerEndpoint endpoint = ServerEndpoint.forPath(req.getServletPath()); + CountDownLatch latch = new CountDownLatch(1); + AsyncContext context = req.startAsync(); + if (endpoint == EXCEPTION) { + context.setTimeout(5000); + } + context.start( + () -> { + try { + HttpServerTest.controller( + endpoint, + () -> { + resp.setContentType("text/plain"); + if (endpoint.equals(SUCCESS) || endpoint.equals(ERROR)) { + resp.setStatus(endpoint.getStatus()); + resp.getWriter().print(endpoint.getBody()); + } else if (endpoint.equals(INDEXED_CHILD)) { + endpoint.collectSpanAttributes(x -> req.getParameter(x)); + resp.setStatus(endpoint.getStatus()); + } else if (endpoint.equals(QUERY_PARAM)) { + resp.setStatus(endpoint.getStatus()); + resp.getWriter().print(req.getQueryString()); + } else if (endpoint.equals(REDIRECT)) { + resp.sendRedirect(endpoint.getBody()); + } else if (endpoint.equals(CAPTURE_HEADERS)) { + resp.setHeader("X-Test-Response", req.getHeader("X-Test-Request")); + resp.setStatus(endpoint.getStatus()); + resp.getWriter().print(endpoint.getBody()); + } else if (endpoint.equals(EXCEPTION)) { + resp.setStatus(endpoint.getStatus()); + PrintWriter writer = resp.getWriter(); + writer.print(endpoint.getBody()); + writer.close(); + throw new ServletException(endpoint.getBody()); + } + return null; + }); + } finally { + // complete at the end so the server span will end after the controller span + if (endpoint != EXCEPTION) { + context.complete(); + } + latch.countDown(); + } + }); + try { + latch.await(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } +} diff --git a/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/ErrorHandlerValve.java b/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/ErrorHandlerValve.java new file mode 100644 index 0000000000..d1a6880231 --- /dev/null +++ b/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/ErrorHandlerValve.java @@ -0,0 +1,26 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.tomcat.v7_0; + +import java.io.IOException; +import org.apache.catalina.connector.Request; +import org.apache.catalina.connector.Response; +import org.apache.catalina.valves.ErrorReportValve; + +class ErrorHandlerValve extends ErrorReportValve { + @Override + protected void report(Request request, Response response, Throwable t) { + if (response.getStatus() < 400 || response.getContentWritten() > 0 || !response.isError()) { + return; + } + try { + response.getWriter().print(t != null ? t.getCause().getMessage() : response.getMessage()); + } catch (IOException ignored) { + // Ignore exception when writing exception message to response fails on IO - same as is done + // by the superclass itself and by other built-in ErrorReportValve implementations. + } + } +} diff --git a/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TestServlet.java b/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TestServlet.java similarity index 97% rename from instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TestServlet.java rename to instrumentation/tomcat/tomcat-7.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TestServlet.java index e80df42261..85d4bd868c 100644 --- a/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TestServlet.java +++ b/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TestServlet.java @@ -13,7 +13,7 @@ import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -public class TestServlet extends HttpServlet { +class TestServlet extends HttpServlet { @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { diff --git a/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/ThreadPoolExecutorTest.java b/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/ThreadPoolExecutorTest.java new file mode 100644 index 0000000000..5b1a072754 --- /dev/null +++ b/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/ThreadPoolExecutorTest.java @@ -0,0 +1,86 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.tomcat.v7_0; + +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import org.apache.tomcat.util.threads.TaskQueue; +import org.apache.tomcat.util.threads.ThreadPoolExecutor; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +class ThreadPoolExecutorTest { + + @RegisterExtension + static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + // Test that PropagatedContext isn't cleared when ThreadPoolExecutor.execute fails with + // RejectedExecutionException + @Test + void testTomcatThreadPool() throws InterruptedException { + AtomicBoolean reject = new AtomicBoolean(); + TaskQueue queue = + new TaskQueue() { + @Override + public boolean offer(Runnable o) { + // TaskQueue.offer returns false when parent.getPoolSize() < parent.getMaximumPoolSize() + // here we simulate the same condition to trigger RejectedExecutionException handling in + // tomcat ThreadPoolExecutor + if (reject.get()) { + reject.set(false); + return false; + } + return super.offer(o); + } + }; + + ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 1, 0, TimeUnit.MILLISECONDS, queue); + queue.setParent(pool); + + CountDownLatch latch = new CountDownLatch(1); + + testing.runWithSpan( + "parent", + () -> { + pool.execute( + () -> { + try { + testing.runWithSpan("child1", () -> latch.await()); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + }); + + reject.set(true); + pool.execute( + () -> { + try { + testing.runWithSpan("child2", () -> latch.await()); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + }); + }); + + latch.countDown(); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), + span -> + span.hasName("child1").hasKind(SpanKind.INTERNAL).hasParent(trace.getSpan(0)), + span -> + span.hasName("child2").hasKind(SpanKind.INTERNAL).hasParent(trace.getSpan(0)))); + + pool.shutdown(); + pool.awaitTermination(10, TimeUnit.SECONDS); + } +} diff --git a/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TomcatAsyncTest.java b/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TomcatAsyncTest.java new file mode 100644 index 0000000000..a249300a56 --- /dev/null +++ b/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TomcatAsyncTest.java @@ -0,0 +1,128 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.tomcat.v7_0; + +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.AUTH_REQUIRED; +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.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.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.api.internal.HttpConstants; +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 java.io.File; +import java.nio.file.Files; +import java.util.UUID; +import javax.servlet.ServletException; +import org.apache.catalina.Context; +import org.apache.catalina.LifecycleException; +import org.apache.catalina.startup.Tomcat; +import org.junit.jupiter.api.extension.RegisterExtension; + +class TomcatAsyncTest extends AbstractHttpServerTest { + + @RegisterExtension + static final InstrumentationExtension testing = HttpServerInstrumentationExtension.forAgent(); + + @Override + public Tomcat setupServer() throws Exception { + Tomcat tomcatServer = new Tomcat(); + File baseDir = Files.createTempDirectory("tomcat").toFile(); + baseDir.deleteOnExit(); + tomcatServer.setBaseDir(baseDir.getAbsolutePath()); + tomcatServer.setPort(port); + tomcatServer.getConnector().setEnableLookups(true); // get localhost instead of 127.0.0.1 + + File applicationDir = new File(baseDir, "/webapps/ROOT"); + if (!applicationDir.exists()) { + applicationDir.mkdirs(); + applicationDir.deleteOnExit(); + } + + Context servletContext = + tomcatServer.addWebapp(getContextPath(), applicationDir.getAbsolutePath()); + // Speed up startup by disabling jar scanning: + servletContext.getJarScanner().setJarScanFilter((jarScanType, jarName) -> false); + + setupServlets(servletContext); + tomcatServer.start(); + return tomcatServer; + } + + protected void setupServlets(Context context) throws Exception { + Class servlet = AsyncServlet.class; + + addServlet(context, SUCCESS.getPath(), servlet); + addServlet(context, QUERY_PARAM.getPath(), servlet); + addServlet(context, ERROR.getPath(), servlet); + addServlet(context, EXCEPTION.getPath(), servlet); + addServlet(context, REDIRECT.getPath(), servlet); + addServlet(context, AUTH_REQUIRED.getPath(), servlet); + addServlet(context, CAPTURE_HEADERS.getPath(), servlet); + addServlet(context, INDEXED_CHILD.getPath(), servlet); + } + + void addServlet(Context servletContext, String path, Class servlet) + throws Exception { + String name = UUID.randomUUID().toString(); + Tomcat.addServlet(servletContext, name, servlet.getDeclaredConstructor().newInstance()); + servletContext.addServletMappingDecoded(path, name); + } + + @Override + public void stopServer(Tomcat server) throws LifecycleException { + server.stop(); + server.destroy(); + } + + @Override + protected void configure(HttpServerTestOptions options) { + options.setContextPath("/tomcat-context"); + + options.setExpectedHttpRoute( + (ServerEndpoint endpoint, String method) -> { + if (method.equals(HttpConstants._OTHER)) { + return getContextPath() + endpoint.getPath(); + } + if (endpoint.equals(NOT_FOUND)) { + return getContextPath() + "/*"; + } + return super.expectedHttpRoute(endpoint, method); + }); + + options.setExpectedException(new ServletException(EXCEPTION.getBody())); + + options.setHasResponseSpan(endpoint -> endpoint == NOT_FOUND || endpoint == REDIRECT); + + // with async requests the span is started in one instrumentation (server instrumentation) + // but ended from another (servlet instrumentation) + options.setMetricsInstrumentationName(() -> "io.opentelemetry.servlet-3.0"); + } + + @Override + protected SpanDataAssert assertResponseSpan( + SpanDataAssert span, String method, ServerEndpoint endpoint) { + if (endpoint.equals(REDIRECT)) { + span.satisfies(spanData -> assertThat(spanData.getName()).endsWith(".sendRedirect")); + } else if (endpoint.equals(NOT_FOUND)) { + span.satisfies(spanData -> assertThat(spanData.getName()).endsWith(".sendError")); + } + span.hasKind(SpanKind.INTERNAL).hasAttributesSatisfying(Attributes::isEmpty); + return span; + } +} diff --git a/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TomcatHandlerTest.java b/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TomcatHandlerTest.java new file mode 100644 index 0000000000..3b02b31a5d --- /dev/null +++ b/instrumentation/tomcat/tomcat-7.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TomcatHandlerTest.java @@ -0,0 +1,126 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.tomcat.v7_0; + +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.AUTH_ERROR; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.AUTH_REQUIRED; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_PARAMETERS; +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.LOGIN; +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 java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.api.internal.HttpConstants; +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 java.io.File; +import java.nio.file.Files; +import java.util.List; +import org.apache.catalina.Context; +import org.apache.catalina.LifecycleException; +import org.apache.catalina.core.StandardHost; +import org.apache.catalina.startup.Tomcat; +import org.junit.jupiter.api.extension.RegisterExtension; + +class TomcatHandlerTest extends AbstractHttpServerTest { + + @RegisterExtension + static final InstrumentationExtension testing = HttpServerInstrumentationExtension.forAgent(); + + private static final List serverEndpointsList = + asList( + SUCCESS, + REDIRECT, + ERROR, + EXCEPTION, + NOT_FOUND, + CAPTURE_HEADERS, + CAPTURE_PARAMETERS, + QUERY_PARAM, + PATH_PARAM, + AUTH_REQUIRED, + LOGIN, + AUTH_ERROR, + INDEXED_CHILD); + + @Override + public Tomcat setupServer() throws Exception { + Tomcat tomcatServer = new Tomcat(); + File baseDir = Files.createTempDirectory("tomcat").toFile(); + baseDir.deleteOnExit(); + tomcatServer.setBaseDir(baseDir.getAbsolutePath()); + tomcatServer.setPort(port); + tomcatServer.getConnector(); + + Context servletContext = + tomcatServer.addContext(getContextPath(), new File(".").getAbsolutePath()); + + Tomcat.addServlet(servletContext, "testServlet", new TestServlet()); + + // Mapping servlet to /* will result in all requests have a name of just a context. + serverEndpointsList.stream() + .filter(endpoint -> !endpoint.equals(NOT_FOUND)) + .forEach( + endpoint -> servletContext.addServletMappingDecoded(endpoint.getPath(), "testServlet")); + + StandardHost host = (StandardHost) tomcatServer.getHost(); + host.setErrorReportValveClass(ErrorHandlerValve.class.getName()); + + tomcatServer.start(); + return tomcatServer; + } + + @Override + public void stopServer(Tomcat server) throws LifecycleException { + server.stop(); + server.destroy(); + } + + @Override + protected void configure(HttpServerTestOptions options) { + options.setContextPath("/app"); + options.setHasResponseCustomizer(serverEndpoint -> true); + options.setTestCaptureRequestParameters(true); + options.setTestErrorBody(false); + + options.setHasResponseSpan( + endpoint -> endpoint == REDIRECT || endpoint == ERROR || endpoint == NOT_FOUND); + + options.setExpectedHttpRoute( + (ServerEndpoint endpoint, String method) -> { + if (method.equals(HttpConstants._OTHER)) { + return getContextPath() + endpoint.getPath(); + } + return super.expectedHttpRoute(endpoint, method); + }); + } + + @Override + protected SpanDataAssert assertResponseSpan( + SpanDataAssert span, String method, ServerEndpoint endpoint) { + if (endpoint.equals(REDIRECT)) { + span.satisfies(spanData -> assertThat(spanData.getName()).endsWith(".sendRedirect")); + } else if (endpoint.equals(NOT_FOUND)) { + span.satisfies(spanData -> assertThat(spanData.getName()).endsWith(".sendError")); + } + span.hasKind(SpanKind.INTERNAL).hasAttributesSatisfying(Attributes::isEmpty); + return span; + } +}