First draft of jersey 1.19 client instrumentation
ADD
dd-java-agent/instrumentation/jax-rs-client/jax-rs-client-1/jax-rs-client-1.gradle
+ext {
+ jerseyClientVersion = '1.19.1'
+}
+
+muzzle {
+ pass {
+ group = "javax.ws.rs"
+ module = "javax.ws.rs-api"
+ versions = "[1.0,2.0)"
+ }
+// pass {
+// // We want to support the dropwizard clients too.
+// group = 'io.dropwizard'
+// module = 'dropwizard-client'
+// versions = "[0.8.0,)"
+// assertInverse = true
+// }
+}
+
+apply from: "${rootDir}/gradle/java.gradle"
+
+apply plugin: 'org.unbroken-dome.test-sets'
+
+testSets {
+ latestDepTest {
+ dirName = 'test'
+ }
+}
+
+dependencies {
+ compileOnly group: 'com.sun.jersey', name: 'jersey-client', version: jerseyClientVersion
+ //compileOnly group: 'javax.ws.rs', name: 'javax.ws.rs-api', version: '2.0.1'
+ //compileOnly group: 'javax.annotation', name: 'javax.annotation-api', version: '1.2'
+
+ compile deps.bytebuddy
+ compile deps.opentracing
+ annotationProcessor deps.autoservice
+ implementation deps.autoservice
+
+ compile project(':dd-java-agent:agent-tooling')
+
+ testCompile project(':dd-java-agent:testing')
+ testCompile project(':dd-java-agent:instrumentation:java-concurrent')
+
+ testCompile group: 'com.sun.jersey', name: 'jersey-client', version: jerseyClientVersion
+
+// testCompile group: 'org.apache.cxf', name: 'cxf-rt-rs-client', version: '3.1.0'
+ // Doesn't work with CXF 3.0.x because their context is wrong:
+ // https://github.com/apache/cxf/commit/335c7bad2436f08d6d54180212df5a52157c9f21
+
+// testCompile group: 'javax.xml.bind', name: 'jaxb-api', version: '2.2.3'
+
+// latestDepTestCompile group: 'org.glassfish.jersey.inject', name: 'jersey-hk2', version: '2.27'
+ latestDepTestCompile group: 'com.sun.jersey', name: 'jersey-client', version: jerseyClientVersion
+// latestDepTestCompile group: 'org.apache.cxf', name: 'cxf-rt-rs-client', version: '3.2.6'
+// latestDepTestCompile group: 'org.jboss.resteasy', name: 'resteasy-client', version: '3.0.26.Final'
+}

ADD
dd-java-agent/instrumentation/jax-rs-client/jax-rs-client-1/src/main/java/datadog/trace/instrumentation/jaxrs/v1/InjectAdapter.java
+package datadog.trace.instrumentation.jaxrs.v1;
+
+import io.opentracing.propagation.TextMap;
+
+import javax.ws.rs.core.MultivaluedMap;
+import java.util.Iterator;
+import java.util.Map;
+
+public final class InjectAdapter implements TextMap {
+ private final MultivaluedMap<String, Object> map;
+
+ public InjectAdapter(final MultivaluedMap<String, Object> map) {
+ this.map = map;
+ }
+
+ @Override
+ public Iterator<Map.Entry<String, String>> iterator() {
+ throw new UnsupportedOperationException(
+ "InjectAdapter should only be used with Tracer.inject()");
+ }
+
+ @Override
+ public void put(final String key, final String value) {
+ // Don't allow duplicates.
+ map.putSingle(key, value);
+ }
+}

ADD
dd-java-agent/instrumentation/jax-rs-client/jax-rs-client-1/src/main/java/datadog/trace/instrumentation/jaxrs/v1/JaxRsClientV1Decorator.java
+package datadog.trace.instrumentation.jaxrs.v1;
+
+import com.sun.jersey.api.client.ClientRequest;
+import com.sun.jersey.api.client.ClientResponse;
+import datadog.trace.agent.decorator.HttpClientDecorator;
+
+import java.net.URI;
+
+public class JaxRsClientV1Decorator
+ extends HttpClientDecorator<ClientRequest, ClientResponse> {
+ public static final JaxRsClientV1Decorator DECORATE = new JaxRsClientV1Decorator();
+
+ @Override
+ protected String[] instrumentationNames() {
+ return new String[] {"jax-rs", "jaxrs", "jax-rs-client"};
+ }
+
+ @Override
+ protected String component() {
+ return "jax-rs.client";
+ }
+
+ @Override
+ protected String method(final ClientRequest httpRequest) {
+ return httpRequest.getMethod();
+ }
+
+ @Override
+ protected URI url(final ClientRequest httpRequest) {
+ return httpRequest.getURI();
+ }
+
+ @Override
+ protected String hostname(final ClientRequest httpRequest) {
+ return httpRequest.getURI().getHost();
+ }
+
+ @Override
+ protected Integer port(final ClientRequest httpRequest) {
+ return httpRequest.getURI().getPort();
+ }
+
+ @Override
+ protected Integer status(ClientResponse clientResponse) {
+ return clientResponse.getStatus();
+ }
+}

ADD
dd-java-agent/instrumentation/jax-rs-client/jax-rs-client-1/src/main/java/datadog/trace/instrumentation/jaxrs/v1/JaxRsClientV1Instrumentation.java
+package datadog.trace.instrumentation.jaxrs.v1;
+
+import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
+import static net.bytebuddy.matcher.ElementMatchers.named;
+import static net.bytebuddy.matcher.ElementMatchers.returns;
+import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
+import static datadog.trace.instrumentation.jaxrs.v1.JaxRsClientV1Decorator.DECORATE;
+import com.google.auto.service.AutoService;
+import com.sun.jersey.api.client.ClientRequest;
+import com.sun.jersey.api.client.ClientResponse;
+import datadog.trace.agent.tooling.Instrumenter;
+import datadog.trace.api.DDTags;
+import io.opentracing.Scope;
+import io.opentracing.Span;
+import io.opentracing.propagation.Format;
+import io.opentracing.util.GlobalTracer;
+import net.bytebuddy.asm.Advice;
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+import static java.util.Collections.singletonMap;
+
+import java.util.Map;
+
+@AutoService(Instrumenter.class)
+public final class JaxRsClientV1Instrumentation extends Instrumenter.Default {
+
+ public JaxRsClientV1Instrumentation() {
+ super("jax-rs", "jaxrs", "jax-rs-client");
+ }
+
+ @Override
+ public ElementMatcher<TypeDescription> typeMatcher() {
+ return safeHasSuperType(named("com.sun.jersey.api.client.ClientHandler"));
+ }
+
+ @Override
+ public String[] helperClassNames() {
+ return new String[] {
+ "datadog.trace.agent.decorator.BaseDecorator",
+ "datadog.trace.agent.decorator.ClientDecorator",
+ "datadog.trace.agent.decorator.HttpClientDecorator",
+ packageName + ".JaxRsClientDecorator",
+ packageName + ".ClientTracingFeature",
+ packageName + ".ClientTracingFilter",
+ packageName + ".InjectAdapter",
+ };
+ }
+
+ @Override
+ public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
+ return singletonMap(
+ named("handle")
+ .and(takesArgument(2, named("com.sun.jersey.api.client.ClientRequest")))
+ .and(returns(named("com.sun.jersey.api.client.ClientResponse"))),
+ HandleAdvice.class.getName()
+ );
+ }
+
+ public static class HandleAdvice {
+
+ @Advice.OnMethodEnter
+ public static Span onEnter(@Advice.Argument(value = 1) final ClientRequest request) {
+
+ // WARNING: this might be a chain...so we only have to trace the first int he chain.
+
+ final Span span =
+ GlobalTracer.get()
+ .buildSpan("jax-rs.client.call")
+ .withTag(DDTags.RESOURCE_NAME, request.getMethod() + " jax-rs.client.call")
+ .start();
+ try (final Scope scope = GlobalTracer.get().scopeManager().activate(span, false)) {
+ DECORATE.afterStart(span);
+ DECORATE.onRequest(span, request);
+
+ GlobalTracer.get()
+ .inject(
+ span.context(),
+ Format.Builtin.HTTP_HEADERS,
+ new InjectAdapter(request.getHeaders()));
+
+ request.getProperties().put("dd.span", span);
+ }
+
+ return span;
+ }
+
+ @Advice.OnMethodExit
+ public static void onExit(@Advice.Argument(value = 1) final ClientRequest request, ClientResponse response) {
+ Span span = (Span) request.getProperties().get("dd.span");
+ DECORATE.onResponse(span, response);
+ DECORATE.beforeFinish(span);
+ span.finish();
+ }
+ }
+}

MODIFY
settings.gradle
settings.gradle
@@ -52,6 +52,7 @@ include ':dd-java-agent:instrumentation:jax-rs-annotations'
include ':dd-java-agent:instrumentation:jax-rs-client'
include ':dd-java-agent:instrumentation:jax-rs-client:connection-error-handling-jersey'
include ':dd-java-agent:instrumentation:jax-rs-client:connection-error-handling-resteasy'
+include ':dd-java-agent:instrumentation:jax-rs-client:jax-rs-client-1'
include ':dd-java-agent:instrumentation:java-concurrent'
include ':dd-java-agent:instrumentation:java-concurrent:kotlin-testing'
include ':dd-java-agent:instrumentation:java-concurrent:scala-testing'

First draft of jersey 1.19 client instrumentation