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

This commit is contained in:
Jay DeLuca 2024-05-21 02:57:35 -04:00 committed by GitHub
parent 64bbbc801a
commit b36d2845b3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 449 additions and 444 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 {

View File

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

View File

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

View File

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