conver servlet 5 tests from groovy to java (#12364)

This commit is contained in:
Gregor Zeitlinger 2024-10-19 03:44:03 +02:00 committed by GitHub
parent f422eece13
commit c165ca4dbd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
56 changed files with 2470 additions and 1955 deletions

View File

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

View File

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

View File

@ -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<Map.Entry<String, String>> getLoggedIds() {
return loggedIds;

View File

@ -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<Test>().configureEach {
jvmArgs("-Dotel.instrumentation.servlet.experimental.capture-request-parameters=test-parameter")
}
}

View File

@ -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<SERVER, CONTEXT> extends AgentInstrumentationSpecification implements HttpServerTestTrait<SERVER> {
def setupSpec() {
setupServer()
}
def cleanupSpec() {
cleanupServer()
}
abstract void addServlet(CONTEXT context, String path, Class<Servlet> 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
}
}

View File

@ -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<Object, Object> {
@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> 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"
}
}

View File

@ -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<Object, Object> {
@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> 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> servlet() {
TestServlet5.Sync
}
}
@IgnoreIf({ !jvm.java11Compatible })
class JettyServlet5TestAsync extends JettyServlet5Test {
@Override
Class<Servlet> servlet() {
TestServlet5.Async
}
@Override
boolean errorEndpointUsesSendError() {
false
}
}
@IgnoreIf({ !jvm.java11Compatible })
class JettyServlet5TestFakeAsync extends JettyServlet5Test {
@Override
Class<Servlet> servlet() {
TestServlet5.FakeAsync
}
}
@IgnoreIf({ !jvm.java11Compatible })
class JettyServlet5TestForward extends JettyDispatchTest {
@Override
Class<Servlet> 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> 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> 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> 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/")
}
}

View File

@ -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<Object, Object> {
@Override
Set<AttributeKey<?>> 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> 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> servlet() {
TestServlet5.Sync
}
@Override
boolean testNotFound() {
false
}
@Override
Throwable expectedException() {
new ServletException(EXCEPTION.body)
}
}

View File

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

View File

@ -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<Tomcat, Context> {
@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> servlet) {
String name = UUID.randomUUID()
Tomcat.addServlet(servletContext, name, servlet.newInstance())
servletContext.addServletMappingDecoded(path, name)
}
@Override
String getContextPath() {
return "/tomcat-context"
}
}

View File

@ -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<Tomcat, Context> {
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> 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<AggregatedHttpResponse> 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<Tuple2<String, String>> 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> servlet() {
TestServlet5.Sync
}
}
class TomcatServlet5TestAsync extends TomcatServlet5Test {
@Override
Class<Servlet> servlet() {
TestServlet5.Async
}
@Override
boolean errorEndpointUsesSendError() {
false
}
}
class TomcatServlet5TestFakeAsync extends TomcatServlet5Test {
@Override
Class<Servlet> servlet() {
TestServlet5.FakeAsync
}
}
class TomcatServlet5TestForward extends TomcatDispatchTest {
@Override
Class<Servlet> 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> 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> 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> 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/")
}
}

View File

@ -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<Test>().configureEach {
jvmArgs("-Dotel.instrumentation.servlet.experimental.capture-request-parameters=test-parameter")
}
}

View File

@ -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<? extends Servlet> servlet() {
return TestServlet5.Async.class;
}
@Override
public boolean errorEndpointUsesSendError() {
return false;
}
}

View File

@ -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<? extends Servlet> servlet() {
return TestServlet5.FakeAsync.class;
}
}

View File

@ -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<Server, ServletContextHandler> {
@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<? extends Servlet> 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);
}
}
}

View File

@ -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<? extends Servlet> servlet() {
return TestServlet5.Sync.class;
}
}

View File

@ -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<Server, ServletContextHandler> {
@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<? extends Servlet> servlet)
throws Exception {
servletContext.addServlet(servlet, path);
}
}

View File

@ -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<Server, ServletHandler> {
@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<AttributeKey<?>> 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<? extends Servlet> servlet)
throws Exception {
servletHandler.addServletWithMapping(servlet, path);
}
@Override
public void stopServer(Server server) throws Exception {
server.stop();
server.destroy();
}
@Override
public Class<? extends Servlet> servlet() {
return TestServlet5.Sync.class;
}
}

View File

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

View File

@ -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<? extends Servlet> 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;
}
}

View File

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

View File

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

View File

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

View File

@ -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<Object, Object> {
@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> servlet) {
ServletContextHandler handler = (ServletContextHandler) handlerObject
handler.addServlet(servlet, path)
}
}
class JettyServlet5TestSync extends Jetty12Servlet5Test {
@Override
Class<Servlet> servlet() {
TestServlet5.Sync
}
}
class JettyServlet5TestAsync extends Jetty12Servlet5Test {
@Override
Class<Servlet> servlet() {
TestServlet5.Async
}
@Override
boolean errorEndpointUsesSendError() {
false
}
}
class JettyServlet5TestFakeAsync extends Jetty12Servlet5Test {
@Override
Class<Servlet> servlet() {
TestServlet5.FakeAsync
}
}
class JettyServlet5TestForward extends JettyDispatchTest {
@Override
Class<Servlet> 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> 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> 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> 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/")
}
}

View File

@ -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<? extends Servlet> servlet() {
return TestServlet5.Async.class;
}
@Override
public boolean errorEndpointUsesSendError() {
return false;
}
}

View File

@ -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<? extends Servlet> servlet() {
return TestServlet5.FakeAsync.class;
}
}

View File

@ -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<? extends Servlet> servlet() {
return TestServlet5.Sync.class;
}
}

View File

@ -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<Server, ServletContextHandler> {
@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<? extends Servlet> servlet)
throws Exception {
servletContext.addServlet(servlet, path);
}
}

View File

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

View File

@ -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<? extends Servlet> 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;
}
}

View File

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

View File

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

View File

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

View File

@ -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<SERVER, CONTEXT> extends HttpServerTest<SERVER> 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> servlet()
abstract void addServlet(CONTEXT context, String path, Class<Servlet> servlet)
public static final ServerEndpoint HTML_PRINT_WRITER =
new ServerEndpoint("HTML_PRINT_WRITER", "htmlPrintWriter",
200,
"<!DOCTYPE html>\n"
+ "<html lang=\"en\">\n"
+ "<head>\n"
+ " <meta charset=\"UTF-8\">\n"
+ " <title>Title</title>\n"
+ "</head>\n"
+ "<body>\n"
+ "<p>test works</p>\n"
+ "</body>\n"
+ "</html>")
public static final ServerEndpoint HTML_SERVLET_OUTPUT_STREAM =
new ServerEndpoint("HTML_SERVLET_OUTPUT_STREAM", "htmlServletOutputStream",
200,
"<!DOCTYPE html>\n"
+ "<html lang=\"en\">\n"
+ "<head>\n"
+ " <meta charset=\"UTF-8\">\n"
+ " <title>Title</title>\n"
+ "</head>\n"
+ "<body>\n"
+ "<p>test works</p>\n"
+ "</body>\n"
+ "</html>")
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 <script type=\"text/javascript\"> Test </script>")
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 = "<!DOCTYPE html>\n" +
"<html lang=\"en\">\n" +
"<head>\n" +
" <script type=\"text/javascript\"> Test </script>\n" +
" <meta charset=\"UTF-8\">\n" +
" <title>Title</title>\n" +
"</head>\n" +
"<body>\n" +
"<p>test works</p>\n" +
"</body>\n" +
"</html>"
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 <script type=\"text/javascript\"> Test2 </script>")
def request = request(HTML_PRINT_WRITER, "GET")
def response = client.execute(request).aggregate().join()
expect:
response.status().code() == HTML_PRINT_WRITER.status
String result = "<!DOCTYPE html>\n" +
"<html lang=\"en\">\n" +
"<head>\n" +
" <script type=\"text/javascript\"> Test2 </script>\n" +
" <meta charset=\"UTF-8\">\n" +
" <title>Title</title>\n" +
"</head>\n" +
"<body>\n" +
"<p>test works</p>\n" +
"</body>\n" +
"</html>"
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)
}
}
}
}
}

View File

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

View File

@ -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<SERVER, CONTEXT>
extends AbstractHttpServerUsingTest<SERVER> {
@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<? extends Servlet> 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<Consumer<SpanDataAssert>> 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");
}
}
}

View File

@ -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<SERVER, CONTEXT> extends AbstractHttpServerTest<SERVER> {
public static final ServerEndpoint HTML_PRINT_WRITER =
new ServerEndpoint(
"HTML_PRINT_WRITER",
"htmlPrintWriter",
200,
"<!DOCTYPE html>\n"
+ "<html lang=\"en\">\n"
+ "<head>\n"
+ " <meta charset=\"UTF-8\">\n"
+ " <title>Title</title>\n"
+ "</head>\n"
+ "<body>\n"
+ "<p>test works</p>\n"
+ "</body>\n"
+ "</html>");
public static final ServerEndpoint HTML_SERVLET_OUTPUT_STREAM =
new ServerEndpoint(
"HTML_SERVLET_OUTPUT_STREAM",
"htmlServletOutputStream",
200,
"<!DOCTYPE html>\n"
+ "<html lang=\"en\">\n"
+ "<head>\n"
+ " <meta charset=\"UTF-8\">\n"
+ " <title>Title</title>\n"
+ "</head>\n"
+ "<body>\n"
+ "<p>test works</p>\n"
+ "</body>\n"
+ "</html>");
@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<? extends Servlet> servlet();
public abstract void addServlet(CONTEXT context, String path, Class<? extends Servlet> servlet)
throws Exception;
protected void setupServlets(CONTEXT context) throws Exception {
Class<? extends Servlet> 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 <script type=\"text/javascript\"> Test Test</script>");
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 =
"<!DOCTYPE html>\n"
+ "<html lang=\"en\">\n"
+ "<head>\n"
+ " <script type=\"text/javascript\"> Test Test</script>\n"
+ " <meta charset=\"UTF-8\">\n"
+ " <title>Title</title>\n"
+ "</head>\n"
+ "<body>\n"
+ "<p>test works</p>\n"
+ "</body>\n"
+ "</html>";
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 <script type=\"text/javascript\"> Test </script>");
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 =
"<!DOCTYPE html>\n"
+ "<html lang=\"en\">\n"
+ "<head>\n"
+ " <script type=\"text/javascript\"> Test </script>\n"
+ " <meta charset=\"UTF-8\">\n"
+ " <title>Title</title>\n"
+ "</head>\n"
+ "<body>\n"
+ "<p>test works</p>\n"
+ "</body>\n"
+ "</html>";
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))));
}
}

View File

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

View File

@ -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<Test>().configureEach {
jvmArgs("-Dotel.instrumentation.servlet.experimental.capture-request-parameters=test-parameter")
}
}

View File

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

View File

@ -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<Map.Entry<String, String>> getLoggedIds() {
return loggedIds;
}
private final List<Map.Entry<String, String>> 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);
}
}

View File

@ -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<? extends Servlet> servlet() {
return TestServlet5.Async.class;
}
@Override
public boolean errorEndpointUsesSendError() {
return false;
}
}

View File

@ -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<? extends Servlet> servlet() {
return TestServlet5.FakeAsync.class;
}
}

View File

@ -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<? extends Servlet> servlet() {
return TestServlet5.Sync.class;
}
}

View File

@ -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<Tomcat, Context> {
@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<? extends Servlet> 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<String> loggedTraces =
accessLogValue.getLoggedIds().stream().map(Map.Entry::getKey).collect(Collectors.toList());
List<String> loggedSpans =
accessLogValue.getLoggedIds().stream()
.map(Map.Entry::getValue)
.collect(Collectors.toList());
testing()
.waitAndAssertTraces(
IntStream.range(0, count)
.mapToObj(
i ->
(Consumer<TraceAssert>)
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<BiConsumer<SpanDataAssert, TraceAssert>> 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<SpanDataAssert>) span -> e.accept(span, trace))
.collect(Collectors.toList()));
SpanData span = trace.getSpan(0);
Map.Entry<String, String> entry = accessLogValue.getLoggedIds().get(0);
assertThat(entry.getKey()).isEqualTo(span.getTraceId());
assertThat(entry.getValue()).isEqualTo(span.getSpanId());
});
}
}

View File

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

View File

@ -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<? extends Servlet> 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;
}
}

View File

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

View File

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

View File

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

View File

@ -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<? extends Filter> 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<? extends Filter> 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.");
}
}
}

View File

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

View File

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

View File

@ -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<Tomcat, Context> {
@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<? extends Servlet> 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";
}
}

View File

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