Convert Jetty 8.0 groovy to java (#7975)

related to
https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/7195
This commit is contained in:
Helen 2023-03-11 01:24:35 -08:00 committed by GitHub
parent 995baa8888
commit 468aa9e777
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 291 additions and 242 deletions

View File

@ -29,3 +29,11 @@ dependencies {
latestDepTestLibrary("org.eclipse.jetty:jetty-server:10.+") // see jetty-11.0 module
latestDepTestLibrary("org.eclipse.jetty:jetty-servlet:10.+") // see jetty-11.0 module
}
// jetty-server 10+ requires Java 11
val latestDepTest = findProperty("testLatestDeps") as Boolean
if (latestDepTest) {
otelJava {
minJavaVersionSupported.set(JavaVersion.VERSION_11)
}
}

View File

@ -1,144 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
import io.opentelemetry.api.common.AttributeKey
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.semconv.trace.attributes.SemanticAttributes
import org.eclipse.jetty.server.Request
import org.eclipse.jetty.server.Response
import org.eclipse.jetty.server.Server
import org.eclipse.jetty.server.handler.AbstractHandler
import org.eclipse.jetty.server.handler.ErrorHandler
import spock.lang.Shared
import javax.servlet.DispatcherType
import javax.servlet.ServletException
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.NOT_FOUND
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS
class JettyHandlerTest extends HttpServerTest<Server> implements AgentTestTrait {
static ErrorHandler errorHandler = new ErrorHandler() {
@Override
protected void handleErrorPage(HttpServletRequest request, Writer writer, int code, String message) throws IOException {
Throwable th = (Throwable) request.getAttribute("javax.servlet.error.exception")
message = th ? th.message : message
if (message) {
writer.write(message)
}
}
}
@Shared
TestHandler testHandler = new TestHandler()
@Override
Server startServer(int port) {
def server = new Server(port)
server.setHandler(handler())
server.addBean(errorHandler)
server.start()
return server
}
AbstractHandler handler() {
testHandler
}
@Override
void stopServer(Server server) {
server.stop()
}
@Override
Set<AttributeKey<?>> httpAttributes(ServerEndpoint endpoint) {
def attributes = super.httpAttributes(endpoint)
attributes.remove(SemanticAttributes.HTTP_ROUTE)
attributes
}
@Override
boolean hasResponseSpan(ServerEndpoint endpoint) {
endpoint == REDIRECT || endpoint == ERROR
}
@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
}
}
static void handleRequest(Request request, HttpServletResponse response) {
ServerEndpoint endpoint = ServerEndpoint.forPath(request.requestURI)
controller(endpoint) {
response.contentType = "text/plain"
switch (endpoint) {
case SUCCESS:
response.status = endpoint.status
response.writer.print(endpoint.body)
break
case QUERY_PARAM:
response.status = endpoint.status
response.writer.print(request.queryString)
break
case REDIRECT:
response.sendRedirect(endpoint.body)
break
case ERROR:
response.sendError(endpoint.status, endpoint.body)
break
case CAPTURE_HEADERS:
response.setHeader("X-Test-Response", request.getHeader("X-Test-Request"))
response.status = endpoint.status
response.writer.print(endpoint.body)
break
case EXCEPTION:
throw new Exception(endpoint.body)
case INDEXED_CHILD:
INDEXED_CHILD.collectSpanAttributes { name -> request.getParameter(name) }
response.status = endpoint.status
response.writer.print(endpoint.body)
break
default:
response.status = NOT_FOUND.status
response.writer.print(NOT_FOUND.body)
break
}
}
}
static class TestHandler extends AbstractHandler {
@Override
void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
//This line here is to verify that we don't break Jetty if it wants to cast to implementation class
//See https://github.com/open-telemetry/opentelemetry-java-instrumentation/issues/1096
Response jettyResponse = response as Response
if (baseRequest.dispatcherType != DispatcherType.ERROR) {
handleRequest(baseRequest, jettyResponse)
baseRequest.handled = true
} else {
errorHandler.handle(target, baseRequest, baseRequest, response)
}
}
}
}

View File

@ -1,98 +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 org.eclipse.jetty.util.thread.QueuedThreadPool
import static org.junit.jupiter.api.Assumptions.assumeTrue
class QueuedThreadPoolTest extends AgentInstrumentationSpecification {
def "QueueThreadPool 'dispatch' propagates"() {
setup:
def pool = new QueuedThreadPool()
// run test only if QueuedThreadPool has dispatch method
// dispatch method was removed in jetty 9.1
assumeTrue(pool.metaClass.getMetaMethod("dispatch", Runnable) != null)
pool.start()
new Runnable() {
@Override
void run() {
runWithSpan("parent") {
// this child will have a span
def child1 = new JavaAsyncChild()
// this child won't
def child2 = new JavaAsyncChild(false, false)
pool.dispatch(child1)
pool.dispatch(child2)
child1.waitForCompletion()
child2.waitForCompletion()
}
}
}.run()
expect:
assertTraces(1) {
trace(0, 2) {
span(0) {
name "parent"
kind SpanKind.INTERNAL
hasNoParent()
}
span(1) {
name "asyncChild"
kind SpanKind.INTERNAL
childOf span(0)
}
}
}
cleanup:
pool.stop()
}
def "QueueThreadPool 'dispatch' propagates lambda"() {
setup:
def pool = new QueuedThreadPool()
// run test only if QueuedThreadPool has dispatch method
// dispatch method was removed in jetty 9.1
assumeTrue(pool.metaClass.getMetaMethod("dispatch", Runnable) != null)
pool.start()
JavaAsyncChild child = new JavaAsyncChild(true, true)
new Runnable() {
@Override
void run() {
runWithSpan("parent") {
pool.dispatch(JavaLambdaMaker.lambda(child))
}
}
}.run()
// We block in child to make sure spans close in predictable order
child.unblock()
child.waitForCompletion()
expect:
assertTraces(1) {
trace(0, 2) {
span(0) {
name "parent"
kind SpanKind.INTERNAL
hasNoParent()
}
span(1) {
name "asyncChild"
kind SpanKind.INTERNAL
childOf span(0)
}
}
}
cleanup:
pool.stop()
}
}

View File

@ -3,6 +3,8 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.jetty.v8_0;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.trace.Tracer;
import java.util.concurrent.Callable;

View File

@ -3,6 +3,8 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.jetty.v8_0;
public class JavaLambdaMaker {
@SuppressWarnings("FunctionalExpressionCanBeFolded")

View File

@ -0,0 +1,175 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.jetty.v8_0;
import static io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions.DEFAULT_HTTP_ATTRIBUTES;
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.REDIRECT;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.common.collect.Sets;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest;
import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions;
import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint;
import io.opentelemetry.sdk.testing.assertj.SpanDataAssert;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import java.io.IOException;
import java.io.Writer;
import java.util.Collections;
import javax.servlet.DispatcherType;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.server.handler.ErrorHandler;
import org.junit.jupiter.api.extension.RegisterExtension;
public class JettyHandlerTest extends AbstractHttpServerTest<Server> {
@RegisterExtension
static final InstrumentationExtension testing = HttpServerInstrumentationExtension.forAgent();
private static final ErrorHandler errorHandler =
new ErrorHandler() {
@Override
protected void handleErrorPage(
HttpServletRequest request, Writer writer, int code, String message)
throws IOException {
Throwable th = (Throwable) request.getAttribute("javax.servlet.error.exception");
String errorMsg = th != null ? th.getMessage() : message;
if (errorMsg != null) {
writer.write(errorMsg);
}
}
};
private static final TestHandler testHandler = new TestHandler();
@Override
protected Server setupServer() {
Server server = new Server(port);
server.setHandler(testHandler);
server.addBean(errorHandler);
try {
server.start();
} catch (Exception e) {
throw new RuntimeException(e);
}
return server;
}
@Override
protected void stopServer(Server server) {
try {
server.stop();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
protected void configure(HttpServerTestOptions options) {
options.setHttpAttributes(
unused ->
Sets.difference(
DEFAULT_HTTP_ATTRIBUTES, Collections.singleton(SemanticAttributes.HTTP_ROUTE)));
options.setHasResponseSpan(endpoint -> endpoint == REDIRECT || endpoint == ERROR);
options.setExpectedException(new IllegalStateException(EXCEPTION.getBody()));
}
@Override
protected SpanDataAssert assertResponseSpan(
SpanDataAssert span, String method, ServerEndpoint endpoint) {
if (endpoint == REDIRECT) {
span.satisfies(spanData -> assertThat(spanData.getName()).endsWith(".sendRedirect"));
} else if (endpoint == ERROR) {
span.satisfies(spanData -> assertThat(spanData.getName()).endsWith(".sendError"));
}
span.hasKind(SpanKind.INTERNAL).hasAttributesSatisfying(Attributes::isEmpty);
return span;
}
private static void handleRequest(Request request, HttpServletResponse response) {
ServerEndpoint endpoint = ServerEndpoint.forPath(request.getRequestURI());
controller(
endpoint,
() -> {
try {
return response(request, response, endpoint);
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
private static HttpServletResponse response(
Request request, HttpServletResponse response, ServerEndpoint endpoint) throws IOException {
response.setContentType("text/plain");
switch (endpoint) {
case SUCCESS:
response.setStatus(endpoint.getStatus());
response.getWriter().print(endpoint.getBody());
break;
case QUERY_PARAM:
response.setStatus(endpoint.getStatus());
response.getWriter().print(request.getQueryString());
break;
case REDIRECT:
response.sendRedirect(endpoint.getBody());
break;
case ERROR:
response.sendError(endpoint.getStatus(), endpoint.getBody());
break;
case CAPTURE_HEADERS:
response.setHeader("X-Test-Response", request.getHeader("X-Test-Request"));
response.setStatus(endpoint.getStatus());
response.getWriter().print(endpoint.getBody());
break;
case EXCEPTION:
throw new IllegalStateException(endpoint.getBody());
case INDEXED_CHILD:
INDEXED_CHILD.collectSpanAttributes(name -> request.getParameter(name));
response.setStatus(endpoint.getStatus());
response.getWriter().print(endpoint.getBody());
break;
default:
response.setStatus(NOT_FOUND.getStatus());
response.getWriter().print(NOT_FOUND.getBody());
break;
}
return response;
}
private static class TestHandler extends AbstractHandler {
@Override
public void handle(
String target,
Request baseRequest,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
// This line here is to verify that we don't break Jetty if it wants to cast to implementation
// class
Response jettyResponse = (Response) response;
if (baseRequest.getDispatcherType() != DispatcherType.ERROR) {
handleRequest(baseRequest, jettyResponse);
baseRequest.setHandled(true);
} else {
errorHandler.handle(target, baseRequest, baseRequest, response);
}
}
}
}

View File

@ -0,0 +1,104 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.jetty.v8_0;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import java.lang.reflect.Method;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
class QueuedThreadPoolTest {
@RegisterExtension
private static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
@Test
void dispatchPropagates() throws Exception {
QueuedThreadPool pool = new QueuedThreadPool();
// run test only if QueuedThreadPool has dispatch method
// dispatch method was removed in jetty 9.1
Method dispatch = null;
try {
dispatch = QueuedThreadPool.class.getMethod("dispatch", Runnable.class);
} catch (NoSuchMethodException ignore) {
// ignore
}
assumeTrue(dispatch != null);
pool.start();
Method finalDispatch = dispatch;
testing.runWithSpan(
"parent",
() -> {
// this child will have a span
JavaAsyncChild child1 = new JavaAsyncChild();
// this child won't
JavaAsyncChild child2 = new JavaAsyncChild(false, false);
if (finalDispatch != null) {
finalDispatch.invoke(pool, child1);
finalDispatch.invoke(pool, child2);
child1.waitForCompletion();
child2.waitForCompletion();
}
});
testing.waitAndAssertTraces(
trace ->
trace.hasSpansSatisfyingExactlyInAnyOrder(
span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(),
span ->
span.hasName("asyncChild")
.hasKind(SpanKind.INTERNAL)
.hasParent(trace.getSpan(0))));
pool.stop();
}
@Test
void dispatchPropagatesLambda() throws Exception {
QueuedThreadPool pool = new QueuedThreadPool();
// run test only if QueuedThreadPool has dispatch method
// dispatch method was removed in jetty 9.1
Method dispatch = null;
try {
dispatch = QueuedThreadPool.class.getMethod("dispatch", Runnable.class);
} catch (NoSuchMethodException ignore) {
// ignore
}
assumeTrue(dispatch != null);
pool.start();
JavaAsyncChild child = new JavaAsyncChild(true, true);
Method finalDispatch = dispatch;
testing.runWithSpan(
"parent",
() -> {
if (finalDispatch != null) {
finalDispatch.invoke(pool, JavaLambdaMaker.lambda(child));
}
});
// We block in child to make sure spans close in predictable order
child.unblock();
child.waitForCompletion();
testing.waitAndAssertTraces(
trace ->
trace.hasSpansSatisfyingExactlyInAnyOrder(
span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(),
span ->
span.hasName("asyncChild")
.hasKind(SpanKind.INTERNAL)
.hasParent(trace.getSpan(0))));
pool.stop();
}
}