JAX-RS with Apache CXF
MODIFY
instrumentation/jaxrs-client/jaxrs-client-2.0/jaxrs-client-2.0-common/javaagent/jaxrs-client-2.0-common-javaagent.gradle
instrumentation/jaxrs-client/jaxrs-client-2.0/jaxrs-client-2.0-common/javaagent/jaxrs-client-2.0-common-javaagent.gradle
@@ -19,6 +19,7 @@ dependencies {
compileOnly group: 'javax.ws.rs', name: 'javax.ws.rs-api', version: '2.0.1'
compileOnly group: 'javax.annotation', name: 'javax.annotation-api', version: '1.3.2'
+ testInstrumentation project(':instrumentation:jaxrs-client:jaxrs-client-2.0:jaxrs-client-2.0-cxf-3.0:javaagent')
testInstrumentation project(':instrumentation:jaxrs-client:jaxrs-client-2.0:jaxrs-client-2.0-jersey-2.0:javaagent')
testInstrumentation project(':instrumentation:jaxrs-client:jaxrs-client-2.0:jaxrs-client-2.0-resteasy-2.0:javaagent')
@@ -37,6 +38,5 @@ dependencies {
latestDepTestLibrary group: 'org.glassfish.jersey.inject', name: 'jersey-hk2', version: '2.+'
latestDepTestLibrary group: 'org.glassfish.jersey.core', name: 'jersey-client', version: '2.+'
- latestDepTestLibrary group: 'org.apache.cxf', name: 'cxf-rt-rs-client', version: '3.2.6'
latestDepTestLibrary group: 'org.jboss.resteasy', name: 'resteasy-client', version: '3.0.26.Final'
}

MODIFY
instrumentation/jaxrs-client/jaxrs-client-2.0/jaxrs-client-2.0-common/javaagent/src/test/groovy/JaxRsClientAsyncTest.groovy
instrumentation/jaxrs-client/jaxrs-client-2.0/jaxrs-client-2.0-common/javaagent/src/test/groovy/JaxRsClientAsyncTest.groovy
@@ -90,18 +90,10 @@ class CxfClientAsyncTest extends JaxRsClientAsyncTest {
@Override
ClientBuilder builder() {
return new ClientBuilderImpl()
+ .property("http.connection.timeout", (long) CONNECT_TIMEOUT_MS)
}
boolean testRedirects() {
false
}
-
- boolean testConnectionFailure() {
- false
- }
-
- boolean testRemoteConnection() {
- // FIXME: span not reported correctly.
- false
- }
}

MODIFY
instrumentation/jaxrs-client/jaxrs-client-2.0/jaxrs-client-2.0-common/javaagent/src/test/groovy/JaxRsClientTest.groovy
instrumentation/jaxrs-client/jaxrs-client-2.0/jaxrs-client-2.0-common/javaagent/src/test/groovy/JaxRsClientTest.groovy
@@ -117,20 +117,10 @@ class CxfClientTest extends JaxRsClientTest {
@Override
ClientBuilder builder() {
return new ClientBuilderImpl()
-// .property(ClientImpl.HTTP_CONNECTION_TIMEOUT_PROP, (long) CONNECT_TIMEOUT_MS)
-// .property(ClientImpl.HTTP_RECEIVE_TIMEOUT_PROP, (long) READ_TIMEOUT_MS)
+ .property("http.connection.timeout", (long) CONNECT_TIMEOUT_MS)
}
boolean testRedirects() {
false
}
-
- boolean testConnectionFailure() {
- false
- }
-
- boolean testRemoteConnection() {
- // FIXME: span not reported correctly.
- false
- }
}

ADD
instrumentation/jaxrs-client/jaxrs-client-2.0/jaxrs-client-2.0-cxf-3.0/javaagent/jaxrs-client-2.0-cxf-3.0-javaagent.gradle
+apply from: "$rootDir/gradle/instrumentation.gradle"
+
+muzzle {
+ pass {
+ group = "org.apache.cxf"
+ module = "cxf-rt-rs-client"
+ versions = "[3.0.0,)"
+ }
+}
+
+dependencies {
+ library group: 'org.apache.cxf', name: 'cxf-rt-rs-client', version: '3.0.0'
+
+ implementation project(':instrumentation:jaxrs-client:jaxrs-client-2.0:jaxrs-client-2.0-common:javaagent')
+}

ADD
instrumentation/jaxrs-client/jaxrs-client-2.0/jaxrs-client-2.0-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrsclient/v2_0/CxfClientInstrumentationModule.java
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.jaxrsclient.v2_0;
+
+import static java.util.Arrays.asList;
+import static net.bytebuddy.matcher.ElementMatchers.named;
+import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
+
+import com.google.auto.service.AutoService;
+import io.opentelemetry.javaagent.tooling.InstrumentationModule;
+import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import net.bytebuddy.asm.Advice;
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+import org.apache.cxf.message.Message;
+
+/**
+ * JAX-RS Client API doesn't define a good point where we can handle connection failures, so we must
+ * handle these errors at the implementation level.
+ */
+@AutoService(InstrumentationModule.class)
+public class CxfClientInstrumentationModule extends InstrumentationModule {
+
+ public CxfClientInstrumentationModule() {
+ super("jaxrs-client", "jaxrs-client-2.0", "cxf-client", "cxf-client-3.0");
+ }
+
+ @Override
+ public List<TypeInstrumentation> typeInstrumentations() {
+ return asList(
+ new CxfClientConnectionErrorInstrumentation(),
+ new CxfAsyncClientConnectionErrorInstrumentation());
+ }
+
+ public static class CxfClientConnectionErrorInstrumentation implements TypeInstrumentation {
+ @Override
+ public ElementMatcher<TypeDescription> typeMatcher() {
+ return named("org.apache.cxf.jaxrs.client.AbstractClient");
+ }
+
+ @Override
+ public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
+ return Collections.singletonMap(
+ named("preProcessResult").and(takesArgument(0, named("org.apache.cxf.message.Message"))),
+ CxfClientInstrumentationModule.class.getName() + "$ErrorAdvice");
+ }
+ }
+
+ public static class CxfAsyncClientConnectionErrorInstrumentation implements TypeInstrumentation {
+ @Override
+ public ElementMatcher<TypeDescription> typeMatcher() {
+ return named("org.apache.cxf.jaxrs.client.JaxrsClientCallback");
+ }
+
+ @Override
+ public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
+ return Collections.singletonMap(
+ named("handleException")
+ .and(
+ takesArgument(0, named(Map.class.getName()))
+ .and(takesArgument(1, named(Throwable.class.getName())))),
+ CxfClientInstrumentationModule.class.getName() + "$AsyncErrorAdvice");
+ }
+ }
+
+ public static class ErrorAdvice {
+
+ @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
+ public static void handleError(
+ @Advice.Argument(0) Message message, @Advice.Thrown Throwable throwable) {
+ if (throwable != null) {
+ CxfClientUtil.handleException(message, throwable);
+ }
+ }
+ }
+
+ public static class AsyncErrorAdvice {
+
+ @Advice.OnMethodExit(suppress = Throwable.class)
+ public static void handleError(
+ @Advice.Argument(0) Map<String, Object> map, @Advice.Argument(1) Throwable throwable) {
+ if (throwable != null && map instanceof Message) {
+ CxfClientUtil.handleException((Message) map, throwable);
+ }
+ }
+ }
+}

ADD
instrumentation/jaxrs-client/jaxrs-client-2.0/jaxrs-client-2.0-cxf-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrsclient/v2_0/CxfClientUtil.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.JaxRsClientTracer.tracer;
+
+import io.opentelemetry.context.Context;
+import javax.ws.rs.client.ClientRequestContext;
+import org.apache.cxf.jaxrs.client.spec.ClientRequestContextImpl;
+import org.apache.cxf.message.Message;
+
+public final class CxfClientUtil {
+
+ public static void handleException(Message message, Throwable throwable) {
+ ClientRequestContext context = new ClientRequestContextImpl(message, false);
+ Object prop = context.getProperty(ClientTracingFilter.CONTEXT_PROPERTY_NAME);
+ if (prop instanceof Context) {
+ tracer().endExceptionally((Context) prop, throwable);
+ }
+ }
+
+ private CxfClientUtil() {}
+}

ADD
instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-cxf-3.2/javaagent/jaxrs-2.0-cxf-3.2-javaagent.gradle
+apply from: "$rootDir/gradle/instrumentation.gradle"
+
+muzzle {
+ // Cant assert fails because muzzle assumes all instrumentations will fail
+ // Instrumentations in jaxrs-2.0-common will pass
+ pass {
+ group = "org.apache.cxf"
+ module = "cxf-rt-frontend-jaxrs"
+ versions = "[3.2,)"
+ }
+}
+
+dependencies {
+ compileOnly group: 'javax.ws.rs', name: 'javax.ws.rs-api', version: '2.0'
+ library group: 'org.apache.cxf', name: 'cxf-rt-frontend-jaxrs', version: '3.2.0'
+
+ implementation project(':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-common:javaagent')
+
+ testInstrumentation project(':instrumentation:servlet:servlet-3.0:javaagent')
+ testInstrumentation project(':instrumentation:jetty-8.0:javaagent')
+
+ testImplementation project(':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-testing')
+ testImplementation group: 'javax.xml.bind', name: 'jaxb-api', version: '2.2.3'
+
+ testLibrary group: 'org.apache.cxf', name: 'cxf-rt-transports-http-jetty', version: '3.2.0'
+ testLibrary group: 'org.apache.cxf', name: 'cxf-rt-ws-policy', version: '3.2.0'
+}
\ No newline at end of file

ADD
instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-cxf-3.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/v2_0/CxfInstrumentationModule.java
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
+
+import com.google.auto.service.AutoService;
+import io.opentelemetry.javaagent.tooling.InstrumentationModule;
+import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
+import java.util.Collections;
+import java.util.List;
+
+@AutoService(InstrumentationModule.class)
+public class CxfInstrumentationModule extends InstrumentationModule {
+ public CxfInstrumentationModule() {
+ super("jaxrs", "jaxrs-2.0", "cxf", "cxf-3.2");
+ }
+
+ @Override
+ public List<TypeInstrumentation> typeInstrumentations() {
+ return Collections.singletonList(new CxfRequestContextInstrumentation());
+ }
+}

ADD
instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-cxf-3.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jaxrs/v2_0/CxfRequestContextInstrumentation.java
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0;
+
+import static io.opentelemetry.javaagent.instrumentation.jaxrs.v2_0.JaxRsAnnotationsTracer.tracer;
+import static java.util.Collections.singletonMap;
+import static net.bytebuddy.matcher.ElementMatchers.isMethod;
+import static net.bytebuddy.matcher.ElementMatchers.named;
+import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
+import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
+
+import io.opentelemetry.api.trace.Span;
+import io.opentelemetry.context.Scope;
+import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
+import java.lang.reflect.Method;
+import java.util.Map;
+import javax.ws.rs.container.ContainerRequestContext;
+import net.bytebuddy.asm.Advice;
+import net.bytebuddy.asm.Advice.Local;
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.description.type.TypeDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+import org.apache.cxf.jaxrs.impl.AbstractRequestContextImpl;
+import org.apache.cxf.jaxrs.model.MethodInvocationInfo;
+import org.apache.cxf.jaxrs.model.OperationResourceInfoStack;
+import org.apache.cxf.message.Message;
+
+/**
+ * CXF specific context instrumentation.
+ *
+ * <p>JAX-RS does not define a way to get the matched resource method from the <code>
+ * ContainerRequestContext</code>
+ *
+ * <p>In the CXF implementation, <code>Message</code> contains <code>OperationResourceInfoStack
+ * </code> which contains <code>MethodInvocationInfo</code>. The matched resource method can be
+ * retrieved from that object
+ */
+public class CxfRequestContextInstrumentation implements TypeInstrumentation {
+
+ @Override
+ public ElementMatcher<TypeDescription> typeMatcher() {
+ return named("org.apache.cxf.jaxrs.impl.AbstractRequestContextImpl");
+ }
+
+ @Override
+ public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
+ return singletonMap(
+ isMethod()
+ .and(named("abortWith"))
+ .and(takesArguments(1))
+ .and(takesArgument(0, named("javax.ws.rs.core.Response"))),
+ CxfRequestContextInstrumentation.class.getName() + "$ContainerRequestContextAdvice");
+ }
+
+ public static class ContainerRequestContextAdvice {
+ @Advice.OnMethodEnter(suppress = Throwable.class)
+ public static void decorateAbortSpan(
+ @Advice.This AbstractRequestContextImpl context,
+ @Local("otelSpan") Span span,
+ @Local("otelScope") Scope scope) {
+
+ if (context.getProperty(JaxRsAnnotationsTracer.ABORT_HANDLED) == null
+ && context instanceof ContainerRequestContext) {
+ Message message = context.getMessage();
+ OperationResourceInfoStack resourceInfoStack =
+ (OperationResourceInfoStack)
+ message.get("org.apache.cxf.jaxrs.model.OperationResourceInfoStack");
+ if (resourceInfoStack == null || resourceInfoStack.isEmpty()) {
+ return;
+ }
+
+ MethodInvocationInfo invocationInfo = resourceInfoStack.peek();
+ Method method = invocationInfo.getMethodInfo().getMethodToInvoke();
+ Class<?> resourceClass = invocationInfo.getRealClass();
+
+ span =
+ RequestContextHelper.createOrUpdateAbortSpan(
+ (ContainerRequestContext) context, resourceClass, method);
+ if (span != null) {
+ scope = tracer().startScope(span);
+ }
+ }
+ }
+
+ @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
+ public static void stopSpan(
+ @Local("otelSpan") Span span,
+ @Local("otelScope") Scope scope,
+ @Advice.Thrown Throwable throwable) {
+ RequestContextHelper.closeSpanAndScope(span, scope, throwable);
+ }
+ }
+}

ADD
instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-cxf-3.2/javaagent/src/test/groovy/CxfAnnotationInstrumentationTest.groovy
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+class CxfAnnotationInstrumentationTest extends JaxRsAnnotationsInstrumentationTest {
+}

ADD
instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-cxf-3.2/javaagent/src/test/groovy/CxfFilterTest.groovy
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import static Resource.Test1
+import static Resource.Test2
+import static Resource.Test3
+
+import io.opentelemetry.instrumentation.test.base.HttpServerTestTrait
+import okhttp3.FormBody
+import okhttp3.HttpUrl
+import okhttp3.Request
+import okhttp3.RequestBody
+import okhttp3.Response
+import org.apache.cxf.endpoint.Server
+import org.apache.cxf.jaxrs.JAXRSServerFactoryBean
+
+class CxfFilterTest extends JaxRsFilterTest implements HttpServerTestTrait<Server> {
+
+ @Override
+ boolean testAbortPrematch() {
+ false
+ }
+
+ @Override
+ boolean runsOnServer() {
+ true
+ }
+
+ @Override
+ Server startServer(int port) {
+ JAXRSServerFactoryBean serverFactory = new JAXRSServerFactoryBean()
+ serverFactory.setProviders([simpleRequestFilter, prematchRequestFilter])
+ serverFactory.setResourceClasses([Test1, Test2, Test3])
+ serverFactory.setAddress(buildAddress().toString())
+
+ def server = serverFactory.create()
+ server.start()
+
+ return server
+ }
+
+ @Override
+ void stopServer(Server httpServer) {
+ httpServer.stop()
+ }
+
+ Request.Builder request(HttpUrl url, String method, RequestBody body) {
+ return new Request.Builder()
+ .url(url)
+ .method(method, body)
+ }
+
+ @Override
+ def makeRequest(String path) {
+ def url = HttpUrl.get(address.resolve(path)).newBuilder().build()
+ def request = request(url, "POST", new FormBody.Builder().build()).build()
+ Response response = client.newCall(request).execute()
+
+ return [response.body().string(), response.code()]
+ }
+}
\ No newline at end of file

ADD
instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-cxf-3.2/javaagent/src/test/groovy/CxfHttpServerTest.groovy
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import javax.ws.rs.core.MediaType
+import javax.ws.rs.core.Response
+import javax.ws.rs.ext.ExceptionMapper
+import org.apache.cxf.jaxrs.JAXRSServerFactoryBean
+import org.apache.cxf.endpoint.Server
+
+class CxfHttpServerTest extends JaxRsHttpServerTest<Server> {
+
+ @Override
+ Server startServer(int port) {
+ JAXRSServerFactoryBean serverFactory = new JAXRSServerFactoryBean()
+ def application = new JaxRsTestApplication()
+ serverFactory.setApplication(application)
+ serverFactory.setResourceClasses(new ArrayList<Class<?>>(application.getClasses()))
+ serverFactory.setProvider(new ExceptionMapper<Exception>() {
+ @Override
+ Response toResponse(Exception exception) {
+ return Response.status(500)
+ .type(MediaType.TEXT_PLAIN_TYPE)
+ .entity(exception.getMessage())
+ .build()
+ }
+ })
+ serverFactory.setAddress(buildAddress().toString())
+
+ def server = serverFactory.create()
+ server.start()
+
+ return server
+ }
+
+ @Override
+ void stopServer(Server httpServer) {
+ httpServer.stop()
+ }
+}
\ No newline at end of file

MODIFY
instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-testing/src/main/groovy/JaxRsFilterTest.groovy
instrumentation/jaxrs/jaxrs-2.0/jaxrs-2.0-testing/src/main/groovy/JaxRsFilterTest.groovy
@@ -13,6 +13,7 @@ import javax.ws.rs.container.PreMatching
import javax.ws.rs.core.MediaType
import javax.ws.rs.core.Response
import javax.ws.rs.ext.Provider
+import org.junit.Assume
import spock.lang.Shared
import spock.lang.Unroll
@@ -27,17 +28,35 @@ abstract class JaxRsFilterTest extends AgentInstrumentationSpecification {
abstract makeRequest(String url)
+ Tuple2<String, String> runRequest(String resource) {
+ if (runsOnServer()) {
+ return makeRequest(resource)
+ }
+ // start a trace because the test doesn't go through any servlet or other instrumentation.
+ return runUnderServerTrace("test.span") {
+ makeRequest(resource)
+ }
+ }
+
+ boolean testAbortPrematch() {
+ true
+ }
+
+ boolean runsOnServer() {
+ false
+ }
+
def "test #resource, #abortNormal, #abortPrematch"() {
+ Assume.assumeTrue(!abortPrematch || testAbortPrematch())
+
given:
simpleRequestFilter.abort = abortNormal
prematchRequestFilter.abort = abortPrematch
def abort = abortNormal || abortPrematch
when:
- // start a trace because the test doesn't go through any servlet or other instrumentation.
- def (responseText, responseStatus) = runUnderServerTrace("test.span") {
- makeRequest(resource)
- }
+
+ def (responseText, responseStatus) = runRequest(resource)
then:
responseText == expectedResponse
@@ -52,17 +71,23 @@ abstract class JaxRsFilterTest extends AgentInstrumentationSpecification {
trace(0, 2) {
span(0) {
name parentSpanName != null ? parentSpanName : "test.span"
+ if (runsOnServer()) {
+ errored abortNormal
+ } else {
attributes {
}
}
+ }
span(1) {
childOf span(0)
name controllerName
+ if (!runsOnServer()) {
attributes {
}
}
}
}
+ }
where:
resource | abortNormal | abortPrematch | parentSpanName | controllerName | expectedResponse
@@ -88,10 +113,7 @@ abstract class JaxRsFilterTest extends AgentInstrumentationSpecification {
prematchRequestFilter.abort = false
when:
- // start a trace because the test doesn't go through any servlet or other instrumentation.
- def (responseText, responseStatus) = runUnderServerTrace("test.span") {
- makeRequest(resource)
- }
+ def (responseText, responseStatus) = runRequest(resource)
then:
responseStatus == Response.Status.OK.statusCode
@@ -101,18 +123,22 @@ abstract class JaxRsFilterTest extends AgentInstrumentationSpecification {
trace(0, 2) {
span(0) {
name parentResourceName
+ if (!runsOnServer()) {
attributes {
}
}
+ }
span(1) {
childOf span(0)
name controller1Name
kind INTERNAL
+ if (!runsOnServer()) {
attributes {
}
}
}
}
+ }
where:
resource | parentResourceName | controller1Name | expectedResponse

MODIFY
settings.gradle
settings.gradle
@@ -110,12 +110,14 @@ include ':instrumentation:hystrix-1.4:javaagent'
include ':instrumentation:java-httpclient:javaagent'
include ':instrumentation:jaxrs:jaxrs-1.0:javaagent'
include ':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-common:javaagent'
+include ':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-cxf-3.2:javaagent'
include ':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-jersey-2.0:javaagent'
include ':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-resteasy-3.0:javaagent'
include ':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-resteasy-3.1:javaagent'
include ':instrumentation:jaxrs:jaxrs-2.0:jaxrs-2.0-testing'
include ':instrumentation:jaxrs-client:jaxrs-client-1.1:javaagent'
include ':instrumentation:jaxrs-client:jaxrs-client-2.0:jaxrs-client-2.0-common:javaagent'
+include ':instrumentation:jaxrs-client:jaxrs-client-2.0:jaxrs-client-2.0-cxf-3.0:javaagent'
include ':instrumentation:jaxrs-client:jaxrs-client-2.0:jaxrs-client-2.0-jersey-2.0:javaagent'
include ':instrumentation:jaxrs-client:jaxrs-client-2.0:jaxrs-client-2.0-resteasy-2.0:javaagent'
include ':instrumentation:jaxws:jaxws-2.0:javaagent'

JAX-RS with Apache CXF (#2192)