Skip to content

Commit b6bb447

Browse files
jetty-httpclient-12: send method must pass along implemented response listeners
The super HttpRequest.send call says: > The listener passed to this method may implement not only Response.CompleteListener > but also other response listener interfaces, and all the events implemented will be notified. The current implementation passes through a lambda that implements CompleteListener only, and does not delegate-in-scope to any other callback methods you might provide
1 parent 54693b6 commit b6bb447

File tree

2 files changed

+62
-6
lines changed

2 files changed

+62
-6
lines changed

instrumentation/jetty-httpclient/jetty-httpclient-12.0/library/src/main/java/io/opentelemetry/instrumentation/jetty/httpclient/v12_0/TracingHttpRequest.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import io.opentelemetry.context.Scope;
1010
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
1111
import io.opentelemetry.instrumentation.jetty.httpclient.v12_0.internal.JettyClientTracingListener;
12+
import io.opentelemetry.instrumentation.jetty.httpclient.v12_0.internal.JettyClientWrapUtil;
1213
import java.net.URI;
1314
import java.nio.ByteBuffer;
1415
import org.eclipse.jetty.client.HttpClient;
@@ -36,12 +37,7 @@ public void send(Response.CompleteListener listener) {
3637
parentContext = Context.current();
3738
// start span and attach listeners.
3839
JettyClientTracingListener.handleRequest(parentContext, this, instrumenter);
39-
super.send(
40-
result -> {
41-
try (Scope scope = openScope()) {
42-
listener.onComplete(result);
43-
}
44-
});
40+
super.send(JettyClientWrapUtil.wrapTheListener(listener, parentContext));
4541
}
4642

4743
private Scope openScope() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.jetty.httpclient.v12_0.internal;
7+
8+
import io.opentelemetry.context.Context;
9+
import io.opentelemetry.context.Scope;
10+
import java.lang.reflect.InvocationTargetException;
11+
import java.lang.reflect.Proxy;
12+
import java.util.ArrayList;
13+
import java.util.List;
14+
import org.eclipse.jetty.client.Response;
15+
16+
/**
17+
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
18+
* any time.
19+
*/
20+
public final class JettyClientWrapUtil {
21+
22+
private JettyClientWrapUtil() {}
23+
24+
/**
25+
* Utility to wrap the response listeners only, this includes the important CompleteListener.
26+
*
27+
* @param context top level context that is above the Jetty client span context
28+
* @param listener listener passed to Jetty client send() method
29+
* @return wrapped listener
30+
*/
31+
public static Response.CompleteListener wrapTheListener(
32+
Response.CompleteListener listener, Context context) {
33+
if (listener == null) {
34+
return listener;
35+
}
36+
37+
Class<?> listenerClass = listener.getClass();
38+
List<Class<?>> interfaces = new ArrayList<>();
39+
for (Class<?> type : Response.Listener.class.getInterfaces()) {
40+
if (type.isInstance(listener)) {
41+
interfaces.add(type);
42+
}
43+
}
44+
if (interfaces.isEmpty()) {
45+
return listener;
46+
}
47+
48+
return (Response.CompleteListener)
49+
Proxy.newProxyInstance(
50+
listenerClass.getClassLoader(),
51+
interfaces.toArray(new Class<?>[0]),
52+
(proxy, method, args) -> {
53+
try (Scope ignored = context.makeCurrent()) {
54+
return method.invoke(listener, args);
55+
} catch (InvocationTargetException exception) {
56+
throw exception.getCause();
57+
}
58+
});
59+
}
60+
}

0 commit comments

Comments
 (0)