From c165ca4dbd2103f3396437106624981c8b9a385e Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Sat, 19 Oct 2024 03:44:03 +0200 Subject: [PATCH] conver servlet 5 tests from groovy to java (#12364) --- .../servlet-3.0/javaagent/build.gradle.kts | 23 - .../v3_0/tomcat/ErrorHandlerValve.java | 3 +- .../v3_0/tomcat/TestAccessLogValve.java | 3 +- .../servlet-5.0/javaagent/build.gradle.kts | 20 - .../groovy/AbstractServlet5MappingTest.groovy | 79 --- .../groovy/JettyServlet5MappingTest.groovy | 60 --- .../src/test/groovy/JettyServlet5Test.groovy | 264 ---------- .../groovy/JettyServletHandlerTest.groovy | 79 --- .../TomcatServlet5FilterMappingTest.groovy | 135 ----- .../groovy/TomcatServlet5MappingTest.groovy | 65 --- .../src/test/groovy/TomcatServlet5Test.groovy | 472 ------------------ .../src/test/resources/realm.properties | 1 - .../jetty11-testing/build.gradle.kts | 26 + .../v5_0/jetty/JettyServlet5AsyncTest.java | 22 + .../jetty/JettyServlet5FakeAsyncTest.java | 16 + .../v5_0/jetty/JettyServlet5MappingTest.java | 60 +++ .../v5_0/jetty/JettyServlet5SyncTest.java | 16 + .../servlet/v5_0/jetty/JettyServlet5Test.java | 74 +++ .../v5_0/jetty/JettyServletHandlerTest.java | 86 ++++ .../jetty/dispatch/JettyDispatchTest.java | 18 + .../JettyServlet5DispatchAsyncTest.java | 54 ++ .../JettyServlet5DispatchImmediateTest.java | 57 +++ .../dispatch/JettyServlet5ForwardTest.java | 56 +++ .../dispatch/JettyServlet5IncludeTest.java | 65 +++ .../test/groovy/Jetty12Servlet5Test.groovy | 241 --------- .../jetty12/Jetty12Servlet5AsyncTest.java | 22 + .../jetty12/Jetty12Servlet5FakeAsyncTest.java | 16 + .../v5_0/jetty12/Jetty12Servlet5SyncTest.java | 16 + .../v5_0/jetty12/Jetty12Servlet5Test.java | 71 +++ .../jetty12/dispatch/Jetty12DispatchTest.java | 18 + .../Jetty12Servlet5DispatchAsyncTest.java | 54 ++ .../Jetty12Servlet5DispatchImmediateTest.java | 57 +++ .../dispatch/Jetty12Servlet5ForwardTest.java | 56 +++ .../dispatch/Jetty12Servlet5IncludeTest.java | 65 +++ .../groovy/test/AbstractServlet5Test.groovy | 220 -------- .../src/main/groovy/test/TestServlet5.groovy | 294 ----------- .../v5_0/AbstractServlet5MappingTest.java | 99 ++++ .../servlet/v5_0/AbstractServlet5Test.java | 227 +++++++++ .../servlet/v5_0/TestServlet5.java | 298 +++++++++++ .../tomcat-testing/build.gradle.kts | 26 + .../v5_0/tomcat/ErrorHandlerValve.java | 32 ++ .../v5_0/tomcat/TestAccessLogValve.java | 82 +++ .../v5_0/tomcat/TomcatServlet5AsyncTest.java | 22 + .../tomcat/TomcatServlet5FakeAsyncTest.java | 17 + .../v5_0/tomcat/TomcatServlet5SyncTest.java | 17 + .../v5_0/tomcat/TomcatServlet5Test.java | 225 +++++++++ .../tomcat/dispatch/TomcatDispatchTest.java | 18 + .../TomcatServlet5DispatchAsyncTest.java | 68 +++ .../TomcatServlet5DispatchImmediateTest.java | 61 +++ .../dispatch/TomcatServlet5ForwardTest.java | 64 +++ .../dispatch/TomcatServlet5IncludeTest.java | 64 +++ .../TomcatServlet5FilterMappingTest.java | 114 +++++ ...tServlet5FilterServletNameMappingTest.java | 25 + ...atServlet5FilterUrlPatternMappingTest.java | 18 + .../mapping/TomcatServlet5MappingTest.java | 62 +++ settings.gradle.kts | 2 + 56 files changed, 2470 insertions(+), 1955 deletions(-) delete mode 100644 instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/AbstractServlet5MappingTest.groovy delete mode 100644 instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/JettyServlet5MappingTest.groovy delete mode 100644 instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/JettyServlet5Test.groovy delete mode 100644 instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/JettyServletHandlerTest.groovy delete mode 100644 instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/TomcatServlet5FilterMappingTest.groovy delete mode 100644 instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/TomcatServlet5MappingTest.groovy delete mode 100644 instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/TomcatServlet5Test.groovy delete mode 100644 instrumentation/servlet/servlet-5.0/javaagent/src/test/resources/realm.properties create mode 100644 instrumentation/servlet/servlet-5.0/jetty11-testing/build.gradle.kts create mode 100644 instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/JettyServlet5AsyncTest.java create mode 100644 instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/JettyServlet5FakeAsyncTest.java create mode 100644 instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/JettyServlet5MappingTest.java create mode 100644 instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/JettyServlet5SyncTest.java create mode 100644 instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/JettyServlet5Test.java create mode 100644 instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/JettyServletHandlerTest.java create mode 100644 instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/dispatch/JettyDispatchTest.java create mode 100644 instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/dispatch/JettyServlet5DispatchAsyncTest.java create mode 100644 instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/dispatch/JettyServlet5DispatchImmediateTest.java create mode 100644 instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/dispatch/JettyServlet5ForwardTest.java create mode 100644 instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/dispatch/JettyServlet5IncludeTest.java delete mode 100644 instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/groovy/Jetty12Servlet5Test.groovy create mode 100644 instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/Jetty12Servlet5AsyncTest.java create mode 100644 instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/Jetty12Servlet5FakeAsyncTest.java create mode 100644 instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/Jetty12Servlet5SyncTest.java create mode 100644 instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/Jetty12Servlet5Test.java create mode 100644 instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/dispatch/Jetty12DispatchTest.java create mode 100644 instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/dispatch/Jetty12Servlet5DispatchAsyncTest.java create mode 100644 instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/dispatch/Jetty12Servlet5DispatchImmediateTest.java create mode 100644 instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/dispatch/Jetty12Servlet5ForwardTest.java create mode 100644 instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/dispatch/Jetty12Servlet5IncludeTest.java delete mode 100644 instrumentation/servlet/servlet-5.0/testing/src/main/groovy/test/AbstractServlet5Test.groovy delete mode 100644 instrumentation/servlet/servlet-5.0/testing/src/main/groovy/test/TestServlet5.groovy create mode 100644 instrumentation/servlet/servlet-5.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/AbstractServlet5MappingTest.java create mode 100644 instrumentation/servlet/servlet-5.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/AbstractServlet5Test.java create mode 100644 instrumentation/servlet/servlet-5.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/TestServlet5.java create mode 100644 instrumentation/servlet/servlet-5.0/tomcat-testing/build.gradle.kts create mode 100644 instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/ErrorHandlerValve.java create mode 100644 instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/TestAccessLogValve.java create mode 100644 instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/TomcatServlet5AsyncTest.java create mode 100644 instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/TomcatServlet5FakeAsyncTest.java create mode 100644 instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/TomcatServlet5SyncTest.java create mode 100644 instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/TomcatServlet5Test.java create mode 100644 instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/dispatch/TomcatDispatchTest.java create mode 100644 instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/dispatch/TomcatServlet5DispatchAsyncTest.java create mode 100644 instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/dispatch/TomcatServlet5DispatchImmediateTest.java create mode 100644 instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/dispatch/TomcatServlet5ForwardTest.java create mode 100644 instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/dispatch/TomcatServlet5IncludeTest.java create mode 100644 instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/mapping/TomcatServlet5FilterMappingTest.java create mode 100644 instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/mapping/TomcatServlet5FilterServletNameMappingTest.java create mode 100644 instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/mapping/TomcatServlet5FilterUrlPatternMappingTest.java create mode 100644 instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/mapping/TomcatServlet5MappingTest.java diff --git a/instrumentation/servlet/servlet-3.0/javaagent/build.gradle.kts b/instrumentation/servlet/servlet-3.0/javaagent/build.gradle.kts index 7b6bafd9de..c08fe237c6 100644 --- a/instrumentation/servlet/servlet-3.0/javaagent/build.gradle.kts +++ b/instrumentation/servlet/servlet-3.0/javaagent/build.gradle.kts @@ -21,27 +21,4 @@ dependencies { bootstrap(project(":instrumentation:servlet:servlet-common:bootstrap")) compileOnly("javax.servlet:javax.servlet-api:3.0.1") - - testInstrumentation(project(":instrumentation:jetty:jetty-8.0:javaagent")) - testImplementation(project(":instrumentation:servlet:servlet-common:bootstrap")) - - testLibrary("org.eclipse.jetty:jetty-server:8.0.0.v20110901") - testLibrary("org.eclipse.jetty:jetty-servlet:8.0.0.v20110901") - testLibrary("org.apache.tomcat.embed:tomcat-embed-core:8.0.41") - testLibrary("org.apache.tomcat.embed:tomcat-embed-jasper:8.0.41") - - latestDepTestLibrary("org.eclipse.jetty:jetty-server:10.+") // see servlet-5.0 module - latestDepTestLibrary("org.eclipse.jetty:jetty-servlet:10.+") // see servlet-5.0 module - - latestDepTestLibrary("org.apache.tomcat.embed:tomcat-embed-core:9.+") // see servlet-5.0 module - latestDepTestLibrary("org.apache.tomcat.embed:tomcat-embed-jasper:9.+") // see servlet-5.0 module -} - -tasks { - withType().configureEach { - jvmArgs("-Dotel.instrumentation.servlet.experimental.capture-request-parameters=test-parameter") - // required on jdk17 - jvmArgs("--add-opens=java.base/java.util=ALL-UNNAMED") - jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") - } } diff --git a/instrumentation/servlet/servlet-3.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/tomcat/ErrorHandlerValve.java b/instrumentation/servlet/servlet-3.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/tomcat/ErrorHandlerValve.java index e65e08c1e6..e2266081cf 100644 --- a/instrumentation/servlet/servlet-3.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/tomcat/ErrorHandlerValve.java +++ b/instrumentation/servlet/servlet-3.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/tomcat/ErrorHandlerValve.java @@ -12,7 +12,8 @@ import org.apache.catalina.valves.ErrorReportValve; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -class ErrorHandlerValve extends ErrorReportValve { +// public, because it's loaded by reflection +public class ErrorHandlerValve extends ErrorReportValve { private static final Logger logger = LoggerFactory.getLogger(ErrorHandlerValve.class); diff --git a/instrumentation/servlet/servlet-3.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/tomcat/TestAccessLogValve.java b/instrumentation/servlet/servlet-3.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/tomcat/TestAccessLogValve.java index 176a19c7df..1be5cbe759 100644 --- a/instrumentation/servlet/servlet-3.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/tomcat/TestAccessLogValve.java +++ b/instrumentation/servlet/servlet-3.0/testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v3_0/tomcat/TestAccessLogValve.java @@ -17,7 +17,8 @@ import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Response; import org.apache.catalina.valves.ValveBase; -class TestAccessLogValve extends ValveBase implements AccessLog { +// public, because it's loaded by reflection +public class TestAccessLogValve extends ValveBase implements AccessLog { public final List> getLoggedIds() { return loggedIds; diff --git a/instrumentation/servlet/servlet-5.0/javaagent/build.gradle.kts b/instrumentation/servlet/servlet-5.0/javaagent/build.gradle.kts index 0f7d1f12a2..f69998771a 100644 --- a/instrumentation/servlet/servlet-5.0/javaagent/build.gradle.kts +++ b/instrumentation/servlet/servlet-5.0/javaagent/build.gradle.kts @@ -16,24 +16,4 @@ dependencies { bootstrap(project(":instrumentation:servlet:servlet-common:bootstrap")) compileOnly("jakarta.servlet:jakarta.servlet-api:5.0.0") - - testInstrumentation(project(":instrumentation:jetty:jetty-11.0:javaagent")) - - testImplementation(project(":instrumentation:servlet:servlet-5.0:testing")) - - testLibrary("org.eclipse.jetty:jetty-server:11.0.0") - testLibrary("org.eclipse.jetty:jetty-servlet:11.0.0") - testLibrary("org.apache.tomcat.embed:tomcat-embed-core:10.0.0") - testLibrary("org.apache.tomcat.embed:tomcat-embed-jasper:10.0.0") - - // Tomcat 10.1 requires Java 11 - latestDepTestLibrary("org.apache.tomcat.embed:tomcat-embed-core:10.0.+") - latestDepTestLibrary("org.apache.tomcat.embed:tomcat-embed-jasper:10.0.+") - latestDepTestLibrary("org.eclipse.jetty:jetty-server:11.+") -} - -tasks { - withType().configureEach { - jvmArgs("-Dotel.instrumentation.servlet.experimental.capture-request-parameters=test-parameter") - } } diff --git a/instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/AbstractServlet5MappingTest.groovy b/instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/AbstractServlet5MappingTest.groovy deleted file mode 100644 index 2e0d1d7d9e..0000000000 --- a/instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/AbstractServlet5MappingTest.groovy +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.api.trace.SpanKind -import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification -import io.opentelemetry.instrumentation.test.base.HttpServerTestTrait -import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpResponse -import jakarta.servlet.Servlet -import jakarta.servlet.ServletException -import jakarta.servlet.http.HttpServlet -import jakarta.servlet.http.HttpServletRequest -import jakarta.servlet.http.HttpServletResponse -import spock.lang.Unroll - -import static io.opentelemetry.api.trace.StatusCode.ERROR - -abstract class AbstractServlet5MappingTest extends AgentInstrumentationSpecification implements HttpServerTestTrait { - - def setupSpec() { - setupServer() - } - - def cleanupSpec() { - cleanupServer() - } - - abstract void addServlet(CONTEXT context, String path, Class servlet) - - protected void setupServlets(CONTEXT context) { - addServlet(context, "/prefix/*", TestServlet) - addServlet(context, "*.suffix", TestServlet) - } - - static class TestServlet extends HttpServlet { - @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - response.getWriter().write("Ok") - } - } - - @Unroll - def "test path #path"() { - setup: - AggregatedHttpResponse response = client.get(address.resolve(path).toString()).aggregate().join() - - expect: - response.status().code() == (success ? 200 : 404) - - and: - def spanCount = success ? 1 : 2 - assertTraces(1) { - trace(0, spanCount) { - span(0) { - name "GET " + getContextPath() + route - kind SpanKind.SERVER - if (!success && response.status().code() >= 500) { - status ERROR - } - } - if (!success) { - span(1) { - } - } - } - } - - where: - path | route | success - 'prefix' | '/prefix/*' | true - 'prefix/' | '/prefix/*' | true - 'prefix/a' | '/prefix/*' | true - 'prefixa' | '/*' | false - 'a.suffix' | '/*.suffix' | true - '.suffix' | '/*.suffix' | true - 'suffix' | '/*' | false - } -} diff --git a/instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/JettyServlet5MappingTest.groovy b/instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/JettyServlet5MappingTest.groovy deleted file mode 100644 index bac23eae68..0000000000 --- a/instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/JettyServlet5MappingTest.groovy +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import jakarta.servlet.Servlet -import jakarta.servlet.ServletException -import jakarta.servlet.http.HttpServlet -import jakarta.servlet.http.HttpServletRequest -import jakarta.servlet.http.HttpServletResponse -import org.eclipse.jetty.server.Server -import org.eclipse.jetty.servlet.ServletContextHandler -import spock.lang.IgnoreIf - -@IgnoreIf({ !jvm.java11Compatible }) -class JettyServlet5MappingTest extends AbstractServlet5MappingTest { - - @Override - Object startServer(int port) { - Server server = new Server(port) - ServletContextHandler handler = new ServletContextHandler(null, contextPath) - setupServlets(handler) - server.setHandler(handler) - server.start() - return server - } - - @Override - void stopServer(Object serverObject) { - Server server = (Server) serverObject - server.stop() - server.destroy() - } - - @Override - protected void setupServlets(Object handlerObject) { - ServletContextHandler handler = (ServletContextHandler) handlerObject - super.setupServlets(handler) - - addServlet(handler, "/", DefaultServlet) - } - - @Override - void addServlet(Object handlerObject, String path, Class servlet) { - ServletContextHandler handler = (ServletContextHandler) handlerObject - handler.addServlet(servlet, path) - } - - static class DefaultServlet extends HttpServlet { - @Override - protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - response.sendError(404) - } - } - - @Override - String getContextPath() { - "/jetty-context" - } -} diff --git a/instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/JettyServlet5Test.groovy b/instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/JettyServlet5Test.groovy deleted file mode 100644 index 6b547b3dab..0000000000 --- a/instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/JettyServlet5Test.groovy +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.RequestDispatcherServlet -import jakarta.servlet.Servlet -import jakarta.servlet.ServletException -import jakarta.servlet.http.HttpServletRequest -import org.eclipse.jetty.server.Server -import org.eclipse.jetty.server.handler.ErrorHandler -import org.eclipse.jetty.servlet.ServletContextHandler -import spock.lang.IgnoreIf -import test.AbstractServlet5Test -import test.TestServlet5 - -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.QUERY_PARAM -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS - -abstract class JettyServlet5Test extends AbstractServlet5Test { - - @Override - boolean testNotFound() { - false - } - - @Override - Throwable expectedException() { - new ServletException(EXCEPTION.body) - } - - @Override - Object startServer(int port) { - def jettyServer = new Server(port) - jettyServer.connectors.each { - it.setHost('localhost') - } - - ServletContextHandler servletContext = new ServletContextHandler(null, contextPath) - servletContext.errorHandler = new ErrorHandler() { - protected void handleErrorPage(HttpServletRequest request, Writer writer, int code, String message) throws IOException { - Throwable th = (Throwable) request.getAttribute("jakarta.servlet.error.exception") - writer.write(th ? th.message : message) - } - } -// setupAuthentication(jettyServer, servletContext) - setupServlets(servletContext) - jettyServer.setHandler(servletContext) - - jettyServer.start() - - return jettyServer - } - - @Override - void stopServer(Object serverObject) { - Server server = (Server) serverObject - server.stop() - server.destroy() - } - - @Override - String getContextPath() { - return "/jetty-context" - } - - @Override - void addServlet(Object handlerObject, String path, Class servlet) { - ServletContextHandler handler = (ServletContextHandler) handlerObject - handler.addServlet(servlet, path) - } - - // FIXME: Add authentication tests back in... -// static setupAuthentication(Server jettyServer, ServletContextHandler servletContext) { -// ConstraintSecurityHandler authConfig = new ConstraintSecurityHandler() -// -// Constraint constraint = new Constraint() -// constraint.setName("auth") -// constraint.setAuthenticate(true) -// constraint.setRoles("role") -// -// ConstraintMapping mapping = new ConstraintMapping() -// mapping.setPathSpec("/auth/*") -// mapping.setConstraint(constraint) -// -// authConfig.setConstraintMappings(mapping) -// authConfig.setAuthenticator(new BasicAuthenticator()) -// -// LoginService loginService = new HashLoginService("TestRealm", -// "src/test/resources/realm.properties") -// authConfig.setLoginService(loginService) -// jettyServer.addBean(loginService) -// -// servletContext.setSecurityHandler(authConfig) -// } -} - -@IgnoreIf({ !jvm.java11Compatible }) -class JettyServlet5TestSync extends JettyServlet5Test { - - @Override - Class servlet() { - TestServlet5.Sync - } -} - -@IgnoreIf({ !jvm.java11Compatible }) -class JettyServlet5TestAsync extends JettyServlet5Test { - - @Override - Class servlet() { - TestServlet5.Async - } - - @Override - boolean errorEndpointUsesSendError() { - false - } -} - -@IgnoreIf({ !jvm.java11Compatible }) -class JettyServlet5TestFakeAsync extends JettyServlet5Test { - - @Override - Class servlet() { - TestServlet5.FakeAsync - } -} - -@IgnoreIf({ !jvm.java11Compatible }) -class JettyServlet5TestForward extends JettyDispatchTest { - @Override - Class servlet() { - TestServlet5.Sync // dispatch to sync servlet - } - - @Override - protected void setupServlets(Object context) { - super.setupServlets(context) - - addServlet(context, "/dispatch" + SUCCESS.path, RequestDispatcherServlet.Forward) - addServlet(context, "/dispatch" + HTML_PRINT_WRITER.path, RequestDispatcherServlet.Forward) - addServlet(context, "/dispatch" + HTML_SERVLET_OUTPUT_STREAM.path, RequestDispatcherServlet.Forward) - addServlet(context, "/dispatch" + QUERY_PARAM.path, RequestDispatcherServlet.Forward) - addServlet(context, "/dispatch" + REDIRECT.path, RequestDispatcherServlet.Forward) - addServlet(context, "/dispatch" + ERROR.path, RequestDispatcherServlet.Forward) - addServlet(context, "/dispatch" + EXCEPTION.path, RequestDispatcherServlet.Forward) - addServlet(context, "/dispatch" + AUTH_REQUIRED.path, RequestDispatcherServlet.Forward) - addServlet(context, "/dispatch" + CAPTURE_HEADERS.path, RequestDispatcherServlet.Forward) - addServlet(context, "/dispatch" + CAPTURE_PARAMETERS.path, RequestDispatcherServlet.Forward) - addServlet(context, "/dispatch" + INDEXED_CHILD.path, RequestDispatcherServlet.Forward) - } -} - -@IgnoreIf({ !jvm.java11Compatible }) -class JettyServlet5TestInclude extends JettyDispatchTest { - @Override - Class servlet() { - TestServlet5.Sync // dispatch to sync servlet - } - - @Override - boolean testRedirect() { - false - } - - @Override - boolean testCapturedHttpHeaders() { - false - } - - @Override - boolean testError() { - false - } - - @Override - protected void setupServlets(Object context) { - super.setupServlets(context) - - addServlet(context, "/dispatch" + SUCCESS.path, RequestDispatcherServlet.Include) - addServlet(context, "/dispatch" + HTML_PRINT_WRITER.path, RequestDispatcherServlet.Include) - addServlet(context, "/dispatch" + HTML_SERVLET_OUTPUT_STREAM.path, RequestDispatcherServlet.Include) - addServlet(context, "/dispatch" + QUERY_PARAM.path, RequestDispatcherServlet.Include) - addServlet(context, "/dispatch" + REDIRECT.path, RequestDispatcherServlet.Include) - addServlet(context, "/dispatch" + ERROR.path, RequestDispatcherServlet.Include) - addServlet(context, "/dispatch" + EXCEPTION.path, RequestDispatcherServlet.Include) - addServlet(context, "/dispatch" + AUTH_REQUIRED.path, RequestDispatcherServlet.Include) - addServlet(context, "/dispatch" + CAPTURE_HEADERS.path, RequestDispatcherServlet.Include) - addServlet(context, "/dispatch" + CAPTURE_PARAMETERS.path, RequestDispatcherServlet.Include) - addServlet(context, "/dispatch" + INDEXED_CHILD.path, RequestDispatcherServlet.Include) - } -} - - -@IgnoreIf({ !jvm.java11Compatible }) -class JettyServlet5TestDispatchImmediate extends JettyDispatchTest { - @Override - Class servlet() { - TestServlet5.Sync - } - - @Override - protected void setupServlets(Object context) { - super.setupServlets(context) - addServlet(context, "/dispatch" + HTML_PRINT_WRITER.path, TestServlet5.DispatchImmediate) - addServlet(context, "/dispatch" + HTML_SERVLET_OUTPUT_STREAM.path, TestServlet5.DispatchImmediate) - addServlet(context, "/dispatch" + SUCCESS.path, TestServlet5.DispatchImmediate) - addServlet(context, "/dispatch" + QUERY_PARAM.path, TestServlet5.DispatchImmediate) - addServlet(context, "/dispatch" + ERROR.path, TestServlet5.DispatchImmediate) - addServlet(context, "/dispatch" + EXCEPTION.path, TestServlet5.DispatchImmediate) - addServlet(context, "/dispatch" + REDIRECT.path, TestServlet5.DispatchImmediate) - addServlet(context, "/dispatch" + AUTH_REQUIRED.path, TestServlet5.DispatchImmediate) - addServlet(context, "/dispatch" + CAPTURE_HEADERS.path, TestServlet5.DispatchImmediate) - addServlet(context, "/dispatch" + CAPTURE_PARAMETERS.path, TestServlet5.DispatchImmediate) - addServlet(context, "/dispatch" + INDEXED_CHILD.path, TestServlet5.DispatchImmediate) - addServlet(context, "/dispatch/recursive", TestServlet5.DispatchRecursive) - } -} - -@IgnoreIf({ !jvm.java11Compatible }) -class JettyServlet5TestDispatchAsync extends JettyDispatchTest { - @Override - Class servlet() { - TestServlet5.Async - } - - @Override - protected void setupServlets(Object context) { - super.setupServlets(context) - - addServlet(context, "/dispatch" + SUCCESS.path, TestServlet5.DispatchAsync) - addServlet(context, "/dispatch" + HTML_PRINT_WRITER.path, TestServlet5.DispatchAsync) - addServlet(context, "/dispatch" + HTML_SERVLET_OUTPUT_STREAM.path, TestServlet5.DispatchAsync) - addServlet(context, "/dispatch" + QUERY_PARAM.path, TestServlet5.DispatchAsync) - addServlet(context, "/dispatch" + ERROR.path, TestServlet5.DispatchAsync) - addServlet(context, "/dispatch" + EXCEPTION.path, TestServlet5.DispatchAsync) - addServlet(context, "/dispatch" + REDIRECT.path, TestServlet5.DispatchAsync) - addServlet(context, "/dispatch" + AUTH_REQUIRED.path, TestServlet5.DispatchAsync) - addServlet(context, "/dispatch" + CAPTURE_HEADERS.path, TestServlet5.DispatchAsync) - addServlet(context, "/dispatch" + CAPTURE_PARAMETERS.path, TestServlet5.DispatchAsync) - addServlet(context, "/dispatch" + INDEXED_CHILD.path, TestServlet5.DispatchAsync) - addServlet(context, "/dispatch/recursive", TestServlet5.DispatchRecursive) - } - - @Override - boolean errorEndpointUsesSendError() { - false - } -} - -abstract class JettyDispatchTest extends JettyServlet5Test { - @Override - URI buildAddress() { - return new URI("http://localhost:$port$contextPath/dispatch/") - } -} diff --git a/instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/JettyServletHandlerTest.groovy b/instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/JettyServletHandlerTest.groovy deleted file mode 100644 index 820bc8b8c5..0000000000 --- a/instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/JettyServletHandlerTest.groovy +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.api.common.AttributeKey -import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint -import io.opentelemetry.semconv.HttpAttributes -import jakarta.servlet.Servlet -import jakarta.servlet.ServletException -import jakarta.servlet.http.HttpServletRequest -import org.eclipse.jetty.server.Server -import org.eclipse.jetty.server.handler.ErrorHandler -import org.eclipse.jetty.servlet.ServletHandler -import spock.lang.IgnoreIf -import test.AbstractServlet5Test -import test.TestServlet5 - -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION - -@IgnoreIf({ !jvm.java11Compatible }) -class JettyServletHandlerTest extends AbstractServlet5Test { - - @Override - Set> httpAttributes(ServerEndpoint endpoint) { - def attributes = super.httpAttributes(endpoint) - attributes.remove(HttpAttributes.HTTP_ROUTE) - attributes - } - - @Override - Object startServer(int port) { - Server server = new Server(port) - ServletHandler handler = new ServletHandler() - server.setHandler(handler) - setupServlets(handler) - server.addBean(new ErrorHandler() { - protected void handleErrorPage(HttpServletRequest request, Writer writer, int code, String message) throws IOException { - Throwable th = (Throwable) request.getAttribute("jakarta.servlet.error.exception") - writer.write(th ? th.message : message) - } - }) - server.start() - return server - } - - @Override - void addServlet(Object handlerObject, String path, Class servlet) { - ServletHandler servletHandler = (ServletHandler) handlerObject - servletHandler.addServletWithMapping(servlet, path) - } - - @Override - void stopServer(Object serverObject) { - Server server = (Server) serverObject - server.stop() - server.destroy() - } - - @Override - String getContextPath() { - "" - } - - @Override - Class servlet() { - TestServlet5.Sync - } - - @Override - boolean testNotFound() { - false - } - - @Override - Throwable expectedException() { - new ServletException(EXCEPTION.body) - } -} diff --git a/instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/TomcatServlet5FilterMappingTest.groovy b/instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/TomcatServlet5FilterMappingTest.groovy deleted file mode 100644 index 71278c52f2..0000000000 --- a/instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/TomcatServlet5FilterMappingTest.groovy +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import jakarta.servlet.Filter -import jakarta.servlet.FilterChain -import jakarta.servlet.FilterConfig -import jakarta.servlet.ServletException -import jakarta.servlet.ServletRequest -import jakarta.servlet.ServletResponse -import jakarta.servlet.http.HttpServlet -import jakarta.servlet.http.HttpServletRequest -import jakarta.servlet.http.HttpServletResponse -import org.apache.catalina.Context -import org.apache.catalina.startup.Tomcat -import org.apache.tomcat.util.descriptor.web.FilterDef -import org.apache.tomcat.util.descriptor.web.FilterMap - -abstract class TomcatServlet5FilterMappingTest extends TomcatServlet5MappingTest { - - void addFilter(Context servletContext, String path, Class filter) { - String name = UUID.randomUUID() - FilterDef filterDef = new FilterDef() - filterDef.setFilter(filter.newInstance()) - filterDef.setFilterName(name) - servletContext.addFilterDef(filterDef) - FilterMap filterMap = new FilterMap() - filterMap.setFilterName(name) - filterMap.addURLPattern(path) - servletContext.addFilterMap(filterMap) - } - - void addFilterWithServletName(Context servletContext, String servletName, Class filter) { - String name = UUID.randomUUID() - FilterDef filterDef = new FilterDef() - filterDef.setFilter(filter.newInstance()) - filterDef.setFilterName(name) - servletContext.addFilterDef(filterDef) - FilterMap filterMap = new FilterMap() - filterMap.setFilterName(name) - filterMap.addServletName(servletName) - servletContext.addFilterMap(filterMap) - } - - static class TestFilter implements Filter { - @Override - void init(FilterConfig filterConfig) throws ServletException { - } - - @Override - void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { - if (servletRequest.getAttribute("firstFilterCalled") != null) { - servletRequest.setAttribute("testFilterCalled", Boolean.TRUE) - filterChain.doFilter(servletRequest, servletResponse) - } else { - throw new IllegalStateException("First filter should have been called.") - } - } - - @Override - void destroy() { - } - } - - static class FirstFilter implements Filter { - @Override - void init(FilterConfig filterConfig) throws ServletException { - } - - @Override - void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { - servletRequest.setAttribute("firstFilterCalled", Boolean.TRUE) - filterChain.doFilter(servletRequest, servletResponse) - } - - @Override - void destroy() { - } - } - - static class LastFilter implements Filter { - - @Override - void init(FilterConfig filterConfig) throws ServletException { - } - - @Override - void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { - if (servletRequest.getAttribute("testFilterCalled") != null) { - HttpServletResponse response = (HttpServletResponse) servletResponse - response.getWriter().write("Ok") - response.setStatus(HttpServletResponse.SC_OK) - } else { - filterChain.doFilter(servletRequest, servletResponse) - } - } - - @Override - void destroy() { - } - } - - static class DefaultServlet extends HttpServlet { - protected void service(HttpServletRequest req, HttpServletResponse resp) { - throw new IllegalStateException("Servlet should not have been called, filter should have handled the request.") - } - } -} - -class TomcatServlet5FilterUrlPatternMappingTest extends TomcatServlet5FilterMappingTest { - @Override - protected void setupServlets(Context context) { - addFilter(context, "/*", FirstFilter) - addFilter(context, "/prefix/*", TestFilter) - addFilter(context, "*.suffix", TestFilter) - addFilter(context, "/*", LastFilter) - } -} - -class TomcatServlet5FilterServletNameMappingTest extends TomcatServlet5FilterMappingTest { - @Override - protected void setupServlets(Context context) { - Tomcat.addServlet(context, "prefix-servlet", DefaultServlet.newInstance()) - context.addServletMappingDecoded("/prefix/*", "prefix-servlet") - Tomcat.addServlet(context, "suffix-servlet", DefaultServlet.newInstance()) - context.addServletMappingDecoded("*.suffix", "suffix-servlet") - - addFilter(context, "/*", FirstFilter) - addFilterWithServletName(context, "prefix-servlet", TestFilter) - addFilterWithServletName(context, "suffix-servlet", TestFilter) - addFilterWithServletName(context, "prefix-servlet", LastFilter) - addFilterWithServletName(context, "suffix-servlet", LastFilter) - } -} diff --git a/instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/TomcatServlet5MappingTest.groovy b/instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/TomcatServlet5MappingTest.groovy deleted file mode 100644 index e39032e2fe..0000000000 --- a/instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/TomcatServlet5MappingTest.groovy +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import jakarta.servlet.Servlet -import org.apache.catalina.Context -import org.apache.catalina.startup.Tomcat -import org.apache.tomcat.JarScanFilter -import org.apache.tomcat.JarScanType - -import java.nio.file.Files - -class TomcatServlet5MappingTest extends AbstractServlet5MappingTest { - - @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 - void addServlet(Context servletContext, String path, Class servlet) { - String name = UUID.randomUUID() - Tomcat.addServlet(servletContext, name, servlet.newInstance()) - servletContext.addServletMappingDecoded(path, name) - } - - @Override - String getContextPath() { - return "/tomcat-context" - } -} diff --git a/instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/TomcatServlet5Test.groovy b/instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/TomcatServlet5Test.groovy deleted file mode 100644 index ecd73205b8..0000000000 --- a/instrumentation/servlet/servlet-5.0/javaagent/src/test/groovy/TomcatServlet5Test.groovy +++ /dev/null @@ -1,472 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.instrumentation.test.asserts.TraceAssert -import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint -import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.RequestDispatcherServlet -import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpResponse -import jakarta.servlet.Servlet -import jakarta.servlet.ServletException -import org.apache.catalina.AccessLog -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 org.apache.catalina.valves.ValveBase -import org.apache.tomcat.JarScanFilter -import org.apache.tomcat.JarScanType -import spock.lang.Shared -import spock.lang.Unroll -import test.AbstractServlet5Test -import test.TestServlet5 - -import java.nio.file.Files -import java.util.concurrent.TimeUnit -import java.util.concurrent.TimeoutException - -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.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.junit.jupiter.api.Assumptions.assumeTrue - -@Unroll -abstract class TomcatServlet5Test extends AbstractServlet5Test { - - static final ServerEndpoint ACCESS_LOG_SUCCESS = new ServerEndpoint("ACCESS_LOG_SUCCESS", - "success?access-log=true", SUCCESS.status, SUCCESS.body, false) - static final ServerEndpoint ACCESS_LOG_ERROR = new ServerEndpoint("ACCESS_LOG_ERROR", - "error-status?access-log=true", ERROR.status, ERROR.body, false) - - @Override - Throwable expectedException() { - new ServletException(EXCEPTION.body) - } - - @Override - boolean hasResponseSpan(ServerEndpoint endpoint) { - endpoint == NOT_FOUND || super.hasResponseSpan(endpoint) - } - - @Override - void responseSpan(TraceAssert trace, int index, Object parent, String method, ServerEndpoint endpoint) { - switch (endpoint) { - case NOT_FOUND: - sendErrorSpan(trace, index, parent) - break - } - super.responseSpan(trace, index, parent, method, endpoint) - } - - @Shared - def accessLogValue = new TestAccessLogValve() - - @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.host as StandardHost).errorReportValveClass = ErrorHandlerValve.name - (tomcatServer.host as StandardHost).getPipeline().addValve(accessLogValue) - - tomcatServer.start() - - return tomcatServer - } - - def setup() { - accessLogValue.loggedIds.clear() - } - - @Override - void stopServer(Tomcat server) { - server.stop() - server.destroy() - } - - @Override - String getContextPath() { - return "/tomcat-context" - } - - @Override - void addServlet(Context servletContext, String path, Class servlet) { - String name = UUID.randomUUID() - Tomcat.addServlet(servletContext, name, servlet.newInstance()) - servletContext.addServletMappingDecoded(path, name) - } - - def "access log has ids for #count requests"() { - given: - def request = request(ACCESS_LOG_SUCCESS, method) - - when: - List responses = (1..count).collect { - return client.execute(request).aggregate().join() - } - - then: - responses.each { response -> - assert response.status().code() == ACCESS_LOG_SUCCESS.status - assert response.contentUtf8() == ACCESS_LOG_SUCCESS.body - } - - and: - assertTraces(count) { - accessLogValue.waitForLoggedIds(count) - assert accessLogValue.loggedIds.size() == count - def loggedTraces = accessLogValue.loggedIds*.first - def loggedSpans = accessLogValue.loggedIds*.second - - (0..count - 1).each { - trace(it, 2) { - serverSpan(it, 0, null, null, "GET", ACCESS_LOG_SUCCESS) - controllerSpan(it, 1, span(0)) - } - - assert loggedTraces.contains(traces[it][0].traceId) - assert loggedSpans.contains(traces[it][0].spanId) - } - } - - where: - method = "GET" - count << [1, 4] // make multiple requests. - } - - def "access log has ids for error request"() { - setup: - assumeTrue(testError()) - - def request = request(ACCESS_LOG_ERROR, method) - def response = client.execute(request).aggregate().join() - - expect: - response.status().code() == ACCESS_LOG_ERROR.status - response.contentUtf8() == ACCESS_LOG_ERROR.body - - and: - def spanCount = 2 - if (errorEndpointUsesSendError()) { - spanCount++ - } - assertTraces(1) { - trace(0, spanCount) { - serverSpan(it, 0, null, null, method, ACCESS_LOG_ERROR) - def spanIndex = 1 - controllerSpan(it, spanIndex, span(spanIndex - 1)) - spanIndex++ - if (errorEndpointUsesSendError()) { - sendErrorSpan(it, spanIndex, span(spanIndex - 1)) - spanIndex++ - } - } - - accessLogValue.waitForLoggedIds(1) - def (String traceId, String spanId) = accessLogValue.loggedIds[0] - assert traces[0][0].traceId == traceId - assert traces[0][0].spanId == spanId - } - - where: - method = "GET" - } - - // FIXME: Add authentication tests back in... -// private setupAuthentication(Tomcat server, Context servletContext) { -// // Login Config -// LoginConfig authConfig = new LoginConfig() -// authConfig.setAuthMethod("BASIC") -// -// // adding constraint with role "test" -// SecurityConstraint constraint = new SecurityConstraint() -// constraint.addAuthRole("role") -// -// // add constraint to a collection with pattern /second -// SecurityCollection collection = new SecurityCollection() -// collection.addPattern("/auth/*") -// constraint.addCollection(collection) -// -// servletContext.setLoginConfig(authConfig) -// // does the context need a auth role too? -// servletContext.addSecurityRole("role") -// servletContext.addConstraint(constraint) -// -// // add tomcat users to realm -// MemoryRealm realm = new MemoryRealm() { -// protected void startInternal() { -// credentialHandler = new MessageDigestCredentialHandler() -// setState(LifecycleState.STARTING) -// } -// } -// realm.addUser(user, pass, "role") -// server.getEngine().setRealm(realm) -// -// servletContext.setLoginConfig(authConfig) -// } -} - -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 e) { - e.printStackTrace() - } - } -} - -class TestAccessLogValve extends ValveBase implements AccessLog { - final List> loggedIds = [] - - TestAccessLogValve() { - super(true) - } - - void log(Request request, Response response, long time) { - if (request.getParameter("access-log") == null) { - return - } - - synchronized (loggedIds) { - loggedIds.add(new Tuple2(request.getAttribute("trace_id"), - request.getAttribute("span_id"))) - loggedIds.notifyAll() - } - } - - void waitForLoggedIds(int expected) { - def timeout = TimeUnit.SECONDS.toMillis(20) - def startTime = System.currentTimeMillis() - def endTime = startTime + timeout - def toWait = timeout - synchronized (loggedIds) { - while (loggedIds.size() < expected && toWait > 0) { - loggedIds.wait(toWait) - toWait = endTime - System.currentTimeMillis() - } - if (toWait <= 0) { - throw new TimeoutException("Timeout waiting for " + expected + " access log ids, got " + loggedIds.size()) - } - } - } - - @Override - void setRequestAttributesEnabled(boolean requestAttributesEnabled) { - } - - @Override - boolean getRequestAttributesEnabled() { - return false - } - - @Override - void invoke(Request request, Response response) throws IOException, ServletException { - getNext().invoke(request, response) - } -} - -class TomcatServlet5TestSync extends TomcatServlet5Test { - - @Override - Class servlet() { - TestServlet5.Sync - } -} - -class TomcatServlet5TestAsync extends TomcatServlet5Test { - - @Override - Class servlet() { - TestServlet5.Async - } - - @Override - boolean errorEndpointUsesSendError() { - false - } -} - -class TomcatServlet5TestFakeAsync extends TomcatServlet5Test { - - @Override - Class servlet() { - TestServlet5.FakeAsync - } -} - -class TomcatServlet5TestForward extends TomcatDispatchTest { - @Override - Class servlet() { - TestServlet5.Sync // dispatch to sync servlet - } - - @Override - boolean testNotFound() { - false - } - - @Override - protected void setupServlets(Context context) { - super.setupServlets(context) - - addServlet(context, "/dispatch" + SUCCESS.path, RequestDispatcherServlet.Forward) - addServlet(context, "/dispatch" + QUERY_PARAM.path, RequestDispatcherServlet.Forward) - addServlet(context, "/dispatch" + REDIRECT.path, RequestDispatcherServlet.Forward) - addServlet(context, "/dispatch" + ERROR.path, RequestDispatcherServlet.Forward) - addServlet(context, "/dispatch" + EXCEPTION.path, RequestDispatcherServlet.Forward) - addServlet(context, "/dispatch" + AUTH_REQUIRED.path, RequestDispatcherServlet.Forward) - addServlet(context, "/dispatch" + CAPTURE_HEADERS.path, RequestDispatcherServlet.Forward) - addServlet(context, "/dispatch" + CAPTURE_PARAMETERS.path, RequestDispatcherServlet.Forward) - addServlet(context, "/dispatch" + INDEXED_CHILD.path, RequestDispatcherServlet.Forward) - addServlet(context, "/dispatch" + HTML_PRINT_WRITER.path, RequestDispatcherServlet.Forward) - addServlet(context, "/dispatch" + HTML_SERVLET_OUTPUT_STREAM.path, RequestDispatcherServlet.Forward) - } -} - -class TomcatServlet5TestInclude extends TomcatDispatchTest { - @Override - Class servlet() { - TestServlet5.Sync // dispatch to sync servlet - } - - @Override - boolean testNotFound() { - false - } - - @Override - boolean testRedirect() { - false - } - - @Override - boolean testCapturedHttpHeaders() { - false - } - - @Override - boolean testError() { - false - } - - @Override - protected void setupServlets(Context context) { - super.setupServlets(context) - - addServlet(context, "/dispatch" + SUCCESS.path, RequestDispatcherServlet.Include) - addServlet(context, "/dispatch" + QUERY_PARAM.path, RequestDispatcherServlet.Include) - addServlet(context, "/dispatch" + REDIRECT.path, RequestDispatcherServlet.Include) - addServlet(context, "/dispatch" + ERROR.path, RequestDispatcherServlet.Include) - addServlet(context, "/dispatch" + EXCEPTION.path, RequestDispatcherServlet.Include) - addServlet(context, "/dispatch" + AUTH_REQUIRED.path, RequestDispatcherServlet.Include) - addServlet(context, "/dispatch" + CAPTURE_HEADERS.path, RequestDispatcherServlet.Include) - addServlet(context, "/dispatch" + CAPTURE_PARAMETERS.path, RequestDispatcherServlet.Include) - addServlet(context, "/dispatch" + INDEXED_CHILD.path, RequestDispatcherServlet.Include) - addServlet(context, "/dispatch" + HTML_PRINT_WRITER.path, RequestDispatcherServlet.Include) - addServlet(context, "/dispatch" + HTML_SERVLET_OUTPUT_STREAM.path, RequestDispatcherServlet.Include) - } -} - -class TomcatServlet5TestDispatchImmediate extends TomcatDispatchTest { - @Override - Class servlet() { - TestServlet5.Sync - } - - @Override - boolean testNotFound() { - false - } - - @Override - protected void setupServlets(Context context) { - super.setupServlets(context) - - addServlet(context, "/dispatch" + SUCCESS.path, TestServlet5.DispatchImmediate) - addServlet(context, "/dispatch" + QUERY_PARAM.path, TestServlet5.DispatchImmediate) - addServlet(context, "/dispatch" + ERROR.path, TestServlet5.DispatchImmediate) - addServlet(context, "/dispatch" + EXCEPTION.path, TestServlet5.DispatchImmediate) - addServlet(context, "/dispatch" + REDIRECT.path, TestServlet5.DispatchImmediate) - addServlet(context, "/dispatch" + AUTH_REQUIRED.path, TestServlet5.DispatchImmediate) - addServlet(context, "/dispatch" + CAPTURE_HEADERS.path, TestServlet5.DispatchImmediate) - addServlet(context, "/dispatch" + CAPTURE_PARAMETERS.path, TestServlet5.DispatchImmediate) - addServlet(context, "/dispatch" + INDEXED_CHILD.path, TestServlet5.DispatchImmediate) - addServlet(context, "/dispatch" + HTML_PRINT_WRITER.path, TestServlet5.DispatchImmediate) - addServlet(context, "/dispatch" + HTML_SERVLET_OUTPUT_STREAM.path, TestServlet5.DispatchImmediate) - addServlet(context, "/dispatch/recursive", TestServlet5.DispatchRecursive) - } -} - -class TomcatServlet5TestDispatchAsync extends TomcatDispatchTest { - @Override - Class servlet() { - TestServlet5.Async - } - - @Override - protected void setupServlets(Context context) { - super.setupServlets(context) - - addServlet(context, "/dispatch" + SUCCESS.path, TestServlet5.DispatchAsync) - addServlet(context, "/dispatch" + QUERY_PARAM.path, TestServlet5.DispatchAsync) - addServlet(context, "/dispatch" + ERROR.path, TestServlet5.DispatchAsync) - addServlet(context, "/dispatch" + EXCEPTION.path, TestServlet5.DispatchAsync) - addServlet(context, "/dispatch" + REDIRECT.path, TestServlet5.DispatchAsync) - addServlet(context, "/dispatch" + AUTH_REQUIRED.path, TestServlet5.DispatchAsync) - addServlet(context, "/dispatch" + CAPTURE_HEADERS.path, TestServlet5.DispatchAsync) - addServlet(context, "/dispatch" + CAPTURE_PARAMETERS.path, TestServlet5.DispatchAsync) - addServlet(context, "/dispatch" + INDEXED_CHILD.path, TestServlet5.DispatchAsync) - addServlet(context, "/dispatch" + HTML_PRINT_WRITER.path, TestServlet5.DispatchAsync) - addServlet(context, "/dispatch" + HTML_SERVLET_OUTPUT_STREAM.path, TestServlet5.DispatchAsync) - addServlet(context, "/dispatch/recursive", TestServlet5.DispatchRecursive) - } - - @Override - boolean errorEndpointUsesSendError() { - false - } -} - -abstract class TomcatDispatchTest extends TomcatServlet5Test { - @Override - URI buildAddress() { - return new URI("http://localhost:$port$contextPath/dispatch/") - } -} diff --git a/instrumentation/servlet/servlet-5.0/javaagent/src/test/resources/realm.properties b/instrumentation/servlet/servlet-5.0/javaagent/src/test/resources/realm.properties deleted file mode 100644 index cacb917078..0000000000 --- a/instrumentation/servlet/servlet-5.0/javaagent/src/test/resources/realm.properties +++ /dev/null @@ -1 +0,0 @@ -user:password,role diff --git a/instrumentation/servlet/servlet-5.0/jetty11-testing/build.gradle.kts b/instrumentation/servlet/servlet-5.0/jetty11-testing/build.gradle.kts new file mode 100644 index 0000000000..3cc85cc031 --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/jetty11-testing/build.gradle.kts @@ -0,0 +1,26 @@ +plugins { + id("otel.javaagent-testing") +} + +dependencies { + testInstrumentation(project(":instrumentation:servlet:servlet-5.0:javaagent")) + testInstrumentation(project(":instrumentation:jetty:jetty-8.0:javaagent")) + testInstrumentation(project(":instrumentation:jetty:jetty-11.0:javaagent")) + testImplementation(project(":instrumentation:servlet:servlet-5.0:testing")) + + testLibrary("org.eclipse.jetty:jetty-server:11.0.0") + testLibrary("org.eclipse.jetty:jetty-servlet:11.0.0") + + latestDepTestLibrary("org.eclipse.jetty:jetty-server:11.+") // see jetty-12-testing module +} + +// Jetty 11 requires Java 11 +otelJava { + minJavaVersionSupported.set(JavaVersion.VERSION_11) +} + +tasks { + withType().configureEach { + jvmArgs("-Dotel.instrumentation.servlet.experimental.capture-request-parameters=test-parameter") + } +} diff --git a/instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/JettyServlet5AsyncTest.java b/instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/JettyServlet5AsyncTest.java new file mode 100644 index 0000000000..987b4977b5 --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/JettyServlet5AsyncTest.java @@ -0,0 +1,22 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.jetty; + +import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.TestServlet5; +import jakarta.servlet.Servlet; + +class JettyServlet5AsyncTest extends JettyServlet5Test { + + @Override + public Class servlet() { + return TestServlet5.Async.class; + } + + @Override + public boolean errorEndpointUsesSendError() { + return false; + } +} diff --git a/instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/JettyServlet5FakeAsyncTest.java b/instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/JettyServlet5FakeAsyncTest.java new file mode 100644 index 0000000000..8f630a7485 --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/JettyServlet5FakeAsyncTest.java @@ -0,0 +1,16 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.jetty; + +import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.TestServlet5; +import jakarta.servlet.Servlet; + +class JettyServlet5FakeAsyncTest extends JettyServlet5Test { + @Override + public Class servlet() { + return TestServlet5.FakeAsync.class; + } +} diff --git a/instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/JettyServlet5MappingTest.java b/instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/JettyServlet5MappingTest.java new file mode 100644 index 0000000000..8c9c96378c --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/JettyServlet5MappingTest.java @@ -0,0 +1,60 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.jetty; + +import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.AbstractServlet5MappingTest; +import jakarta.servlet.Servlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletContextHandler; + +class JettyServlet5MappingTest extends AbstractServlet5MappingTest { + + @Override + protected Server setupServer() throws Exception { + Server server = new Server(port); + ServletContextHandler handler = new ServletContextHandler(null, getContextPath()); + setupServlets(handler); + server.setHandler(handler); + server.start(); + return server; + } + + @Override + public void stopServer(Server server) throws Exception { + server.stop(); + server.destroy(); + } + + @Override + protected void setupServlets(ServletContextHandler handler) throws Exception { + super.setupServlets(handler); + + addServlet(handler, "/", DefaultServlet.class); + } + + @Override + public void addServlet( + ServletContextHandler servletContextHandler, String path, Class servlet) { + servletContextHandler.addServlet(servlet, path); + } + + @Override + public String getContextPath() { + return "/jetty-context"; + } + + public static class DefaultServlet extends HttpServlet { + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) + throws IOException { + response.sendError(404); + } + } +} diff --git a/instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/JettyServlet5SyncTest.java b/instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/JettyServlet5SyncTest.java new file mode 100644 index 0000000000..6f195555aa --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/JettyServlet5SyncTest.java @@ -0,0 +1,16 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.jetty; + +import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.TestServlet5; +import jakarta.servlet.Servlet; + +class JettyServlet5SyncTest extends JettyServlet5Test { + @Override + public Class servlet() { + return TestServlet5.Sync.class; + } +} diff --git a/instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/JettyServlet5Test.java b/instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/JettyServlet5Test.java new file mode 100644 index 0000000000..3c44dd8d70 --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/JettyServlet5Test.java @@ -0,0 +1,74 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.jetty; + +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions; +import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.AbstractServlet5Test; +import jakarta.servlet.Servlet; +import jakarta.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.io.Writer; +import java.net.InetSocketAddress; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.ErrorHandler; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.junit.jupiter.api.extension.RegisterExtension; + +public abstract class JettyServlet5Test + extends AbstractServlet5Test { + + @RegisterExtension + protected static final InstrumentationExtension testing = + HttpServerInstrumentationExtension.forAgent(); + + @Override + protected void configure(HttpServerTestOptions options) { + super.configure(options); + options.setTestNotFound(false); + options.setExpectedException(new IllegalStateException(EXCEPTION.getBody())); + options.setContextPath("/jetty-context"); + } + + @Override + protected Server setupServer() throws Exception { + Server jettyServer = new Server(new InetSocketAddress("localhost", port)); + + ServletContextHandler servletContext = new ServletContextHandler(null, getContextPath()); + servletContext.setErrorHandler( + new ErrorHandler() { + @Override + protected void handleErrorPage( + HttpServletRequest request, Writer writer, int code, String message) + throws IOException { + Throwable th = (Throwable) request.getAttribute("jakarta.servlet.error.exception"); + writer.write(th != null ? th.getMessage() : message); + } + }); + setupServlets(servletContext); + jettyServer.setHandler(servletContext); + + jettyServer.start(); + + return jettyServer; + } + + @Override + public void stopServer(Server server) throws Exception { + server.stop(); + server.destroy(); + } + + @Override + public void addServlet( + ServletContextHandler servletContext, String path, Class servlet) + throws Exception { + servletContext.addServlet(servlet, path); + } +} diff --git a/instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/JettyServletHandlerTest.java b/instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/JettyServletHandlerTest.java new file mode 100644 index 0000000000..45f0cb19aa --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/JettyServletHandlerTest.java @@ -0,0 +1,86 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.jetty; + +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions; +import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.AbstractServlet5Test; +import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.TestServlet5; +import io.opentelemetry.semconv.HttpAttributes; +import jakarta.servlet.Servlet; +import jakarta.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.io.Writer; +import java.util.HashSet; +import java.util.Set; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.handler.ErrorHandler; +import org.eclipse.jetty.servlet.ServletHandler; +import org.junit.jupiter.api.extension.RegisterExtension; + +class JettyServletHandlerTest extends AbstractServlet5Test { + + @RegisterExtension + protected static final InstrumentationExtension testing = + HttpServerInstrumentationExtension.forAgent(); + + @Override + protected void configure(HttpServerTestOptions options) { + super.configure(options); + options.setContextPath(""); + options.setTestNotFound(false); + options.setExpectedException(new IllegalStateException(EXCEPTION.getBody())); + options.setHttpAttributes( + serverEndpoint -> { + Set> attributes = + new HashSet<>(HttpServerTestOptions.DEFAULT_HTTP_ATTRIBUTES); + attributes.remove(HttpAttributes.HTTP_ROUTE); + return attributes; + }); + } + + @Override + protected Server setupServer() throws Exception { + Server server = new Server(port); + ServletHandler handler = new ServletHandler(); + server.setHandler(handler); + setupServlets(handler); + server.addBean( + new ErrorHandler() { + @Override + protected void handleErrorPage( + HttpServletRequest request, Writer writer, int code, String message) + throws IOException { + Throwable th = (Throwable) request.getAttribute("jakarta.servlet.error.exception"); + writer.write(th != null ? th.getMessage() : message); + } + }); + server.start(); + return server; + } + + @Override + public void addServlet( + ServletHandler servletHandler, String path, Class servlet) + throws Exception { + servletHandler.addServletWithMapping(servlet, path); + } + + @Override + public void stopServer(Server server) throws Exception { + server.stop(); + server.destroy(); + } + + @Override + public Class servlet() { + return TestServlet5.Sync.class; + } +} diff --git a/instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/dispatch/JettyDispatchTest.java b/instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/dispatch/JettyDispatchTest.java new file mode 100644 index 0000000000..21220fd2cf --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/dispatch/JettyDispatchTest.java @@ -0,0 +1,18 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.jetty.dispatch; + +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions; +import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.jetty.JettyServlet5Test; + +abstract class JettyDispatchTest extends JettyServlet5Test { + + @Override + protected void configure(HttpServerTestOptions options) { + super.configure(options); + options.setContextPath(getContextPath() + "/dispatch"); + } +} diff --git a/instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/dispatch/JettyServlet5DispatchAsyncTest.java b/instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/dispatch/JettyServlet5DispatchAsyncTest.java new file mode 100644 index 0000000000..65fb37463e --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/dispatch/JettyServlet5DispatchAsyncTest.java @@ -0,0 +1,54 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.jetty.dispatch; + +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.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.javaagent.instrumentation.servlet.v5_0.TestServlet5; +import jakarta.servlet.Servlet; +import org.eclipse.jetty.servlet.ServletContextHandler; + +class JettyServlet5DispatchAsyncTest extends JettyDispatchTest { + @Override + public Class servlet() { + return TestServlet5.Async.class; + } + + @Override + protected void setupServlets(ServletContextHandler context) throws Exception { + super.setupServlets(context); + addServlet( + context, "/dispatch" + HTML_PRINT_WRITER.getPath(), TestServlet5.DispatchAsync.class); + addServlet( + context, + "/dispatch" + HTML_SERVLET_OUTPUT_STREAM.getPath(), + TestServlet5.DispatchAsync.class); + addServlet(context, "/dispatch" + SUCCESS.getPath(), TestServlet5.DispatchAsync.class); + addServlet(context, "/dispatch" + QUERY_PARAM.getPath(), TestServlet5.DispatchAsync.class); + addServlet(context, "/dispatch" + ERROR.getPath(), TestServlet5.DispatchAsync.class); + addServlet(context, "/dispatch" + EXCEPTION.getPath(), TestServlet5.DispatchAsync.class); + addServlet(context, "/dispatch" + REDIRECT.getPath(), TestServlet5.DispatchAsync.class); + addServlet(context, "/dispatch" + AUTH_REQUIRED.getPath(), TestServlet5.DispatchAsync.class); + addServlet(context, "/dispatch" + CAPTURE_HEADERS.getPath(), TestServlet5.DispatchAsync.class); + addServlet( + context, "/dispatch" + CAPTURE_PARAMETERS.getPath(), TestServlet5.DispatchAsync.class); + addServlet(context, "/dispatch" + INDEXED_CHILD.getPath(), TestServlet5.DispatchAsync.class); + addServlet(context, "/dispatch/recursive", TestServlet5.DispatchRecursive.class); + } + + @Override + public boolean errorEndpointUsesSendError() { + return false; + } +} diff --git a/instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/dispatch/JettyServlet5DispatchImmediateTest.java b/instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/dispatch/JettyServlet5DispatchImmediateTest.java new file mode 100644 index 0000000000..22fdae2672 --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/dispatch/JettyServlet5DispatchImmediateTest.java @@ -0,0 +1,57 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.jetty.dispatch; + +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.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.javaagent.instrumentation.servlet.v5_0.TestServlet5; +import jakarta.servlet.Servlet; +import org.eclipse.jetty.servlet.ServletContextHandler; + +class JettyServlet5DispatchImmediateTest extends JettyDispatchTest { + @Override + public Class servlet() { + return TestServlet5.Async.class; + } + + @Override + public boolean errorEndpointUsesSendError() { + return false; + } + + @Override + protected void setupServlets(ServletContextHandler context) throws Exception { + super.setupServlets(context); + addServlet( + context, "/dispatch" + HTML_PRINT_WRITER.getPath(), TestServlet5.DispatchImmediate.class); + addServlet( + context, + "/dispatch" + HTML_SERVLET_OUTPUT_STREAM.getPath(), + TestServlet5.DispatchImmediate.class); + addServlet(context, "/dispatch" + SUCCESS.getPath(), TestServlet5.DispatchImmediate.class); + addServlet(context, "/dispatch" + QUERY_PARAM.getPath(), TestServlet5.DispatchImmediate.class); + addServlet(context, "/dispatch" + ERROR.getPath(), TestServlet5.DispatchImmediate.class); + addServlet(context, "/dispatch" + EXCEPTION.getPath(), TestServlet5.DispatchImmediate.class); + addServlet(context, "/dispatch" + REDIRECT.getPath(), TestServlet5.DispatchImmediate.class); + addServlet( + context, "/dispatch" + AUTH_REQUIRED.getPath(), TestServlet5.DispatchImmediate.class); + addServlet( + context, "/dispatch" + CAPTURE_HEADERS.getPath(), TestServlet5.DispatchImmediate.class); + addServlet( + context, "/dispatch" + CAPTURE_PARAMETERS.getPath(), TestServlet5.DispatchImmediate.class); + addServlet( + context, "/dispatch" + INDEXED_CHILD.getPath(), TestServlet5.DispatchImmediate.class); + addServlet(context, "/dispatch/recursive", TestServlet5.DispatchRecursive.class); + } +} diff --git a/instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/dispatch/JettyServlet5ForwardTest.java b/instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/dispatch/JettyServlet5ForwardTest.java new file mode 100644 index 0000000000..215e74dffd --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/dispatch/JettyServlet5ForwardTest.java @@ -0,0 +1,56 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.jetty.dispatch; + +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.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.javaagent.instrumentation.servlet.v5_0.RequestDispatcherServlet; +import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.TestServlet5; +import jakarta.servlet.Servlet; +import org.eclipse.jetty.servlet.ServletContextHandler; + +class JettyServlet5ForwardTest extends JettyDispatchTest { + @Override + public Class servlet() { + return TestServlet5.Sync.class; // dispatch to sync servlet + } + + @Override + protected void setupServlets(ServletContextHandler context) throws Exception { + super.setupServlets(context); + + addServlet(context, "/dispatch" + SUCCESS.getPath(), RequestDispatcherServlet.Forward.class); + addServlet( + context, "/dispatch" + HTML_PRINT_WRITER.getPath(), RequestDispatcherServlet.Forward.class); + addServlet( + context, + "/dispatch" + HTML_SERVLET_OUTPUT_STREAM.getPath(), + RequestDispatcherServlet.Forward.class); + addServlet( + context, "/dispatch" + QUERY_PARAM.getPath(), RequestDispatcherServlet.Forward.class); + addServlet(context, "/dispatch" + REDIRECT.getPath(), RequestDispatcherServlet.Forward.class); + addServlet(context, "/dispatch" + ERROR.getPath(), RequestDispatcherServlet.Forward.class); + addServlet(context, "/dispatch" + EXCEPTION.getPath(), RequestDispatcherServlet.Forward.class); + addServlet( + context, "/dispatch" + AUTH_REQUIRED.getPath(), RequestDispatcherServlet.Forward.class); + addServlet( + context, "/dispatch" + CAPTURE_HEADERS.getPath(), RequestDispatcherServlet.Forward.class); + addServlet( + context, + "/dispatch" + CAPTURE_PARAMETERS.getPath(), + RequestDispatcherServlet.Forward.class); + addServlet( + context, "/dispatch" + INDEXED_CHILD.getPath(), RequestDispatcherServlet.Forward.class); + } +} diff --git a/instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/dispatch/JettyServlet5IncludeTest.java b/instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/dispatch/JettyServlet5IncludeTest.java new file mode 100644 index 0000000000..20fbac892d --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/jetty11-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty/dispatch/JettyServlet5IncludeTest.java @@ -0,0 +1,65 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.jetty.dispatch; + +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.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.testing.junit.http.HttpServerTestOptions; +import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.RequestDispatcherServlet; +import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.TestServlet5; +import jakarta.servlet.Servlet; +import org.eclipse.jetty.servlet.ServletContextHandler; + +class JettyServlet5IncludeTest extends JettyDispatchTest { + @Override + public Class servlet() { + return TestServlet5.Sync.class; // dispatch to sync servlet + } + + @Override + protected void configure(HttpServerTestOptions options) { + super.configure(options); + options.setTestRedirect(false); + options.setTestCaptureHttpHeaders(false); + options.setTestError(false); + } + + @Override + protected void setupServlets(ServletContextHandler context) throws Exception { + super.setupServlets(context); + + addServlet(context, "/dispatch" + SUCCESS.getPath(), RequestDispatcherServlet.Include.class); + addServlet( + context, "/dispatch" + HTML_PRINT_WRITER.getPath(), RequestDispatcherServlet.Include.class); + addServlet( + context, + "/dispatch" + HTML_SERVLET_OUTPUT_STREAM.getPath(), + RequestDispatcherServlet.Include.class); + addServlet( + context, "/dispatch" + QUERY_PARAM.getPath(), RequestDispatcherServlet.Include.class); + addServlet(context, "/dispatch" + REDIRECT.getPath(), RequestDispatcherServlet.Include.class); + addServlet(context, "/dispatch" + ERROR.getPath(), RequestDispatcherServlet.Include.class); + addServlet(context, "/dispatch" + EXCEPTION.getPath(), RequestDispatcherServlet.Include.class); + addServlet( + context, "/dispatch" + AUTH_REQUIRED.getPath(), RequestDispatcherServlet.Include.class); + addServlet( + context, "/dispatch" + CAPTURE_HEADERS.getPath(), RequestDispatcherServlet.Include.class); + addServlet( + context, + "/dispatch" + CAPTURE_PARAMETERS.getPath(), + RequestDispatcherServlet.Include.class); + addServlet( + context, "/dispatch" + INDEXED_CHILD.getPath(), RequestDispatcherServlet.Include.class); + } +} diff --git a/instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/groovy/Jetty12Servlet5Test.groovy b/instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/groovy/Jetty12Servlet5Test.groovy deleted file mode 100644 index 545c1005e7..0000000000 --- a/instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/groovy/Jetty12Servlet5Test.groovy +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.RequestDispatcherServlet -import jakarta.servlet.Servlet -import jakarta.servlet.ServletException -import org.eclipse.jetty.ee10.servlet.ServletContextHandler -import org.eclipse.jetty.server.Request -import org.eclipse.jetty.server.Response -import org.eclipse.jetty.server.Server -import org.eclipse.jetty.util.Callback -import test.AbstractServlet5Test -import test.TestServlet5 - -import java.nio.charset.StandardCharsets - -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.QUERY_PARAM -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS - -abstract class Jetty12Servlet5Test extends AbstractServlet5Test { - - @Override - boolean testNotFound() { - false - } - - @Override - Throwable expectedException() { - new ServletException(EXCEPTION.body) - } - - @Override - Object startServer(int port) { - def jettyServer = new Server(port) - jettyServer.connectors.each { - it.setHost('localhost') - } - - ServletContextHandler servletContext = new ServletContextHandler(contextPath) - servletContext.errorHandler = new Request.Handler() { - - @Override - boolean handle(Request request, Response response, Callback callback) throws Exception { - String message = (String) request.getAttribute("org.eclipse.jetty.server.error_message") - if (message != null) { - response.write(true, StandardCharsets.UTF_8.encode(message), Callback.NOOP) - } - callback.succeeded() - return true - } - } - - setupServlets(servletContext) - jettyServer.setHandler(servletContext) - - jettyServer.start() - - return jettyServer - } - - @Override - void stopServer(Object serverObject) { - Server server = (Server) serverObject - server.stop() - server.destroy() - } - - @Override - String getContextPath() { - return "/jetty-context" - } - - @Override - void addServlet(Object handlerObject, String path, Class servlet) { - ServletContextHandler handler = (ServletContextHandler) handlerObject - handler.addServlet(servlet, path) - } -} - -class JettyServlet5TestSync extends Jetty12Servlet5Test { - - @Override - Class servlet() { - TestServlet5.Sync - } -} - -class JettyServlet5TestAsync extends Jetty12Servlet5Test { - - @Override - Class servlet() { - TestServlet5.Async - } - - @Override - boolean errorEndpointUsesSendError() { - false - } -} - -class JettyServlet5TestFakeAsync extends Jetty12Servlet5Test { - - @Override - Class servlet() { - TestServlet5.FakeAsync - } -} - -class JettyServlet5TestForward extends JettyDispatchTest { - @Override - Class servlet() { - TestServlet5.Sync // dispatch to sync servlet - } - - @Override - protected void setupServlets(Object context) { - super.setupServlets(context) - - addServlet(context, "/dispatch" + SUCCESS.path, RequestDispatcherServlet.Forward) - addServlet(context, "/dispatch" + HTML_PRINT_WRITER.path, RequestDispatcherServlet.Forward) - addServlet(context, "/dispatch" + HTML_SERVLET_OUTPUT_STREAM.path, RequestDispatcherServlet.Forward) - addServlet(context, "/dispatch" + QUERY_PARAM.path, RequestDispatcherServlet.Forward) - addServlet(context, "/dispatch" + REDIRECT.path, RequestDispatcherServlet.Forward) - addServlet(context, "/dispatch" + ERROR.path, RequestDispatcherServlet.Forward) - addServlet(context, "/dispatch" + EXCEPTION.path, RequestDispatcherServlet.Forward) - addServlet(context, "/dispatch" + AUTH_REQUIRED.path, RequestDispatcherServlet.Forward) - addServlet(context, "/dispatch" + CAPTURE_HEADERS.path, RequestDispatcherServlet.Forward) - addServlet(context, "/dispatch" + CAPTURE_PARAMETERS.path, RequestDispatcherServlet.Forward) - addServlet(context, "/dispatch" + INDEXED_CHILD.path, RequestDispatcherServlet.Forward) - } -} - -class JettyServlet5TestInclude extends JettyDispatchTest { - @Override - Class servlet() { - TestServlet5.Sync // dispatch to sync servlet - } - - @Override - boolean testRedirect() { - false - } - - @Override - boolean testCapturedHttpHeaders() { - false - } - - @Override - boolean testError() { - false - } - - @Override - protected void setupServlets(Object context) { - super.setupServlets(context) - - addServlet(context, "/dispatch" + SUCCESS.path, RequestDispatcherServlet.Include) - addServlet(context, "/dispatch" + HTML_PRINT_WRITER.path, RequestDispatcherServlet.Include) - addServlet(context, "/dispatch" + HTML_SERVLET_OUTPUT_STREAM.path, RequestDispatcherServlet.Include) - addServlet(context, "/dispatch" + QUERY_PARAM.path, RequestDispatcherServlet.Include) - addServlet(context, "/dispatch" + REDIRECT.path, RequestDispatcherServlet.Include) - addServlet(context, "/dispatch" + ERROR.path, RequestDispatcherServlet.Include) - addServlet(context, "/dispatch" + EXCEPTION.path, RequestDispatcherServlet.Include) - addServlet(context, "/dispatch" + AUTH_REQUIRED.path, RequestDispatcherServlet.Include) - addServlet(context, "/dispatch" + CAPTURE_HEADERS.path, RequestDispatcherServlet.Include) - addServlet(context, "/dispatch" + CAPTURE_PARAMETERS.path, RequestDispatcherServlet.Include) - addServlet(context, "/dispatch" + INDEXED_CHILD.path, RequestDispatcherServlet.Include) - } -} - - -class JettyServlet5TestDispatchImmediate extends JettyDispatchTest { - @Override - Class servlet() { - TestServlet5.Sync - } - - @Override - protected void setupServlets(Object context) { - super.setupServlets(context) - addServlet(context, "/dispatch" + HTML_PRINT_WRITER.path, TestServlet5.DispatchImmediate) - addServlet(context, "/dispatch" + HTML_SERVLET_OUTPUT_STREAM.path, TestServlet5.DispatchImmediate) - addServlet(context, "/dispatch" + SUCCESS.path, TestServlet5.DispatchImmediate) - addServlet(context, "/dispatch" + QUERY_PARAM.path, TestServlet5.DispatchImmediate) - addServlet(context, "/dispatch" + ERROR.path, TestServlet5.DispatchImmediate) - addServlet(context, "/dispatch" + EXCEPTION.path, TestServlet5.DispatchImmediate) - addServlet(context, "/dispatch" + REDIRECT.path, TestServlet5.DispatchImmediate) - addServlet(context, "/dispatch" + AUTH_REQUIRED.path, TestServlet5.DispatchImmediate) - addServlet(context, "/dispatch" + CAPTURE_HEADERS.path, TestServlet5.DispatchImmediate) - addServlet(context, "/dispatch" + CAPTURE_PARAMETERS.path, TestServlet5.DispatchImmediate) - addServlet(context, "/dispatch" + INDEXED_CHILD.path, TestServlet5.DispatchImmediate) - addServlet(context, "/dispatch/recursive", TestServlet5.DispatchRecursive) - } -} - -class JettyServlet5TestDispatchAsync extends JettyDispatchTest { - @Override - Class servlet() { - TestServlet5.Async - } - - @Override - protected void setupServlets(Object context) { - super.setupServlets(context) - - addServlet(context, "/dispatch" + SUCCESS.path, TestServlet5.DispatchAsync) - addServlet(context, "/dispatch" + HTML_PRINT_WRITER.path, TestServlet5.DispatchAsync) - addServlet(context, "/dispatch" + HTML_SERVLET_OUTPUT_STREAM.path, TestServlet5.DispatchAsync) - addServlet(context, "/dispatch" + QUERY_PARAM.path, TestServlet5.DispatchAsync) - addServlet(context, "/dispatch" + ERROR.path, TestServlet5.DispatchAsync) - addServlet(context, "/dispatch" + EXCEPTION.path, TestServlet5.DispatchAsync) - addServlet(context, "/dispatch" + REDIRECT.path, TestServlet5.DispatchAsync) - addServlet(context, "/dispatch" + AUTH_REQUIRED.path, TestServlet5.DispatchAsync) - addServlet(context, "/dispatch" + CAPTURE_HEADERS.path, TestServlet5.DispatchAsync) - addServlet(context, "/dispatch" + CAPTURE_PARAMETERS.path, TestServlet5.DispatchAsync) - addServlet(context, "/dispatch" + INDEXED_CHILD.path, TestServlet5.DispatchAsync) - addServlet(context, "/dispatch/recursive", TestServlet5.DispatchRecursive) - } - - @Override - boolean errorEndpointUsesSendError() { - false - } -} - -abstract class JettyDispatchTest extends Jetty12Servlet5Test { - @Override - URI buildAddress() { - return new URI("http://localhost:$port$contextPath/dispatch/") - } -} diff --git a/instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/Jetty12Servlet5AsyncTest.java b/instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/Jetty12Servlet5AsyncTest.java new file mode 100644 index 0000000000..5542010493 --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/Jetty12Servlet5AsyncTest.java @@ -0,0 +1,22 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.jetty12; + +import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.TestServlet5; +import jakarta.servlet.Servlet; + +class Jetty12Servlet5AsyncTest extends Jetty12Servlet5Test { + + @Override + public Class servlet() { + return TestServlet5.Async.class; + } + + @Override + public boolean errorEndpointUsesSendError() { + return false; + } +} diff --git a/instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/Jetty12Servlet5FakeAsyncTest.java b/instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/Jetty12Servlet5FakeAsyncTest.java new file mode 100644 index 0000000000..41585b1f2b --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/Jetty12Servlet5FakeAsyncTest.java @@ -0,0 +1,16 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.jetty12; + +import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.TestServlet5; +import jakarta.servlet.Servlet; + +class Jetty12Servlet5FakeAsyncTest extends Jetty12Servlet5Test { + @Override + public Class servlet() { + return TestServlet5.FakeAsync.class; + } +} diff --git a/instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/Jetty12Servlet5SyncTest.java b/instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/Jetty12Servlet5SyncTest.java new file mode 100644 index 0000000000..7de861176b --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/Jetty12Servlet5SyncTest.java @@ -0,0 +1,16 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.jetty12; + +import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.TestServlet5; +import jakarta.servlet.Servlet; + +class Jetty12Servlet5SyncTest extends Jetty12Servlet5Test { + @Override + public Class servlet() { + return TestServlet5.Sync.class; + } +} diff --git a/instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/Jetty12Servlet5Test.java b/instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/Jetty12Servlet5Test.java new file mode 100644 index 0000000000..1eba647f9f --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/Jetty12Servlet5Test.java @@ -0,0 +1,71 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.jetty12; + +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions; +import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.AbstractServlet5Test; +import jakarta.servlet.Servlet; +import java.net.InetSocketAddress; +import java.nio.charset.StandardCharsets; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.Callback; +import org.junit.jupiter.api.extension.RegisterExtension; + +public abstract class Jetty12Servlet5Test + extends AbstractServlet5Test { + + @RegisterExtension + protected static final InstrumentationExtension testing = + HttpServerInstrumentationExtension.forAgent(); + + @Override + protected void configure(HttpServerTestOptions options) { + super.configure(options); + options.setTestNotFound(false); + options.setExpectedException(new IllegalStateException(EXCEPTION.getBody())); + options.setContextPath("/jetty-context"); + } + + @Override + protected Server setupServer() throws Exception { + Server jettyServer = new Server(new InetSocketAddress("localhost", port)); + + ServletContextHandler servletContext = new ServletContextHandler(getContextPath()); + servletContext.setErrorHandler( + (request, response, callback) -> { + String message = (String) request.getAttribute("org.eclipse.jetty.server.error_message"); + if (message != null) { + response.write(true, StandardCharsets.UTF_8.encode(message), Callback.NOOP); + } + callback.succeeded(); + return true; + }); + setupServlets(servletContext); + jettyServer.setHandler(servletContext); + + jettyServer.start(); + + return jettyServer; + } + + @Override + public void stopServer(Server server) throws Exception { + server.stop(); + server.destroy(); + } + + @Override + public void addServlet( + ServletContextHandler servletContext, String path, Class servlet) + throws Exception { + servletContext.addServlet(servlet, path); + } +} diff --git a/instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/dispatch/Jetty12DispatchTest.java b/instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/dispatch/Jetty12DispatchTest.java new file mode 100644 index 0000000000..9f43cd2311 --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/dispatch/Jetty12DispatchTest.java @@ -0,0 +1,18 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.jetty12.dispatch; + +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions; +import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.jetty12.Jetty12Servlet5Test; + +abstract class Jetty12DispatchTest extends Jetty12Servlet5Test { + + @Override + protected void configure(HttpServerTestOptions options) { + super.configure(options); + options.setContextPath(getContextPath() + "/dispatch"); + } +} diff --git a/instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/dispatch/Jetty12Servlet5DispatchAsyncTest.java b/instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/dispatch/Jetty12Servlet5DispatchAsyncTest.java new file mode 100644 index 0000000000..ad8b730fc0 --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/dispatch/Jetty12Servlet5DispatchAsyncTest.java @@ -0,0 +1,54 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.jetty12.dispatch; + +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.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.javaagent.instrumentation.servlet.v5_0.TestServlet5; +import jakarta.servlet.Servlet; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; + +class Jetty12Servlet5DispatchAsyncTest extends Jetty12DispatchTest { + @Override + public Class servlet() { + return TestServlet5.Async.class; + } + + @Override + protected void setupServlets(ServletContextHandler context) throws Exception { + super.setupServlets(context); + addServlet( + context, "/dispatch" + HTML_PRINT_WRITER.getPath(), TestServlet5.DispatchAsync.class); + addServlet( + context, + "/dispatch" + HTML_SERVLET_OUTPUT_STREAM.getPath(), + TestServlet5.DispatchAsync.class); + addServlet(context, "/dispatch" + SUCCESS.getPath(), TestServlet5.DispatchAsync.class); + addServlet(context, "/dispatch" + QUERY_PARAM.getPath(), TestServlet5.DispatchAsync.class); + addServlet(context, "/dispatch" + ERROR.getPath(), TestServlet5.DispatchAsync.class); + addServlet(context, "/dispatch" + EXCEPTION.getPath(), TestServlet5.DispatchAsync.class); + addServlet(context, "/dispatch" + REDIRECT.getPath(), TestServlet5.DispatchAsync.class); + addServlet(context, "/dispatch" + AUTH_REQUIRED.getPath(), TestServlet5.DispatchAsync.class); + addServlet(context, "/dispatch" + CAPTURE_HEADERS.getPath(), TestServlet5.DispatchAsync.class); + addServlet( + context, "/dispatch" + CAPTURE_PARAMETERS.getPath(), TestServlet5.DispatchAsync.class); + addServlet(context, "/dispatch" + INDEXED_CHILD.getPath(), TestServlet5.DispatchAsync.class); + addServlet(context, "/dispatch/recursive", TestServlet5.DispatchRecursive.class); + } + + @Override + public boolean errorEndpointUsesSendError() { + return false; + } +} diff --git a/instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/dispatch/Jetty12Servlet5DispatchImmediateTest.java b/instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/dispatch/Jetty12Servlet5DispatchImmediateTest.java new file mode 100644 index 0000000000..f0b7cf5c2e --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/dispatch/Jetty12Servlet5DispatchImmediateTest.java @@ -0,0 +1,57 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.jetty12.dispatch; + +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.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.javaagent.instrumentation.servlet.v5_0.TestServlet5; +import jakarta.servlet.Servlet; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; + +class Jetty12Servlet5DispatchImmediateTest extends Jetty12DispatchTest { + @Override + public Class servlet() { + return TestServlet5.Async.class; + } + + @Override + public boolean errorEndpointUsesSendError() { + return false; + } + + @Override + protected void setupServlets(ServletContextHandler context) throws Exception { + super.setupServlets(context); + addServlet( + context, "/dispatch" + HTML_PRINT_WRITER.getPath(), TestServlet5.DispatchImmediate.class); + addServlet( + context, + "/dispatch" + HTML_SERVLET_OUTPUT_STREAM.getPath(), + TestServlet5.DispatchImmediate.class); + addServlet(context, "/dispatch" + SUCCESS.getPath(), TestServlet5.DispatchImmediate.class); + addServlet(context, "/dispatch" + QUERY_PARAM.getPath(), TestServlet5.DispatchImmediate.class); + addServlet(context, "/dispatch" + ERROR.getPath(), TestServlet5.DispatchImmediate.class); + addServlet(context, "/dispatch" + EXCEPTION.getPath(), TestServlet5.DispatchImmediate.class); + addServlet(context, "/dispatch" + REDIRECT.getPath(), TestServlet5.DispatchImmediate.class); + addServlet( + context, "/dispatch" + AUTH_REQUIRED.getPath(), TestServlet5.DispatchImmediate.class); + addServlet( + context, "/dispatch" + CAPTURE_HEADERS.getPath(), TestServlet5.DispatchImmediate.class); + addServlet( + context, "/dispatch" + CAPTURE_PARAMETERS.getPath(), TestServlet5.DispatchImmediate.class); + addServlet( + context, "/dispatch" + INDEXED_CHILD.getPath(), TestServlet5.DispatchImmediate.class); + addServlet(context, "/dispatch/recursive", TestServlet5.DispatchRecursive.class); + } +} diff --git a/instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/dispatch/Jetty12Servlet5ForwardTest.java b/instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/dispatch/Jetty12Servlet5ForwardTest.java new file mode 100644 index 0000000000..88d3872015 --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/dispatch/Jetty12Servlet5ForwardTest.java @@ -0,0 +1,56 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.jetty12.dispatch; + +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.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.javaagent.instrumentation.servlet.v5_0.RequestDispatcherServlet; +import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.TestServlet5; +import jakarta.servlet.Servlet; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; + +class Jetty12Servlet5ForwardTest extends Jetty12DispatchTest { + @Override + public Class servlet() { + return TestServlet5.Sync.class; // dispatch to sync servlet + } + + @Override + protected void setupServlets(ServletContextHandler context) throws Exception { + super.setupServlets(context); + + addServlet(context, "/dispatch" + SUCCESS.getPath(), RequestDispatcherServlet.Forward.class); + addServlet( + context, "/dispatch" + HTML_PRINT_WRITER.getPath(), RequestDispatcherServlet.Forward.class); + addServlet( + context, + "/dispatch" + HTML_SERVLET_OUTPUT_STREAM.getPath(), + RequestDispatcherServlet.Forward.class); + addServlet( + context, "/dispatch" + QUERY_PARAM.getPath(), RequestDispatcherServlet.Forward.class); + addServlet(context, "/dispatch" + REDIRECT.getPath(), RequestDispatcherServlet.Forward.class); + addServlet(context, "/dispatch" + ERROR.getPath(), RequestDispatcherServlet.Forward.class); + addServlet(context, "/dispatch" + EXCEPTION.getPath(), RequestDispatcherServlet.Forward.class); + addServlet( + context, "/dispatch" + AUTH_REQUIRED.getPath(), RequestDispatcherServlet.Forward.class); + addServlet( + context, "/dispatch" + CAPTURE_HEADERS.getPath(), RequestDispatcherServlet.Forward.class); + addServlet( + context, + "/dispatch" + CAPTURE_PARAMETERS.getPath(), + RequestDispatcherServlet.Forward.class); + addServlet( + context, "/dispatch" + INDEXED_CHILD.getPath(), RequestDispatcherServlet.Forward.class); + } +} diff --git a/instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/dispatch/Jetty12Servlet5IncludeTest.java b/instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/dispatch/Jetty12Servlet5IncludeTest.java new file mode 100644 index 0000000000..4cb653d213 --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/jetty12-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/jetty12/dispatch/Jetty12Servlet5IncludeTest.java @@ -0,0 +1,65 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.jetty12.dispatch; + +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.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.testing.junit.http.HttpServerTestOptions; +import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.RequestDispatcherServlet; +import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.TestServlet5; +import jakarta.servlet.Servlet; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; + +class Jetty12Servlet5IncludeTest extends Jetty12DispatchTest { + @Override + public Class servlet() { + return TestServlet5.Sync.class; // dispatch to sync servlet + } + + @Override + protected void configure(HttpServerTestOptions options) { + super.configure(options); + options.setTestRedirect(false); + options.setTestCaptureHttpHeaders(false); + options.setTestError(false); + } + + @Override + protected void setupServlets(ServletContextHandler context) throws Exception { + super.setupServlets(context); + + addServlet(context, "/dispatch" + SUCCESS.getPath(), RequestDispatcherServlet.Include.class); + addServlet( + context, "/dispatch" + HTML_PRINT_WRITER.getPath(), RequestDispatcherServlet.Include.class); + addServlet( + context, + "/dispatch" + HTML_SERVLET_OUTPUT_STREAM.getPath(), + RequestDispatcherServlet.Include.class); + addServlet( + context, "/dispatch" + QUERY_PARAM.getPath(), RequestDispatcherServlet.Include.class); + addServlet(context, "/dispatch" + REDIRECT.getPath(), RequestDispatcherServlet.Include.class); + addServlet(context, "/dispatch" + ERROR.getPath(), RequestDispatcherServlet.Include.class); + addServlet(context, "/dispatch" + EXCEPTION.getPath(), RequestDispatcherServlet.Include.class); + addServlet( + context, "/dispatch" + AUTH_REQUIRED.getPath(), RequestDispatcherServlet.Include.class); + addServlet( + context, "/dispatch" + CAPTURE_HEADERS.getPath(), RequestDispatcherServlet.Include.class); + addServlet( + context, + "/dispatch" + CAPTURE_PARAMETERS.getPath(), + RequestDispatcherServlet.Include.class); + addServlet( + context, "/dispatch" + INDEXED_CHILD.getPath(), RequestDispatcherServlet.Include.class); + } +} diff --git a/instrumentation/servlet/servlet-5.0/testing/src/main/groovy/test/AbstractServlet5Test.groovy b/instrumentation/servlet/servlet-5.0/testing/src/main/groovy/test/AbstractServlet5Test.groovy deleted file mode 100644 index 2fd99b2910..0000000000 --- a/instrumentation/servlet/servlet-5.0/testing/src/main/groovy/test/AbstractServlet5Test.groovy +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package test - -import io.opentelemetry.api.trace.SpanKind -import io.opentelemetry.instrumentation.api.internal.HttpConstants -import io.opentelemetry.instrumentation.test.AgentTestTrait -import io.opentelemetry.instrumentation.test.asserts.TraceAssert -import io.opentelemetry.instrumentation.test.base.HttpServerTest -import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint -import io.opentelemetry.javaagent.bootstrap.servlet.ExperimentalSnippetHolder -import io.opentelemetry.semconv.HttpAttributes -import jakarta.servlet.Servlet - -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.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 - -abstract class AbstractServlet5Test extends HttpServerTest implements AgentTestTrait { - @Override - URI buildAddress() { - return new URI("http://localhost:$port$contextPath/") - } - - // FIXME: Add authentication tests back in... -// @Shared -// protected String user = "user" -// @Shared -// protected String pass = "password" - - abstract Class servlet() - - abstract void addServlet(CONTEXT context, String path, Class servlet) - - public static final ServerEndpoint HTML_PRINT_WRITER = - new ServerEndpoint("HTML_PRINT_WRITER", "htmlPrintWriter", - 200, - "\n" - + "\n" - + "\n" - + " \n" - + " Title\n" - + "\n" - + "\n" - + "

test works

\n" - + "\n" - + "") - public static final ServerEndpoint HTML_SERVLET_OUTPUT_STREAM = - new ServerEndpoint("HTML_SERVLET_OUTPUT_STREAM", "htmlServletOutputStream", - 200, - "\n" - + "\n" - + "\n" - + " \n" - + " Title\n" - + "\n" - + "\n" - + "

test works

\n" - + "\n" - + "") - - 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, INDEXED_CHILD.path, servlet) - addServlet(context, CAPTURE_HEADERS.path, servlet) - addServlet(context, CAPTURE_PARAMETERS.path, servlet) - addServlet(context, HTML_PRINT_WRITER.path, servlet) - addServlet(context, HTML_SERVLET_OUTPUT_STREAM.path, servlet) - } - - @Override - boolean testCapturedRequestParameters() { - true - } - - boolean errorEndpointUsesSendError() { - true - } - - @Override - String expectedHttpRoute(ServerEndpoint endpoint, String method) { - // no need to compute route if we're not expecting it - if (!httpAttributes(endpoint).contains(HttpAttributes.HTTP_ROUTE)) { - return null - } - if (method == HttpConstants._OTHER) { - return endpoint.resolvePath(address).path - } - switch (endpoint) { - case NOT_FOUND: - return getContextPath() + "/*" - default: - return super.expectedHttpRoute(endpoint, method) - } - } - - @Override - boolean hasResponseCustomizer(ServerEndpoint endpoint) { - true - } - - @Override - boolean hasResponseSpan(ServerEndpoint endpoint) { - endpoint == REDIRECT || (endpoint == ERROR && errorEndpointUsesSendError()) - } - - @Override - void responseSpan(TraceAssert trace, int index, Object parent, String method, ServerEndpoint endpoint) { - switch (endpoint) { - case REDIRECT: - redirectSpan(trace, index, parent) - break - case ERROR: - sendErrorSpan(trace, index, parent) - break - } - } - - def "snippet injection with ServletOutputStream"() { - setup: - ExperimentalSnippetHolder.setSnippet("\n ") - def request = request(HTML_SERVLET_OUTPUT_STREAM, "GET") - def response = client.execute(request).aggregate().join() - - expect: - response.status().code() == HTML_SERVLET_OUTPUT_STREAM.status - String result = "\n" + - "\n" + - "\n" + - " \n" + - " \n" + - " Title\n" + - "\n" + - "\n" + - "

test works

\n" + - "\n" + - "" - response.contentUtf8() == result - response.headers().contentLength() == result.length() - - cleanup: - ExperimentalSnippetHolder.setSnippet("") - - def expectedRoute = expectedHttpRoute(HTML_SERVLET_OUTPUT_STREAM, "GET") - assertTraces(1) { - trace(0, 2) { - span(0) { - name "GET" + (expectedRoute != null ? " " + expectedRoute : "") - kind SpanKind.SERVER - hasNoParent() - } - span(1) { - name "controller" - kind SpanKind.INTERNAL - childOf span(0) - } - } - } - } - - def "snippet injection with PrintWriter"() { - setup: - ExperimentalSnippetHolder.setSnippet("\n ") - def request = request(HTML_PRINT_WRITER, "GET") - def response = client.execute(request).aggregate().join() - - expect: - response.status().code() == HTML_PRINT_WRITER.status - String result = "\n" + - "\n" + - "\n" + - " \n" + - " \n" + - " Title\n" + - "\n" + - "\n" + - "

test works

\n" + - "\n" + - "" - - response.contentUtf8() == result - response.headers().contentLength() == result.length() - - cleanup: - ExperimentalSnippetHolder.setSnippet("") - - def expectedRoute = expectedHttpRoute(HTML_PRINT_WRITER, "GET") - assertTraces(1) { - trace(0, 2) { - span(0) { - name "GET" + (expectedRoute != null ? " " + expectedRoute : "") - kind SpanKind.SERVER - hasNoParent() - } - span(1) { - name "controller" - kind SpanKind.INTERNAL - childOf span(0) - } - } - } - } -} diff --git a/instrumentation/servlet/servlet-5.0/testing/src/main/groovy/test/TestServlet5.groovy b/instrumentation/servlet/servlet-5.0/testing/src/main/groovy/test/TestServlet5.groovy deleted file mode 100644 index 48f60f121e..0000000000 --- a/instrumentation/servlet/servlet-5.0/testing/src/main/groovy/test/TestServlet5.groovy +++ /dev/null @@ -1,294 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package test - -import io.opentelemetry.instrumentation.test.base.HttpServerTest -import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint -import jakarta.servlet.RequestDispatcher -import jakarta.servlet.ServletException -import jakarta.servlet.annotation.WebServlet -import jakarta.servlet.http.HttpServlet -import jakarta.servlet.http.HttpServletRequest -import jakarta.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.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.QUERY_PARAM -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT -import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS - -class TestServlet5 { - - @WebServlet - static class Sync extends HttpServlet { - @Override - protected void service(HttpServletRequest req, HttpServletResponse resp) { - String servletPath = req.getAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH) - if (servletPath == null) { - servletPath = req.servletPath - } - ServerEndpoint endpoint = ServerEndpoint.forPath(servletPath) - HttpServerTest.controller(endpoint) { - resp.contentType = "text/plain" - switch (endpoint) { - case SUCCESS: - resp.status = endpoint.status - resp.writer.print(endpoint.body) - break - case INDEXED_CHILD: - resp.status = endpoint.status - endpoint.collectSpanAttributes { req.getParameter(it) } - 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 CAPTURE_PARAMETERS: - req.setCharacterEncoding("UTF8") - def value = req.getParameter("test-parameter") - if (value != "test value õäöü") { - throw new ServletException("request parameter does not have expected value " + value) - } - resp.status = endpoint.status - resp.writer.print(endpoint.body) - break - case ERROR: - resp.sendError(endpoint.status, endpoint.body) - break - case EXCEPTION: - throw new ServletException(endpoint.body) - case AbstractServlet5Test.HTML_PRINT_WRITER: - resp.contentType = "text/html" - resp.status = endpoint.status - resp.setContentLengthLong(endpoint.body.length()) - resp.writer.print(endpoint.body) - break - case AbstractServlet5Test.HTML_SERVLET_OUTPUT_STREAM: - resp.contentType = "text/html" - resp.status = endpoint.status - resp.setContentLength(endpoint.body.length()) - byte[] body = endpoint.body.getBytes() - resp.getOutputStream().write(body, 0, body.length) - break - } - } - } - } - - @WebServlet(asyncSupported = true) - static class Async 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) - context.complete() - break - case INDEXED_CHILD: - endpoint.collectSpanAttributes { req.getParameter(it) } - resp.status = endpoint.status - context.complete() - break - case QUERY_PARAM: - resp.status = endpoint.status - resp.writer.print(req.queryString) - context.complete() - break - case REDIRECT: - resp.sendRedirect(endpoint.body) - context.complete() - break - case CAPTURE_HEADERS: - resp.setHeader("X-Test-Response", req.getHeader("X-Test-Request")) - resp.status = endpoint.status - resp.writer.print(endpoint.body) - context.complete() - break - case CAPTURE_PARAMETERS: - req.setCharacterEncoding("UTF8") - def value = req.getParameter("test-parameter") - if (value != "test value õäöü") { - throw new ServletException("request parameter does not have expected value " + value) - } - resp.status = endpoint.status - resp.writer.print(endpoint.body) - context.complete() - break - case ERROR: - resp.status = endpoint.status - resp.writer.print(endpoint.body) -// resp.sendError(endpoint.status, endpoint.body) - context.complete() - break - case EXCEPTION: - resp.status = endpoint.status - def writer = resp.writer - writer.print(endpoint.body) - if (req.getClass().getName().contains("catalina")) { - // on tomcat close the writer to ensure response is sent immediately, otherwise - // there is a chance that tomcat resets the connection before the response is sent - writer.close() - } - throw new ServletException(endpoint.body) - break - case AbstractServlet5Test.HTML_PRINT_WRITER: - resp.contentType = "text/html" - resp.status = endpoint.status - resp.setContentLength(endpoint.body.length()) - resp.writer.print(endpoint.body) - context.complete() - break - case AbstractServlet5Test.HTML_SERVLET_OUTPUT_STREAM: - resp.contentType = "text/html" - resp.status = endpoint.status - resp.getOutputStream().print(endpoint.body) - context.complete() - break - - } - } - } finally { - latch.countDown() - } - } - latch.await() - } - } - - @WebServlet(asyncSupported = true) - static class FakeAsync extends HttpServlet { - @Override - protected void service(HttpServletRequest req, HttpServletResponse resp) { - def context = req.startAsync() - try { - ServerEndpoint endpoint = ServerEndpoint.forPath(req.servletPath) - 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 CAPTURE_PARAMETERS: - req.setCharacterEncoding("UTF8") - def value = req.getParameter("test-parameter") - if (value != "test value õäöü") { - throw new ServletException("request parameter does not have expected value " + value) - } - resp.status = endpoint.status - resp.writer.print(endpoint.body) - break - case ERROR: - resp.sendError(endpoint.status, endpoint.body) - break - case EXCEPTION: - resp.status = endpoint.status - resp.writer.print(endpoint.body) - throw new ServletException(endpoint.body) - case AbstractServlet5Test.HTML_PRINT_WRITER: - // intentionally testing setting status before contentType here to cover that case somewhere - resp.status = endpoint.status - resp.contentType = "text/html" - resp.writer.print(endpoint.body) - break - case AbstractServlet5Test.HTML_SERVLET_OUTPUT_STREAM: - resp.contentType = "text/html" - resp.status = endpoint.status - resp.getOutputStream().print(endpoint.body) - break - } - } - } finally { - context.complete() - } - } - } - - @WebServlet(asyncSupported = true) - static class DispatchImmediate extends HttpServlet { - @Override - protected void service(HttpServletRequest req, HttpServletResponse resp) { - def target = req.servletPath.replace("/dispatch", "") - if (req.queryString != null) { - target += "?" + req.queryString - } - req.startAsync().dispatch(target) - } - } - - @WebServlet(asyncSupported = true) - static class DispatchAsync extends HttpServlet { - @Override - protected void service(HttpServletRequest req, HttpServletResponse resp) { - def target = req.servletPath.replace("/dispatch", "") - if (req.queryString != null) { - target += "?" + req.queryString - } - def context = req.startAsync() - context.start { - context.dispatch(target) - } - } - } - - // TODO: Add tests for this! - @WebServlet(asyncSupported = true) - static class DispatchRecursive extends HttpServlet { - @Override - protected void service(HttpServletRequest req, HttpServletResponse resp) { - if (req.servletPath == "/recursive") { - resp.writer.print("Hello Recursive") - return - } - def depth = Integer.parseInt(req.getParameter("depth")) - if (depth > 0) { - req.startAsync().dispatch("/dispatch/recursive?depth=" + (depth - 1)) - } else { - req.startAsync().dispatch("/recursive") - } - } - } -} diff --git a/instrumentation/servlet/servlet-5.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/AbstractServlet5MappingTest.java b/instrumentation/servlet/servlet-5.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/AbstractServlet5MappingTest.java new file mode 100644 index 0000000000..9edb1ef362 --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/AbstractServlet5MappingTest.java @@ -0,0 +1,99 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; + +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerUsingTest; +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension; +import io.opentelemetry.sdk.testing.assertj.SpanDataAssert; +import io.opentelemetry.sdk.trace.data.StatusData; +import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpResponse; +import jakarta.servlet.Servlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public abstract class AbstractServlet5MappingTest + extends AbstractHttpServerUsingTest { + + @RegisterExtension + private static final InstrumentationExtension testing = + HttpServerInstrumentationExtension.forAgent(); + + @BeforeAll + void setup() { + startServer(); + } + + @AfterAll + void cleanup() { + cleanupServer(); + } + + public abstract void addServlet(CONTEXT context, String path, Class servlet) + throws Exception; + + protected void setupServlets(CONTEXT context) throws Exception { + addServlet(context, "/prefix/*", TestServlet.class); + addServlet(context, "*.suffix", TestServlet.class); + } + + @ParameterizedTest + @CsvSource({ + "prefix, /prefix/*, true", + "prefix/, /prefix/*, true", + "prefix/a, /prefix/*, true", + "prefixa, /*, false", + "a.suffix, /*.suffix, true", + ".suffix, /*.suffix, true", + "suffix, /*, false", + }) + void testPath(String path, String route, boolean success) { + + AggregatedHttpResponse response = + client.get(address.resolve(path).toString()).aggregate().join(); + + assertThat(response.status().code()).isEqualTo(success ? 200 : 404); + + testing.waitAndAssertTraces( + trace -> { + List> assertions = new ArrayList<>(); + assertions.add( + span -> + span.hasName("GET " + getContextPath() + route) + .hasKind(SpanKind.SERVER) + .hasStatus( + !success && response.status().code() >= 500 + ? StatusData.error() + : StatusData.unset())); + if (!success) { + assertions.add(span -> {}); + } + + trace.hasSpansSatisfyingExactly(assertions); + }); + } + + public static class TestServlet extends HttpServlet { + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) + throws IOException { + response.getWriter().write("Ok"); + } + } +} diff --git a/instrumentation/servlet/servlet-5.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/AbstractServlet5Test.java b/instrumentation/servlet/servlet-5.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/AbstractServlet5Test.java new file mode 100644 index 0000000000..35da3928ce --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/AbstractServlet5Test.java @@ -0,0 +1,227 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_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.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.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 io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; + +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.api.internal.HttpConstants; +import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest; +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions; +import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint; +import io.opentelemetry.javaagent.bootstrap.servlet.ExperimentalSnippetHolder; +import io.opentelemetry.sdk.testing.assertj.SpanDataAssert; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpRequest; +import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpResponse; +import jakarta.servlet.Servlet; +import org.junit.jupiter.api.Test; + +public abstract class AbstractServlet5Test extends AbstractHttpServerTest { + + public static final ServerEndpoint HTML_PRINT_WRITER = + new ServerEndpoint( + "HTML_PRINT_WRITER", + "htmlPrintWriter", + 200, + "\n" + + "\n" + + "\n" + + " \n" + + " Title\n" + + "\n" + + "\n" + + "

test works

\n" + + "\n" + + ""); + public static final ServerEndpoint HTML_SERVLET_OUTPUT_STREAM = + new ServerEndpoint( + "HTML_SERVLET_OUTPUT_STREAM", + "htmlServletOutputStream", + 200, + "\n" + + "\n" + + "\n" + + " \n" + + " Title\n" + + "\n" + + "\n" + + "

test works

\n" + + "\n" + + ""); + + @Override + protected void configure(HttpServerTestOptions options) { + super.configure(options); + options.setTestCaptureRequestParameters(true); + options.setHasResponseCustomizer(e -> true); + options.setHasResponseSpan(this::hasResponseSpan); + options.setExpectedException(new IllegalStateException(EXCEPTION.getBody())); + } + + protected boolean hasResponseSpan(ServerEndpoint endpoint) { + return endpoint.equals(REDIRECT) || (endpoint.equals(ERROR) && errorEndpointUsesSendError()); + } + + public abstract Class servlet(); + + public abstract void addServlet(CONTEXT context, String path, Class servlet) + throws Exception; + + protected void setupServlets(CONTEXT context) throws Exception { + Class servlet = servlet(); + + 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, INDEXED_CHILD.getPath(), servlet); + addServlet(context, CAPTURE_HEADERS.getPath(), servlet); + addServlet(context, CAPTURE_PARAMETERS.getPath(), servlet); + addServlet(context, HTML_PRINT_WRITER.getPath(), servlet); + addServlet(context, HTML_SERVLET_OUTPUT_STREAM.getPath(), servlet); + } + + @Override + public String expectedHttpRoute(ServerEndpoint endpoint, String method) { + // no need to compute route if we're not expecting it + if (!hasHttpRouteAttribute(endpoint)) { + return null; + } + + if (method.equals(HttpConstants._OTHER)) { + return getContextPath() + endpoint.getPath(); + } + + if (NOT_FOUND.equals(endpoint)) { + return getContextPath() + "/*"; + } else { + return super.expectedHttpRoute(endpoint, method); + } + } + + public boolean errorEndpointUsesSendError() { + return true; + } + + @Override + protected SpanDataAssert assertResponseSpan( + SpanDataAssert span, SpanData parentSpan, String method, ServerEndpoint endpoint) { + switch (endpoint.name()) { + case "REDIRECT": + SpanDataAssert spanDataAssert = + span.satisfies(s -> assertThat(s.getName()).matches(".*\\.sendRedirect")) + .hasKind(SpanKind.INTERNAL); + if (assertParentOnRedirect()) { + return spanDataAssert.hasParent(parentSpan); + } + return spanDataAssert; + case "ERROR": + return span.satisfies(s -> assertThat(s.getName()).matches(".*\\.sendError")) + .hasKind(SpanKind.INTERNAL) + .hasParent(parentSpan); + default: + break; + } + return span; + } + + protected boolean assertParentOnRedirect() { + return true; + } + + @Test + void snippetInjectionWithServletOutputStream() { + ExperimentalSnippetHolder.setSnippet( + "\n "); + AggregatedHttpRequest request = request(HTML_SERVLET_OUTPUT_STREAM, "GET"); + AggregatedHttpResponse response = client.execute(request).aggregate().join(); + + assertThat(response.status().code()).isEqualTo(HTML_SERVLET_OUTPUT_STREAM.getStatus()); + String result = + "\n" + + "\n" + + "\n" + + " \n" + + " \n" + + " Title\n" + + "\n" + + "\n" + + "

test works

\n" + + "\n" + + ""; + assertThat(response.contentUtf8()).isEqualTo(result); + assertThat(response.headers().contentLength()).isEqualTo(result.length()); + + ExperimentalSnippetHolder.setSnippet(""); + + String expectedRoute = expectedHttpRoute(HTML_SERVLET_OUTPUT_STREAM, "GET"); + testing() + .waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("GET" + (expectedRoute != null ? " " + expectedRoute : "")) + .hasKind(SpanKind.SERVER) + .hasNoParent(), + span -> + span.hasName("controller") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(0)))); + } + + @Test + void snippetInjectionWithPrintWriter() { + ExperimentalSnippetHolder.setSnippet("\n "); + AggregatedHttpRequest request = request(HTML_PRINT_WRITER, "GET"); + AggregatedHttpResponse response = client.execute(request).aggregate().join(); + + assertThat(response.status().code()).isEqualTo(HTML_PRINT_WRITER.getStatus()); + String result = + "\n" + + "\n" + + "\n" + + " \n" + + " \n" + + " Title\n" + + "\n" + + "\n" + + "

test works

\n" + + "\n" + + ""; + + assertThat(response.contentUtf8()).isEqualTo(result); + assertThat(response.headers().contentLength()).isEqualTo(result.length()); + + ExperimentalSnippetHolder.setSnippet(""); + + String expectedRoute = expectedHttpRoute(HTML_PRINT_WRITER, "GET"); + testing() + .waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("GET" + (expectedRoute != null ? " " + expectedRoute : "")) + .hasKind(SpanKind.SERVER) + .hasNoParent(), + span -> + span.hasName("controller") + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(0)))); + } +} diff --git a/instrumentation/servlet/servlet-5.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/TestServlet5.java b/instrumentation/servlet/servlet-5.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/TestServlet5.java new file mode 100644 index 0000000000..95345f3769 --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/testing/src/main/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/TestServlet5.java @@ -0,0 +1,298 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0; + +import static io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest.controller; +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.QUERY_PARAM; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS; +import static io.opentelemetry.javaagent.instrumentation.servlet.v5_0.AbstractServlet5Test.HTML_PRINT_WRITER; +import static io.opentelemetry.javaagent.instrumentation.servlet.v5_0.AbstractServlet5Test.HTML_SERVLET_OUTPUT_STREAM; + +import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint; +import jakarta.servlet.AsyncContext; +import jakarta.servlet.RequestDispatcher; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.CountDownLatch; + +public class TestServlet5 { + + private TestServlet5() {} + + @WebServlet + public static class Sync extends HttpServlet { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { + String servletPath = (String) req.getAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH); + if (servletPath == null) { + servletPath = req.getServletPath(); + } + + ServerEndpoint endpoint = ServerEndpoint.forPath(servletPath); + controller( + endpoint, + () -> { + resp.setContentType("text/plain"); + if (SUCCESS.equals(endpoint)) { + resp.setStatus(endpoint.getStatus()); + resp.getWriter().print(endpoint.getBody()); + } else if (INDEXED_CHILD.equals(endpoint)) { + endpoint.collectSpanAttributes(req::getParameter); + resp.setStatus(endpoint.getStatus()); + } else if (QUERY_PARAM.equals(endpoint)) { + resp.setStatus(endpoint.getStatus()); + resp.getWriter().print(req.getQueryString()); + } else if (REDIRECT.equals(endpoint)) { + resp.sendRedirect(endpoint.getBody()); + } else if (CAPTURE_HEADERS.equals(endpoint)) { + resp.setHeader("X-Test-Response", req.getHeader("X-Test-Request")); + resp.setStatus(endpoint.getStatus()); + resp.getWriter().print(endpoint.getBody()); + } else if (CAPTURE_PARAMETERS.equals(endpoint)) { + req.setCharacterEncoding("UTF8"); + String value = req.getParameter("test-parameter"); + if (!value.equals("test value õäöü")) { + throw new IllegalStateException( + "request parameter does not have expected value " + value); + } + + resp.setStatus(endpoint.getStatus()); + resp.getWriter().print(endpoint.getBody()); + } else if (ERROR.equals(endpoint)) { + resp.sendError(endpoint.getStatus(), endpoint.getBody()); + } else if (EXCEPTION.equals(endpoint)) { + throw new IllegalStateException(endpoint.getBody()); + } else if (HTML_PRINT_WRITER.equals(endpoint)) { + resp.setContentType("text/html"); + resp.setStatus(endpoint.getStatus()); + resp.setContentLength(endpoint.getBody().length()); + resp.getWriter().print(endpoint.getBody()); + } else if (HTML_SERVLET_OUTPUT_STREAM.equals(endpoint)) { + resp.setContentType("text/html"); + resp.setStatus(endpoint.getStatus()); + resp.setContentLength(endpoint.getBody().length()); + byte[] body = endpoint.getBody().getBytes(StandardCharsets.UTF_8); + resp.getOutputStream().write(body, 0, body.length); + } + return null; + }); + } + } + + @WebServlet(asyncSupported = true) + public static class Async 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.equals(EXCEPTION)) { + context.setTimeout(5000); + } + + context.start( + () -> { + try { + controller( + endpoint, + () -> { + resp.setContentType("text/plain"); + if (SUCCESS.equals(endpoint)) { + resp.setStatus(endpoint.getStatus()); + resp.getWriter().print(endpoint.getBody()); + context.complete(); + } else if (INDEXED_CHILD.equals(endpoint)) { + endpoint.collectSpanAttributes(req::getParameter); + resp.setStatus(endpoint.getStatus()); + context.complete(); + } else if (QUERY_PARAM.equals(endpoint)) { + resp.setStatus(endpoint.getStatus()); + resp.getWriter().print(req.getQueryString()); + context.complete(); + } else if (REDIRECT.equals(endpoint)) { + resp.sendRedirect(endpoint.getBody()); + context.complete(); + } else if (CAPTURE_HEADERS.equals(endpoint)) { + resp.setHeader("X-Test-Response", req.getHeader("X-Test-Request")); + resp.setStatus(endpoint.getStatus()); + resp.getWriter().print(endpoint.getBody()); + context.complete(); + } else if (CAPTURE_PARAMETERS.equals(endpoint)) { + req.setCharacterEncoding("UTF8"); + String value = req.getParameter("test-parameter"); + if (!value.equals("test value õäöü")) { + throw new IllegalStateException( + "request parameter does not have expected value " + value); + } + + resp.setStatus(endpoint.getStatus()); + resp.getWriter().print(endpoint.getBody()); + context.complete(); + } else if (ERROR.equals(endpoint)) { + resp.setStatus(endpoint.getStatus()); + resp.getWriter().print(endpoint.getBody()); + context.complete(); + } else if (EXCEPTION.equals(endpoint)) { + resp.setStatus(endpoint.getStatus()); + PrintWriter writer = resp.getWriter(); + writer.print(endpoint.getBody()); + if (req.getClass().getName().contains("catalina")) { + // on tomcat close the writer to ensure response is sent immediately, + // otherwise there is a chance that tomcat resets the connection before the + // response is sent + writer.close(); + } + throw new IllegalStateException(endpoint.getBody()); + } else if (HTML_PRINT_WRITER.equals(endpoint)) { + resp.setContentType("text/html"); + resp.setStatus(endpoint.getStatus()); + resp.setContentLength(endpoint.getBody().length()); + resp.getWriter().print(endpoint.getBody()); + context.complete(); + } else if (HTML_SERVLET_OUTPUT_STREAM.equals(endpoint)) { + resp.setContentType("text/html"); + resp.setStatus(endpoint.getStatus()); + resp.getOutputStream().print(endpoint.getBody()); + context.complete(); + } + return null; + }); + } catch (Exception exception) { + if (exception instanceof RuntimeException) { + throw (RuntimeException) exception; + } + throw new IllegalStateException(exception); + } finally { + latch.countDown(); + } + }); + try { + latch.await(); + } catch (InterruptedException exception) { + Thread.currentThread().interrupt(); + } + } + } + + @WebServlet(asyncSupported = true) + public static class FakeAsync extends HttpServlet { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { + AsyncContext context = req.startAsync(); + try { + ServerEndpoint endpoint = ServerEndpoint.forPath(req.getServletPath()); + + controller( + endpoint, + () -> { + resp.setContentType("text/plain"); + if (SUCCESS.equals(endpoint)) { + resp.setStatus(endpoint.getStatus()); + resp.getWriter().print(endpoint.getBody()); + } else if (INDEXED_CHILD.equals(endpoint)) { + endpoint.collectSpanAttributes(req::getParameter); + resp.setStatus(endpoint.getStatus()); + } else if (QUERY_PARAM.equals(endpoint)) { + resp.setStatus(endpoint.getStatus()); + resp.getWriter().print(req.getQueryString()); + } else if (REDIRECT.equals(endpoint)) { + resp.sendRedirect(endpoint.getBody()); + } else if (CAPTURE_HEADERS.equals(endpoint)) { + resp.setHeader("X-Test-Response", req.getHeader("X-Test-Request")); + resp.setStatus(endpoint.getStatus()); + resp.getWriter().print(endpoint.getBody()); + } else if (CAPTURE_PARAMETERS.equals(endpoint)) { + req.setCharacterEncoding("UTF8"); + String value = req.getParameter("test-parameter"); + if (!value.equals("test value õäöü")) { + throw new IllegalStateException( + "request parameter does not have expected value " + value); + } + + resp.setStatus(endpoint.getStatus()); + resp.getWriter().print(endpoint.getBody()); + } else if (ERROR.equals(endpoint)) { + resp.sendError(endpoint.getStatus(), endpoint.getBody()); + } else if (EXCEPTION.equals(endpoint)) { + resp.setStatus(endpoint.getStatus()); + resp.getWriter().print(endpoint.getBody()); + throw new IllegalStateException(endpoint.getBody()); + } else if (HTML_PRINT_WRITER.equals(endpoint)) { + // intentionally testing setting status before contentType here to cover that case + // somewhere + resp.setStatus(endpoint.getStatus()); + resp.setContentType("text/html"); + resp.getWriter().print(endpoint.getBody()); + } else if (HTML_SERVLET_OUTPUT_STREAM.equals(endpoint)) { + resp.setContentType("text/html"); + resp.setStatus(endpoint.getStatus()); + resp.getOutputStream().print(endpoint.getBody()); + } + return null; + }); + } finally { + context.complete(); + } + } + } + + @WebServlet(asyncSupported = true) + public static class DispatchImmediate extends HttpServlet { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) { + String target = req.getServletPath().replace("/dispatch", ""); + if (req.getQueryString() != null) { + target += "?" + req.getQueryString(); + } + + req.startAsync().dispatch(target); + } + } + + @WebServlet(asyncSupported = true) + public static class DispatchAsync extends HttpServlet { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) { + AsyncContext context = req.startAsync(); + context.start( + () -> { + String target = req.getServletPath().replace("/dispatch", ""); + if (req.getQueryString() != null) { + target += "?" + req.getQueryString(); + } + context.dispatch(target); + }); + } + } + + @WebServlet(asyncSupported = true) + public static class DispatchRecursive extends HttpServlet { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { + if (req.getServletPath().equals("/recursive")) { + resp.getWriter().print("Hello Recursive"); + } + + int depth = Integer.parseInt(req.getParameter("depth")); + if (depth > 0) { + req.startAsync().dispatch("/dispatch/recursive?depth=" + (depth - 1)); + } else { + req.startAsync().dispatch("/recursive"); + } + } + } +} diff --git a/instrumentation/servlet/servlet-5.0/tomcat-testing/build.gradle.kts b/instrumentation/servlet/servlet-5.0/tomcat-testing/build.gradle.kts new file mode 100644 index 0000000000..5825ace4d2 --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/tomcat-testing/build.gradle.kts @@ -0,0 +1,26 @@ +plugins { + id("otel.javaagent-testing") +} + +dependencies { + testInstrumentation(project(":instrumentation:jetty:jetty-11.0:javaagent")) + testImplementation(project(":instrumentation:servlet:servlet-5.0:testing")) + + testLibrary("org.apache.tomcat.embed:tomcat-embed-core:10.0.0") + testLibrary("org.apache.tomcat.embed:tomcat-embed-jasper:10.0.0") +} + +val testLatestDeps = findProperty("testLatestDeps") as Boolean + +if (testLatestDeps) { + otelJava { + // Tomcat 10.1 requires Java 11 + minJavaVersionSupported.set(JavaVersion.VERSION_11) + } +} + +tasks { + withType().configureEach { + jvmArgs("-Dotel.instrumentation.servlet.experimental.capture-request-parameters=test-parameter") + } +} diff --git a/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/ErrorHandlerValve.java b/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/ErrorHandlerValve.java new file mode 100644 index 0000000000..43a69d8b1c --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/ErrorHandlerValve.java @@ -0,0 +1,32 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.tomcat; + +import java.io.IOException; +import org.apache.catalina.connector.Request; +import org.apache.catalina.connector.Response; +import org.apache.catalina.valves.ErrorReportValve; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +// public, because it's loaded by reflection +public class ErrorHandlerValve extends ErrorReportValve { + + private static final Logger logger = LoggerFactory.getLogger(ErrorHandlerValve.class); + + @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 e) { + logger.error("Failed to write error response", e); + } + } +} diff --git a/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/TestAccessLogValve.java b/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/TestAccessLogValve.java new file mode 100644 index 0000000000..dc57f69b64 --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/TestAccessLogValve.java @@ -0,0 +1,82 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.tomcat; + +import jakarta.servlet.ServletException; +import java.io.IOException; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import org.apache.catalina.AccessLog; +import org.apache.catalina.connector.Request; +import org.apache.catalina.connector.Response; +import org.apache.catalina.valves.ValveBase; + +// public, because it's loaded by reflection +public class TestAccessLogValve extends ValveBase implements AccessLog { + + public final List> getLoggedIds() { + return loggedIds; + } + + private final List> loggedIds = new ArrayList<>(); + + public TestAccessLogValve() { + super(true); + } + + @Override + public void log(Request request, Response response, long time) { + if (request.getParameter("access-log") == null) { + return; + } + + synchronized (loggedIds) { + loggedIds.add( + new AbstractMap.SimpleEntry<>( + request.getAttribute("trace_id").toString(), + request.getAttribute("span_id").toString())); + loggedIds.notifyAll(); + } + } + + public void waitForLoggedIds(int expected) { + long timeout = TimeUnit.SECONDS.toMillis(20); + long startTime = System.currentTimeMillis(); + long endTime = startTime + timeout; + long toWait = timeout; + synchronized (loggedIds) { + while (loggedIds.size() < expected && toWait > 0) { + try { + loggedIds.wait(toWait); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + toWait = endTime - System.currentTimeMillis(); + } + + if (toWait <= 0) { + throw new RuntimeException( + "Timeout waiting for " + expected + " access log ids, got " + loggedIds.size()); + } + } + } + + @Override + public void setRequestAttributesEnabled(boolean requestAttributesEnabled) {} + + @Override + public boolean getRequestAttributesEnabled() { + return false; + } + + @Override + public void invoke(Request request, Response response) throws IOException, ServletException { + getNext().invoke(request, response); + } +} diff --git a/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/TomcatServlet5AsyncTest.java b/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/TomcatServlet5AsyncTest.java new file mode 100644 index 0000000000..21896caef8 --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/TomcatServlet5AsyncTest.java @@ -0,0 +1,22 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.tomcat; + +import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.TestServlet5; +import jakarta.servlet.Servlet; + +class TomcatServlet5AsyncTest extends TomcatServlet5Test { + + @Override + public Class servlet() { + return TestServlet5.Async.class; + } + + @Override + public boolean errorEndpointUsesSendError() { + return false; + } +} diff --git a/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/TomcatServlet5FakeAsyncTest.java b/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/TomcatServlet5FakeAsyncTest.java new file mode 100644 index 0000000000..b6d0773e95 --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/TomcatServlet5FakeAsyncTest.java @@ -0,0 +1,17 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.tomcat; + +import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.TestServlet5; +import jakarta.servlet.Servlet; + +class TomcatServlet5FakeAsyncTest extends TomcatServlet5Test { + + @Override + public Class servlet() { + return TestServlet5.FakeAsync.class; + } +} diff --git a/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/TomcatServlet5SyncTest.java b/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/TomcatServlet5SyncTest.java new file mode 100644 index 0000000000..8af07a7220 --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/TomcatServlet5SyncTest.java @@ -0,0 +1,17 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.tomcat; + +import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.TestServlet5; +import jakarta.servlet.Servlet; + +class TomcatServlet5SyncTest extends TomcatServlet5Test { + + @Override + public Class servlet() { + return TestServlet5.Sync.class; + } +} diff --git a/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/TomcatServlet5Test.java b/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/TomcatServlet5Test.java new file mode 100644 index 0000000000..82a4dff407 --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/TomcatServlet5Test.java @@ -0,0 +1,225 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.tomcat; + +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.NOT_FOUND; +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS; +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +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.javaagent.instrumentation.servlet.v5_0.AbstractServlet5Test; +import io.opentelemetry.sdk.testing.assertj.SpanDataAssert; +import io.opentelemetry.sdk.testing.assertj.TraceAssert; +import io.opentelemetry.sdk.trace.data.SpanData; +import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpRequest; +import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpResponse; +import jakarta.servlet.Servlet; +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.IntStream; +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.Assumptions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; + +public abstract class TomcatServlet5Test extends AbstractServlet5Test { + + @RegisterExtension + protected static final InstrumentationExtension testing = + HttpServerInstrumentationExtension.forAgent(); + + private static final ServerEndpoint ACCESS_LOG_SUCCESS = + new ServerEndpoint( + "ACCESS_LOG_SUCCESS", + "success?access-log=true", + SUCCESS.getStatus(), + SUCCESS.getBody(), + false); + private static final ServerEndpoint ACCESS_LOG_ERROR = + new ServerEndpoint( + "ACCESS_LOG_ERROR", + "error-status?access-log=true", + ERROR.getStatus(), + ERROR.getBody(), + false); + private final TestAccessLogValve accessLogValue = new TestAccessLogValve(); + + @TempDir private static File tempDir; + + @Override + protected void configure(HttpServerTestOptions options) { + super.configure(options); + options.setExpectedException(new IllegalStateException(EXCEPTION.getBody())); + options.setContextPath("/tomcat-context"); + options.setTestError(testError()); + } + + public boolean testError() { + return false; + } + + @Override + protected SpanDataAssert assertResponseSpan( + SpanDataAssert span, SpanData parentSpan, String method, ServerEndpoint endpoint) { + if (NOT_FOUND.equals(endpoint)) { + span.satisfies(s -> assertThat(s.getName()).matches(".*\\.sendError")) + .hasKind(SpanKind.INTERNAL) + .hasParent(parentSpan); + } + return super.assertResponseSpan(span, parentSpan, method, endpoint); + } + + @Override + protected boolean hasResponseSpan(ServerEndpoint endpoint) { + return endpoint == NOT_FOUND || super.hasResponseSpan(endpoint); + } + + @Override + protected Tomcat setupServer() throws Exception { + Tomcat tomcatServer = new Tomcat(); + + File baseDir = tempDir; + 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"); + applicationDir.mkdirs(); + + Context servletContext = + tomcatServer.addWebapp(getContextPath(), applicationDir.getAbsolutePath()); + // Speed up startup by disabling jar scanning: + servletContext.getJarScanner().setJarScanFilter((jarScanType, jarName) -> false); + + setupServlets(servletContext); + + ((StandardHost) tomcatServer.getHost()) + .setErrorReportValveClass(ErrorHandlerValve.class.getName()); + tomcatServer.getHost().getPipeline().addValve(accessLogValue); + + tomcatServer.start(); + + return tomcatServer; + } + + @BeforeEach + void setUp() { + accessLogValue.getLoggedIds().clear(); + testing().clearAllExportedData(); + } + + @Override + public void stopServer(Tomcat server) throws LifecycleException { + server.stop(); + server.destroy(); + } + + @Override + public void addServlet(Context servletContext, String path, Class servlet) + throws Exception { + String name = UUID.randomUUID().toString(); + Tomcat.addServlet(servletContext, name, servlet.getConstructor().newInstance()); + servletContext.addServletMappingDecoded(path, name); + } + + @ParameterizedTest + @CsvSource({"1", "4"}) + void accessLogHasIdsForCountRequests(int count) { + AggregatedHttpRequest request = request(ACCESS_LOG_SUCCESS, "GET"); + + IntStream.range(0, count) + .mapToObj(i -> client.execute(request).aggregate().join()) + .forEach( + response -> { + assertThat(response.status().code()).isEqualTo(ACCESS_LOG_SUCCESS.getStatus()); + assertThat(response.contentUtf8()).isEqualTo(ACCESS_LOG_SUCCESS.getBody()); + }); + + accessLogValue.waitForLoggedIds(count); + assertThat(accessLogValue.getLoggedIds().size()).isEqualTo(count); + List loggedTraces = + accessLogValue.getLoggedIds().stream().map(Map.Entry::getKey).collect(Collectors.toList()); + List loggedSpans = + accessLogValue.getLoggedIds().stream() + .map(Map.Entry::getValue) + .collect(Collectors.toList()); + + testing() + .waitAndAssertTraces( + IntStream.range(0, count) + .mapToObj( + i -> + (Consumer) + trace -> { + trace.hasSpansSatisfyingExactly( + span -> + assertServerSpan( + span, "GET", ACCESS_LOG_SUCCESS, SUCCESS.getStatus()), + span -> assertControllerSpan(span, null)); + SpanData span = trace.getSpan(0); + assertThat(loggedTraces).contains(span.getTraceId()); + assertThat(loggedSpans).contains(span.getSpanId()); + }) + .collect(Collectors.toList())); + } + + @Test + void accessLogHasIdsForErrorRequest() { + Assumptions.assumeTrue(testError()); + + AggregatedHttpRequest request = request(ACCESS_LOG_ERROR, "GET"); + AggregatedHttpResponse response = client.execute(request).aggregate().join(); + + assertThat(response.status().code()).isEqualTo(ACCESS_LOG_ERROR.getStatus()); + assertThat(response.contentUtf8()).isEqualTo(ACCESS_LOG_ERROR.getBody()); + + List> spanDataAsserts = new ArrayList<>(); + spanDataAsserts.add( + (span, trace) -> assertServerSpan(span, "GET", ACCESS_LOG_ERROR, ERROR.getStatus())); + spanDataAsserts.add((span, trace) -> assertControllerSpan(span, null)); + if (errorEndpointUsesSendError()) { + spanDataAsserts.add( + (span, trace) -> + span.satisfies(s -> assertThat(s.getName()).matches(".*\\.sendError")) + .hasKind(SpanKind.INTERNAL) + .hasParent(trace.getSpan(1))); + } + + accessLogValue.waitForLoggedIds(1); + testing() + .waitAndAssertTraces( + trace -> { + trace.hasSpansSatisfyingExactly( + spanDataAsserts.stream() + .map(e -> (Consumer) span -> e.accept(span, trace)) + .collect(Collectors.toList())); + SpanData span = trace.getSpan(0); + Map.Entry entry = accessLogValue.getLoggedIds().get(0); + assertThat(entry.getKey()).isEqualTo(span.getTraceId()); + assertThat(entry.getValue()).isEqualTo(span.getSpanId()); + }); + } +} diff --git a/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/dispatch/TomcatDispatchTest.java b/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/dispatch/TomcatDispatchTest.java new file mode 100644 index 0000000000..ca380be201 --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/dispatch/TomcatDispatchTest.java @@ -0,0 +1,18 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.tomcat.dispatch; + +import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions; +import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.tomcat.TomcatServlet5Test; + +abstract class TomcatDispatchTest extends TomcatServlet5Test { + + @Override + protected void configure(HttpServerTestOptions options) { + super.configure(options); + options.setContextPath(getContextPath() + "/dispatch"); + } +} diff --git a/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/dispatch/TomcatServlet5DispatchAsyncTest.java b/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/dispatch/TomcatServlet5DispatchAsyncTest.java new file mode 100644 index 0000000000..22df1934db --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/dispatch/TomcatServlet5DispatchAsyncTest.java @@ -0,0 +1,68 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.tomcat.dispatch; + +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.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.testing.junit.http.HttpServerTestOptions; +import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.TestServlet5; +import jakarta.servlet.Servlet; +import org.apache.catalina.Context; + +class TomcatServlet5DispatchAsyncTest extends TomcatDispatchTest { + + @Override + protected void configure(HttpServerTestOptions options) { + super.configure(options); + options.setVerifyServerSpanEndTime(false); + } + + @Override + public Class servlet() { + return TestServlet5.Async.class; + } + + @Override + protected void setupServlets(Context context) throws Exception { + super.setupServlets(context); + + addServlet(context, "/dispatch" + SUCCESS.getPath(), TestServlet5.DispatchAsync.class); + addServlet(context, "/dispatch" + QUERY_PARAM.getPath(), TestServlet5.DispatchAsync.class); + addServlet(context, "/dispatch" + ERROR.getPath(), TestServlet5.DispatchAsync.class); + addServlet(context, "/dispatch" + EXCEPTION.getPath(), TestServlet5.DispatchAsync.class); + addServlet(context, "/dispatch" + REDIRECT.getPath(), TestServlet5.DispatchAsync.class); + addServlet(context, "/dispatch" + AUTH_REQUIRED.getPath(), TestServlet5.DispatchAsync.class); + addServlet(context, "/dispatch" + CAPTURE_HEADERS.getPath(), TestServlet5.DispatchAsync.class); + addServlet( + context, "/dispatch" + CAPTURE_PARAMETERS.getPath(), TestServlet5.DispatchAsync.class); + addServlet(context, "/dispatch" + INDEXED_CHILD.getPath(), TestServlet5.DispatchAsync.class); + addServlet( + context, "/dispatch" + HTML_PRINT_WRITER.getPath(), TestServlet5.DispatchAsync.class); + addServlet( + context, + "/dispatch" + HTML_SERVLET_OUTPUT_STREAM.getPath(), + TestServlet5.DispatchAsync.class); + addServlet(context, "/dispatch/recursive", TestServlet5.DispatchRecursive.class); + } + + @Override + public boolean errorEndpointUsesSendError() { + return false; + } + + @Override + protected boolean assertParentOnRedirect() { + return false; + } +} diff --git a/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/dispatch/TomcatServlet5DispatchImmediateTest.java b/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/dispatch/TomcatServlet5DispatchImmediateTest.java new file mode 100644 index 0000000000..d2683c2032 --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/dispatch/TomcatServlet5DispatchImmediateTest.java @@ -0,0 +1,61 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.tomcat.dispatch; + +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.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.testing.junit.http.HttpServerTestOptions; +import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.TestServlet5; +import jakarta.servlet.Servlet; +import org.apache.catalina.Context; + +class TomcatServlet5DispatchImmediateTest extends TomcatDispatchTest { + + @Override + public Class servlet() { + return TestServlet5.Sync.class; + } + + @Override + protected void configure(HttpServerTestOptions options) { + super.configure(options); + options.setTestNotFound(false); + } + + @Override + protected void setupServlets(Context context) throws Exception { + super.setupServlets(context); + + addServlet(context, "/dispatch" + SUCCESS.getPath(), TestServlet5.DispatchImmediate.class); + addServlet(context, "/dispatch" + QUERY_PARAM.getPath(), TestServlet5.DispatchImmediate.class); + addServlet(context, "/dispatch" + ERROR.getPath(), TestServlet5.DispatchImmediate.class); + addServlet(context, "/dispatch" + EXCEPTION.getPath(), TestServlet5.DispatchImmediate.class); + addServlet(context, "/dispatch" + REDIRECT.getPath(), TestServlet5.DispatchImmediate.class); + addServlet( + context, "/dispatch" + AUTH_REQUIRED.getPath(), TestServlet5.DispatchImmediate.class); + addServlet( + context, "/dispatch" + CAPTURE_HEADERS.getPath(), TestServlet5.DispatchImmediate.class); + addServlet( + context, "/dispatch" + CAPTURE_PARAMETERS.getPath(), TestServlet5.DispatchImmediate.class); + addServlet( + context, "/dispatch" + INDEXED_CHILD.getPath(), TestServlet5.DispatchImmediate.class); + addServlet( + context, "/dispatch" + HTML_PRINT_WRITER.getPath(), TestServlet5.DispatchImmediate.class); + addServlet( + context, + "/dispatch" + HTML_SERVLET_OUTPUT_STREAM.getPath(), + TestServlet5.DispatchImmediate.class); + addServlet(context, "/dispatch/recursive", TestServlet5.DispatchRecursive.class); + } +} diff --git a/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/dispatch/TomcatServlet5ForwardTest.java b/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/dispatch/TomcatServlet5ForwardTest.java new file mode 100644 index 0000000000..70b32f8e61 --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/dispatch/TomcatServlet5ForwardTest.java @@ -0,0 +1,64 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.tomcat.dispatch; + +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.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.testing.junit.http.HttpServerTestOptions; +import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.RequestDispatcherServlet; +import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.TestServlet5; +import jakarta.servlet.Servlet; +import org.apache.catalina.Context; + +class TomcatServlet5ForwardTest extends TomcatDispatchTest { + + @Override + public Class servlet() { + return TestServlet5.Sync.class; // dispatch to sync servlet + } + + @Override + protected void configure(HttpServerTestOptions options) { + super.configure(options); + options.setTestNotFound(false); + } + + @Override + protected void setupServlets(Context context) throws Exception { + super.setupServlets(context); + + addServlet(context, "/dispatch" + SUCCESS.getPath(), RequestDispatcherServlet.Forward.class); + addServlet( + context, "/dispatch" + QUERY_PARAM.getPath(), RequestDispatcherServlet.Forward.class); + addServlet(context, "/dispatch" + REDIRECT.getPath(), RequestDispatcherServlet.Forward.class); + addServlet(context, "/dispatch" + ERROR.getPath(), RequestDispatcherServlet.Forward.class); + addServlet(context, "/dispatch" + EXCEPTION.getPath(), RequestDispatcherServlet.Forward.class); + addServlet( + context, "/dispatch" + AUTH_REQUIRED.getPath(), RequestDispatcherServlet.Forward.class); + addServlet( + context, "/dispatch" + CAPTURE_HEADERS.getPath(), RequestDispatcherServlet.Forward.class); + addServlet( + context, + "/dispatch" + CAPTURE_PARAMETERS.getPath(), + RequestDispatcherServlet.Forward.class); + addServlet( + context, "/dispatch" + INDEXED_CHILD.getPath(), RequestDispatcherServlet.Forward.class); + addServlet( + context, "/dispatch" + HTML_PRINT_WRITER.getPath(), RequestDispatcherServlet.Forward.class); + addServlet( + context, + "/dispatch" + HTML_SERVLET_OUTPUT_STREAM.getPath(), + RequestDispatcherServlet.Forward.class); + } +} diff --git a/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/dispatch/TomcatServlet5IncludeTest.java b/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/dispatch/TomcatServlet5IncludeTest.java new file mode 100644 index 0000000000..873b190117 --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/dispatch/TomcatServlet5IncludeTest.java @@ -0,0 +1,64 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.tomcat.dispatch; + +import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.AUTH_REQUIRED; +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.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.testing.junit.http.HttpServerTestOptions; +import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.RequestDispatcherServlet; +import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.TestServlet5; +import jakarta.servlet.Servlet; +import org.apache.catalina.Context; + +class TomcatServlet5IncludeTest extends TomcatDispatchTest { + + @Override + public Class servlet() { + return TestServlet5.Sync.class; // dispatch to sync servlet + } + + @Override + protected void configure(HttpServerTestOptions options) { + super.configure(options); + options.setTestNotFound(false); + options.setTestRedirect(false); + options.setTestCaptureHttpHeaders(false); + options.setTestError(false); + } + + @Override + protected void setupServlets(Context context) throws Exception { + super.setupServlets(context); + + addServlet(context, "/dispatch" + SUCCESS.getPath(), RequestDispatcherServlet.Include.class); + addServlet( + context, "/dispatch" + QUERY_PARAM.getPath(), RequestDispatcherServlet.Include.class); + addServlet(context, "/dispatch" + REDIRECT.getPath(), RequestDispatcherServlet.Include.class); + addServlet(context, "/dispatch" + ERROR.getPath(), RequestDispatcherServlet.Include.class); + addServlet(context, "/dispatch" + EXCEPTION.getPath(), RequestDispatcherServlet.Include.class); + addServlet( + context, "/dispatch" + AUTH_REQUIRED.getPath(), RequestDispatcherServlet.Include.class); + addServlet( + context, + "/dispatch" + CAPTURE_PARAMETERS.getPath(), + RequestDispatcherServlet.Include.class); + addServlet( + context, "/dispatch" + INDEXED_CHILD.getPath(), RequestDispatcherServlet.Include.class); + addServlet( + context, "/dispatch" + HTML_PRINT_WRITER.getPath(), RequestDispatcherServlet.Include.class); + addServlet( + context, + "/dispatch" + HTML_SERVLET_OUTPUT_STREAM.getPath(), + RequestDispatcherServlet.Include.class); + } +} diff --git a/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/mapping/TomcatServlet5FilterMappingTest.java b/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/mapping/TomcatServlet5FilterMappingTest.java new file mode 100644 index 0000000000..c90e66b89c --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/mapping/TomcatServlet5FilterMappingTest.java @@ -0,0 +1,114 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.tomcat.mapping; + +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.FilterConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.UUID; +import org.apache.catalina.Context; +import org.apache.tomcat.util.descriptor.web.FilterDef; +import org.apache.tomcat.util.descriptor.web.FilterMap; + +abstract class TomcatServlet5FilterMappingTest extends TomcatServlet5MappingTest { + public void addFilter(Context servletContext, String path, Class filter) + throws Exception { + String name = UUID.randomUUID().toString(); + FilterDef filterDef = new FilterDef(); + filterDef.setFilter(filter.getConstructor().newInstance()); + filterDef.setFilterName(name); + servletContext.addFilterDef(filterDef); + FilterMap filterMap = new FilterMap(); + filterMap.setFilterName(name); + filterMap.addURLPattern(path); + servletContext.addFilterMap(filterMap); + } + + public void addFilterWithServletName( + Context servletContext, String servletName, Class filter) throws Exception { + String name = UUID.randomUUID().toString(); + FilterDef filterDef = new FilterDef(); + filterDef.setFilter(filter.getConstructor().newInstance()); + filterDef.setFilterName(name); + servletContext.addFilterDef(filterDef); + FilterMap filterMap = new FilterMap(); + filterMap.setFilterName(name); + filterMap.addServletName(servletName); + servletContext.addFilterMap(filterMap); + } + + public static class TestFilter implements Filter { + @Override + public void init(FilterConfig filterConfig) {} + + @Override + public void doFilter( + ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) + throws IOException, ServletException { + if (servletRequest.getAttribute("firstFilterCalled") != null) { + servletRequest.setAttribute("testFilterCalled", Boolean.TRUE); + filterChain.doFilter(servletRequest, servletResponse); + } else { + throw new IllegalStateException("First filter should have been called."); + } + } + + @Override + public void destroy() {} + } + + public static class FirstFilter implements Filter { + @Override + public void init(FilterConfig filterConfig) {} + + @Override + public void doFilter( + ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) + throws IOException, ServletException { + servletRequest.setAttribute("firstFilterCalled", Boolean.TRUE); + filterChain.doFilter(servletRequest, servletResponse); + } + + @Override + public void destroy() {} + } + + public static class LastFilter implements Filter { + @Override + public void init(FilterConfig filterConfig) {} + + @Override + public void doFilter( + ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) + throws IOException, ServletException { + if (servletRequest.getAttribute("testFilterCalled") != null) { + HttpServletResponse response = (HttpServletResponse) servletResponse; + response.getWriter().write("Ok"); + response.setStatus(HttpServletResponse.SC_OK); + } else { + filterChain.doFilter(servletRequest, servletResponse); + } + } + + @Override + public void destroy() {} + } + + public static class DefaultServlet extends HttpServlet { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) { + throw new IllegalStateException( + "Servlet should not have been called, filter should have handled the request."); + } + } +} diff --git a/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/mapping/TomcatServlet5FilterServletNameMappingTest.java b/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/mapping/TomcatServlet5FilterServletNameMappingTest.java new file mode 100644 index 0000000000..ac4ed0fe28 --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/mapping/TomcatServlet5FilterServletNameMappingTest.java @@ -0,0 +1,25 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.tomcat.mapping; + +import org.apache.catalina.Context; +import org.apache.catalina.startup.Tomcat; + +class TomcatServlet5FilterServletNameMappingTest extends TomcatServlet5FilterMappingTest { + @Override + protected void setupServlets(Context context) throws Exception { + Tomcat.addServlet(context, "prefix-servlet", new DefaultServlet()); + context.addServletMappingDecoded("/prefix/*", "prefix-servlet"); + Tomcat.addServlet(context, "suffix-servlet", new DefaultServlet()); + context.addServletMappingDecoded("*.suffix", "suffix-servlet"); + + addFilter(context, "/*", FirstFilter.class); + addFilterWithServletName(context, "prefix-servlet", TestFilter.class); + addFilterWithServletName(context, "suffix-servlet", TestFilter.class); + addFilterWithServletName(context, "prefix-servlet", LastFilter.class); + addFilterWithServletName(context, "suffix-servlet", LastFilter.class); + } +} diff --git a/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/mapping/TomcatServlet5FilterUrlPatternMappingTest.java b/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/mapping/TomcatServlet5FilterUrlPatternMappingTest.java new file mode 100644 index 0000000000..7f69167aaa --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/mapping/TomcatServlet5FilterUrlPatternMappingTest.java @@ -0,0 +1,18 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.tomcat.mapping; + +import org.apache.catalina.Context; + +class TomcatServlet5FilterUrlPatternMappingTest extends TomcatServlet5FilterMappingTest { + @Override + protected void setupServlets(Context context) throws Exception { + addFilter(context, "/*", FirstFilter.class); + addFilter(context, "/prefix/*", TestFilter.class); + addFilter(context, "*.suffix", TestFilter.class); + addFilter(context, "/*", LastFilter.class); + } +} diff --git a/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/mapping/TomcatServlet5MappingTest.java b/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/mapping/TomcatServlet5MappingTest.java new file mode 100644 index 0000000000..326cf8120e --- /dev/null +++ b/instrumentation/servlet/servlet-5.0/tomcat-testing/src/test/java/io/opentelemetry/javaagent/instrumentation/servlet/v5_0/tomcat/mapping/TomcatServlet5MappingTest.java @@ -0,0 +1,62 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.servlet.v5_0.tomcat.mapping; + +import io.opentelemetry.javaagent.instrumentation.servlet.v5_0.AbstractServlet5MappingTest; +import jakarta.servlet.Servlet; +import java.io.File; +import java.util.UUID; +import org.apache.catalina.Context; +import org.apache.catalina.LifecycleException; +import org.apache.catalina.startup.Tomcat; +import org.junit.jupiter.api.io.TempDir; + +class TomcatServlet5MappingTest extends AbstractServlet5MappingTest { + @TempDir private static File tempDir; + + @Override + protected Tomcat setupServer() throws Exception { + Tomcat tomcatServer = new Tomcat(); + + File baseDir = tempDir; + 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"); + applicationDir.mkdirs(); + + 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; + } + + @Override + public void stopServer(Tomcat server) throws LifecycleException { + server.stop(); + server.destroy(); + } + + @Override + public void addServlet(Context context, String path, Class servlet) + throws Exception { + String name = UUID.randomUUID().toString(); + Tomcat.addServlet(context, name, servlet.getConstructor().newInstance()); + context.addServletMappingDecoded(path, name); + } + + @Override + public String getContextPath() { + return "/tomcat-context"; + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 82580654f2..943db5bee9 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -554,8 +554,10 @@ include(":instrumentation:servlet:servlet-3.0:javaagent-unit-tests") include(":instrumentation:servlet:servlet-3.0:testing") include(":instrumentation:servlet:servlet-5.0:javaagent") include(":instrumentation:servlet:servlet-5.0:javaagent-unit-tests") +include(":instrumentation:servlet:servlet-5.0:jetty11-testing") include(":instrumentation:servlet:servlet-5.0:jetty12-testing") include(":instrumentation:servlet:servlet-5.0:testing") +include(":instrumentation:servlet:servlet-5.0:tomcat-testing") include(":instrumentation:servlet:servlet-common:bootstrap") include(":instrumentation:servlet:servlet-common:javaagent") include(":instrumentation:servlet:servlet-javax-common:javaagent")