Browse Source

Convert tomcat 7 tests from groovy to java (#11402)

Jay DeLuca 9 months ago
parent
commit
b36d2845b3
10 changed files with 449 additions and 444 deletions
  1. 0 82
      instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/AsyncServlet.groovy
  2. 0 90
      instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/ThreadPoolExecutorTest.groovy
  3. 0 143
      instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TomcatAsyncTest.groovy
  4. 0 128
      instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TomcatHandlerTest.groovy
  5. 82 0
      instrumentation/tomcat/tomcat-7.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/AsyncServlet.java
  6. 26 0
      instrumentation/tomcat/tomcat-7.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/ErrorHandlerValve.java
  7. 1 1
      instrumentation/tomcat/tomcat-7.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TestServlet.java
  8. 86 0
      instrumentation/tomcat/tomcat-7.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/ThreadPoolExecutorTest.java
  9. 128 0
      instrumentation/tomcat/tomcat-7.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TomcatAsyncTest.java
  10. 126 0
      instrumentation/tomcat/tomcat-7.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TomcatHandlerTest.java

+ 0 - 82
instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/AsyncServlet.groovy

@@ -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()
-  }
-}

+ 0 - 90
instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/ThreadPoolExecutorTest.groovy

@@ -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)
-  }
-}

+ 0 - 143
instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TomcatAsyncTest.groovy

@@ -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<Tomcat> 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> servlet) {
-    String name = UUID.randomUUID()
-    Tomcat.addServlet(servletContext, name, servlet.newInstance())
-    servletContext.addServletMappingDecoded(path, name)
-  }
-
-  Class<Servlet> 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
-    }
-  }
-}

+ 0 - 128
instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TomcatHandlerTest.groovy

@@ -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<Tomcat> implements AgentTestTrait {
-
-  private static final List<ServerEndpoint> 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.
-    }
-  }
-}

+ 82 - 0
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);
+    }
+  }
+}

+ 26 - 0
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.
+    }
+  }
+}

+ 1 - 1
instrumentation/tomcat/tomcat-7.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/tomcat/v7_0/TestServlet.java → 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 {

+ 86 - 0
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);
+  }
+}

+ 128 - 0
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<Tomcat> {
+
+  @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<AsyncServlet> 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<AsyncServlet> 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;
+  }
+}

+ 126 - 0
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<Tomcat> {
+
+  @RegisterExtension
+  static final InstrumentationExtension testing = HttpServerInstrumentationExtension.forAgent();
+
+  private static final List<ServerEndpoint> 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;
+  }
+}