Fix failing test on latest jersey client
MODIFY
instrumentation/jaxrs-client/jaxrs-client-2.0/jaxrs-client-2.0-jersey-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrsclient/v2_0/JerseyClientInstrumentationModule.java
instrumentation/jaxrs-client/jaxrs-client-2.0/jaxrs-client-2.0-jersey-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrsclient/v2_0/JerseyClientInstrumentationModule.java
@@ -8,7 +8,7 @@ package io.opentelemetry.javaagent.instrumentation.jaxrsclient.v2_0;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;
-import static net.bytebuddy.matcher.ElementMatchers.returns;
+import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.tooling.InstrumentationModule;
@@ -17,12 +17,13 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.concurrent.Future;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.implementation.bytecode.assign.Assigner;
import net.bytebuddy.matcher.ElementMatcher;
import org.glassfish.jersey.client.ClientRequest;
+import org.glassfish.jersey.client.OpenTelemetryResponseCallbackWrapper;
/**
* JAX-RS Client API doesn't define a good point where we can handle connection failures, so we must
@@ -35,6 +36,11 @@ public class JerseyClientInstrumentationModule extends InstrumentationModule {
super("jaxrs-client", "jaxrs-client-2.0", "jersey-client", "jersey-client-2.0");
}
+ @Override
+ public boolean isHelperClass(String className) {
+ return className.equals("org.glassfish.jersey.client.OpenTelemetryResponseCallbackWrapper");
+ }
+
@Override
public List<TypeInstrumentation> typeInstrumentations() {
return Collections.singletonList(new JerseyClientConnectionErrorInstrumentation());
@@ -43,7 +49,7 @@ public class JerseyClientInstrumentationModule extends InstrumentationModule {
public static class JerseyClientConnectionErrorInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
- return named("org.glassfish.jersey.client.JerseyInvocation");
+ return named("org.glassfish.jersey.client.ClientRuntime");
}
@Override
@@ -51,11 +57,16 @@ public class JerseyClientInstrumentationModule extends InstrumentationModule {
Map<ElementMatcher<? super MethodDescription>, String> transformers = new HashMap<>();
transformers.put(
- isMethod().and(isPublic()).and(named("invoke")),
+ isMethod()
+ .and(isPublic())
+ .and(named("invoke"))
+ .and(takesArgument(0, named("org.glassfish.jersey.client.ClientRequest"))),
JerseyClientInstrumentationModule.class.getName() + "$InvokeAdvice");
-
transformers.put(
- isMethod().and(isPublic()).and(named("submit")).and(returns(Future.class)),
+ isMethod()
+ .and(named("submit").or(named("createRunnableForAsyncProcessing")))
+ .and(takesArgument(0, named("org.glassfish.jersey.client.ClientRequest")))
+ .and(takesArgument(1, named("org.glassfish.jersey.client.ResponseCallback"))),
JerseyClientInstrumentationModule.class.getName() + "$SubmitAdvice");
return transformers;
@@ -66,8 +77,7 @@ public class JerseyClientInstrumentationModule extends InstrumentationModule {
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void handleError(
- @Advice.FieldValue("requestContext") ClientRequest context,
- @Advice.Thrown Throwable throwable) {
+ @Advice.Argument(0) ClientRequest context, @Advice.Thrown Throwable throwable) {
if (throwable != null) {
JerseyClientUtil.handleException(context, throwable);
}
@@ -76,11 +86,13 @@ public class JerseyClientInstrumentationModule extends InstrumentationModule {
public static class SubmitAdvice {
- @Advice.OnMethodExit(suppress = Throwable.class)
+ // using dynamic typing because parameter type is package private
+ @Advice.OnMethodEnter(suppress = Throwable.class)
public static void handleError(
- @Advice.FieldValue("requestContext") ClientRequest context,
- @Advice.Return(readOnly = false) Future<?> future) {
- future = JerseyClientUtil.addErrorReporting(context, future);
+ @Advice.Argument(0) ClientRequest context,
+ @Advice.Argument(value = 1, readOnly = false, typing = Assigner.Typing.DYNAMIC)
+ Object callback) {
+ callback = OpenTelemetryResponseCallbackWrapper.wrap(context, callback);
}
}
}

MODIFY
instrumentation/jaxrs-client/jaxrs-client-2.0/jaxrs-client-2.0-jersey-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrsclient/v2_0/JerseyClientUtil.java
instrumentation/jaxrs-client/jaxrs-client-2.0/jaxrs-client-2.0-jersey-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrsclient/v2_0/JerseyClientUtil.java
@@ -8,34 +8,10 @@ package io.opentelemetry.javaagent.instrumentation.jaxrsclient.v2_0;
import static io.opentelemetry.javaagent.instrumentation.jaxrsclient.v2_0.JaxRsClientTracer.tracer;
import io.opentelemetry.context.Context;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Future;
import org.glassfish.jersey.client.ClientRequest;
public final class JerseyClientUtil {
- public static Future<?> addErrorReporting(ClientRequest context, Future<?> future) {
- // since jersey 2.30 jersey internally uses CompletableFuture
- // we can't wrap it with WrappedFuture as it causes ClassCastException when casting
- // to CompletableFuture
- if (future instanceof CompletableFuture) {
- future =
- ((CompletableFuture<?>) future)
- .whenComplete(
- (result, exception) -> {
- if (exception != null) {
- handleException(context, exception);
- }
- });
- } else {
- if (!(future instanceof WrappedFuture)) {
- future = new WrappedFuture<>(future, context);
- }
- }
-
- return future;
- }
-
public static void handleException(ClientRequest context, Throwable exception) {
Object prop = context.getProperty(ClientTracingFilter.CONTEXT_PROPERTY_NAME);
if (prop instanceof Context) {

DELETE
instrumentation/jaxrs-client/jaxrs-client-2.0/jaxrs-client-2.0-jersey-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrsclient/v2_0/WrappedFuture.java
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package io.opentelemetry.javaagent.instrumentation.jaxrsclient.v2_0;
-
-import static io.opentelemetry.javaagent.instrumentation.jaxrsclient.v2_0.JerseyClientUtil.handleException;
-
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import org.glassfish.jersey.client.ClientRequest;
-
-public class WrappedFuture<T> implements Future<T> {
-
- private final Future<T> wrapped;
- private final ClientRequest context;
-
- public WrappedFuture(Future<T> wrapped, ClientRequest context) {
- this.wrapped = wrapped;
- this.context = context;
- }
-
- @Override
- public boolean cancel(boolean mayInterruptIfRunning) {
- return wrapped.cancel(mayInterruptIfRunning);
- }
-
- @Override
- public boolean isCancelled() {
- return wrapped.isCancelled();
- }
-
- @Override
- public boolean isDone() {
- return wrapped.isDone();
- }
-
- @Override
- public T get() throws InterruptedException, ExecutionException {
- try {
- return wrapped.get();
- } catch (ExecutionException e) {
- handleException(context, e.getCause());
- throw e;
- }
- }
-
- @Override
- public T get(long timeout, TimeUnit unit)
- throws InterruptedException, ExecutionException, TimeoutException {
- try {
- return wrapped.get(timeout, unit);
- } catch (ExecutionException e) {
- handleException(context, e.getCause());
- throw e;
- }
- }
-}

ADD
instrumentation/jaxrs-client/jaxrs-client-2.0/jaxrs-client-2.0-jersey-2.0/javaagent/src/main/java/org/glassfish/jersey/client/OpenTelemetryResponseCallbackWrapper.java
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package org.glassfish.jersey.client;
+
+import static io.opentelemetry.javaagent.instrumentation.jaxrsclient.v2_0.JerseyClientUtil.handleException;
+
+import javax.ws.rs.ProcessingException;
+import org.glassfish.jersey.process.internal.RequestScope;
+
+// implemented interface is package private so wrapper needs to be in the same package
+public class OpenTelemetryResponseCallbackWrapper implements ResponseCallback {
+ private final ClientRequest request;
+ private final ResponseCallback delegate;
+
+ public OpenTelemetryResponseCallbackWrapper(ClientRequest request, ResponseCallback delegate) {
+ this.request = request;
+ this.delegate = delegate;
+ }
+
+ public static Object wrap(ClientRequest request, Object callback) {
+ if (callback instanceof ResponseCallback) {
+ return new OpenTelemetryResponseCallbackWrapper(request, (ResponseCallback) callback);
+ }
+ return callback;
+ }
+
+ @Override
+ public void completed(ClientResponse clientResponse, RequestScope requestScope) {
+ delegate.completed(clientResponse, requestScope);
+ }
+
+ @Override
+ public void failed(ProcessingException exception) {
+ handleException(request, exception.getCause());
+ delegate.failed(exception);
+ }
+}

Fix failing test on latest jersey client (#2809)