diff --git a/instrumentation/jetty-httpclient/jetty-httpclient-9.0/javaagent/jetty-httpclient-9.0-javaagent.gradle b/instrumentation/jetty-httpclient/jetty-httpclient-9.0/javaagent/jetty-httpclient-9.0-javaagent.gradle new file mode 100644 index 000000000000..ea74d6a6e1bd --- /dev/null +++ b/instrumentation/jetty-httpclient/jetty-httpclient-9.0/javaagent/jetty-httpclient-9.0-javaagent.gradle @@ -0,0 +1,27 @@ +apply from: "$rootDir/gradle/instrumentation.gradle" + +muzzle { + pass { + group = "org.eclipse.jetty" + module = 'jetty-client' + versions = "[9.1,10)" + assertInverse = true + } +} + +def jettyVers = [:] + +//Jetty client 9.1 is the best starting point for the v9.x series, v9.0.x is incompatible +jettyVers.base9 = '9.1.0.v20131115' +//jettyVers.base9 = '9.0.0.v20130308' +jettyVers.useInTests9 = '9.3.28.v20191105' + +dependencies { + implementation project(':instrumentation:jetty-httpclient:jetty-httpclient-9.0:library') + + library "org.eclipse.jetty:jetty-client:${jettyVers.base9}" + + testImplementation project(':instrumentation:jetty-httpclient:jetty-httpclient-9.0:testing') + testLibrary "org.eclipse.jetty:jetty-server:${jettyVers.useInTests9}" + +} \ No newline at end of file diff --git a/instrumentation/jetty-httpclient/jetty-httpclient-9.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/httpclient/v9_0/JettyHttpClient9InstrumentationModule.java b/instrumentation/jetty-httpclient/jetty-httpclient-9.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/httpclient/v9_0/JettyHttpClient9InstrumentationModule.java new file mode 100644 index 000000000000..1beb7db6f37e --- /dev/null +++ b/instrumentation/jetty-httpclient/jetty-httpclient-9.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/httpclient/v9_0/JettyHttpClient9InstrumentationModule.java @@ -0,0 +1,94 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.jetty.httpclient.v9_0; + +import static io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge.currentContext; +import static io.opentelemetry.javaagent.instrumentation.jetty.httpclient.v9_0.JettyHttpClient9Tracer.tracer; +import static java.util.Collections.singletonList; +import static java.util.Collections.singletonMap; +import static net.bytebuddy.matcher.ElementMatchers.hasSuperType; +import static net.bytebuddy.matcher.ElementMatchers.isInterface; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.named; + +import com.google.auto.service.AutoService; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +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.eclipse.jetty.client.api.Request; + +@AutoService(InstrumentationModule.class) +public class JettyHttpClient9InstrumentationModule extends InstrumentationModule { + + public JettyHttpClient9InstrumentationModule() { + super("jetty-httpclient", "jetty-httpclient-9"); + } + + @Override + public List typeInstrumentations() { + return singletonList(new JettyHttpClient9Instrumentation()); + } + + public static class JettyHttpClient9Instrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return hasSuperType(named("org.eclipse.jetty.client.api.Request").and(isInterface())); + } + + @Override + public Map, String> transformers() { + + return singletonMap( + isMethod().and(named("send")), + JettyHttpClient9InstrumentationModule.class.getName() + "$JettyHttpClient9Advice"); + } + } + + public static class JettyHttpClient9Advice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void addTracingEnter( + @Advice.Local("otelContext") Context context, + @Advice.Local("otelScope") Scope scope, + @Advice.This Request jettyRequest) { + + Context parentContext = currentContext(); + if (!tracer().shouldStartSpan(parentContext)) { + return; + } + + JettyHttpClient9TracingInterceptor interceptor = + new JettyHttpClient9TracingInterceptor(parentContext); + interceptor.attachToRequest(jettyRequest); + + scope = interceptor.getCtx().makeCurrent(); + context = interceptor.getCtx(); + } + + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + public static void exitTracingInterceptor( + @Advice.Thrown Throwable throwable, + @Advice.Local("otelContext") Context context, + @Advice.Local("otelScope") Scope scope) { + + if (throwable != null) { + tracer().endExceptionally(context, throwable); + } + + if (scope != null) { + scope.close(); + } + } + } +} diff --git a/instrumentation/jetty-httpclient/jetty-httpclient-9.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/jetty/httpclient/v9_0/JettyHttpClient9AgentTest.groovy b/instrumentation/jetty-httpclient/jetty-httpclient-9.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/jetty/httpclient/v9_0/JettyHttpClient9AgentTest.groovy new file mode 100644 index 000000000000..64d97d06f525 --- /dev/null +++ b/instrumentation/jetty-httpclient/jetty-httpclient-9.0/javaagent/src/test/groovy/io/opentelemetry/javaagent/instrumentation/jetty/httpclient/v9_0/JettyHttpClient9AgentTest.groovy @@ -0,0 +1,18 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.jetty.httpclient.v9_0 + +import io.opentelemetry.context.Context +import io.opentelemetry.instrumentation.test.AgentTestTrait +import org.eclipse.jetty.client.api.Request + +class JettyHttpClient9AgentTest extends AbstractJettyClient9Test implements AgentTestTrait { + + @Override + void attachInterceptor(Request jettyRequest, Context parentCtx) { + //Do nothing + } +} diff --git a/instrumentation/jetty-httpclient/jetty-httpclient-9.0/library/jetty-httpclient-9.0-library.gradle b/instrumentation/jetty-httpclient/jetty-httpclient-9.0/library/jetty-httpclient-9.0-library.gradle new file mode 100644 index 000000000000..e5357ed00ff4 --- /dev/null +++ b/instrumentation/jetty-httpclient/jetty-httpclient-9.0/library/jetty-httpclient-9.0-library.gradle @@ -0,0 +1,22 @@ +apply from: "$rootDir/gradle/instrumentation-library.gradle" +apply plugin: "net.ltgt.errorprone" + +def jettyVers = [:] + +//Jetty client 9.1 is the best starting point for the v9.x series, v9.0.x is incompatible +jettyVers.base9 = '9.1.0.v20131115' +//jettyVers.base9 = '9.0.0.v20130308' +jettyVers.useInTests9 = '9.3.28.v20191105' + +dependencies { + library "org.eclipse.jetty:jetty-client:${jettyVers.base9}" + testImplementation project(':instrumentation:jetty-httpclient::jetty-httpclient-9.0:testing') + + implementation deps.slf4j + + testLibrary "org.eclipse.jetty:jetty-server:${jettyVers.useInTests9}" + testLibrary "org.eclipse.jetty:jetty-client:${jettyVers.useInTests9}" + + +} + diff --git a/instrumentation/jetty-httpclient/jetty-httpclient-9.0/library/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/httpclient/v9_0/JettyHttpClient9Tracer.java b/instrumentation/jetty-httpclient/jetty-httpclient-9.0/library/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/httpclient/v9_0/JettyHttpClient9Tracer.java new file mode 100644 index 000000000000..99a9a3fb5693 --- /dev/null +++ b/instrumentation/jetty-httpclient/jetty-httpclient-9.0/library/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/httpclient/v9_0/JettyHttpClient9Tracer.java @@ -0,0 +1,87 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.jetty.httpclient.v9_0; + +import static io.opentelemetry.javaagent.instrumentation.jetty.httpclient.v9_0.RequestHeaderInjectorAdapter.SETTER; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.context.propagation.TextMapSetter; +import io.opentelemetry.instrumentation.api.tracer.HttpClientTracer; +import io.opentelemetry.instrumentation.api.tracer.net.NetPeerAttributes; +import java.net.URI; +import java.net.URISyntaxException; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.client.api.Response; +import org.eclipse.jetty.http.HttpFields; + +public class JettyHttpClient9Tracer extends HttpClientTracer { + private static final JettyHttpClient9Tracer TRACER = new JettyHttpClient9Tracer(); + + public static final JettyHttpClient9Tracer tracer() { + return TRACER; + } + + private JettyHttpClient9Tracer() { + super(NetPeerAttributes.INSTANCE); + } + + @Override + protected String getInstrumentationName() { + return "io.opentelemetry.javaagent.jetty-httpclient-9.0"; + } + + @Override + protected String method(Request request) { + return request.getMethod(); + } + + @Override + protected @Nullable URI url(Request request) throws URISyntaxException { + return request.getURI(); + } + + @Override + protected @Nullable Integer status(Response response) { + return response.getStatus(); + } + + @Override + protected @Nullable String requestHeader(Request request, String name) { + HttpFields headers = request.getHeaders(); + return extractHeader(headers, name); + } + + @Override + protected @Nullable String responseHeader(Response response, String name) { + HttpFields headers = response.getHeaders(); + return extractHeader(headers, name); + } + + /** Override so can be called from interceptor code */ + @Override + protected void onRequest(Span span, Request request) { + super.onRequest(span, request); + } + + protected void updateSpanName(Span span, Request request) { + span.updateName(super.spanNameForRequest(request)); + } + + @Override + protected TextMapSetter getSetter() { + return SETTER; + } + + private @Nullable String extractHeader(HttpFields headers, String name) { + + String headerVal = null; + if (headers != null) { + headerVal = headers.containsKey(name) ? headers.get(name) : null; + } + return headerVal; + } +} diff --git a/instrumentation/jetty-httpclient/jetty-httpclient-9.0/library/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/httpclient/v9_0/JettyHttpClient9TracingInterceptor.java b/instrumentation/jetty-httpclient/jetty-httpclient-9.0/library/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/httpclient/v9_0/JettyHttpClient9TracingInterceptor.java new file mode 100644 index 000000000000..baee66b32174 --- /dev/null +++ b/instrumentation/jetty-httpclient/jetty-httpclient-9.0/library/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/httpclient/v9_0/JettyHttpClient9TracingInterceptor.java @@ -0,0 +1,126 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.jetty.httpclient.v9_0; + +import static io.opentelemetry.javaagent.instrumentation.jetty.httpclient.v9_0.JettyHttpClient9Tracer.tracer; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import java.util.List; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.client.api.Response; +import org.eclipse.jetty.client.api.Result; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class JettyHttpClient9TracingInterceptor + implements Request.BeginListener, + Request.FailureListener, + Response.SuccessListener, + Response.FailureListener, + Response.CompleteListener, + Request.CommitListener { + + private static final Logger LOG = + LoggerFactory.getLogger(JettyHttpClient9TracingInterceptor.class); + + private @Nullable Context ctx; + + @Nullable + public Context getCtx() { + return ctx; + } + + private final Context parentContext; + + public JettyHttpClient9TracingInterceptor(Context parentCtx) { + this.parentContext = parentCtx; + } + + public void attachToRequest(Request jettyRequest) { + List current = + jettyRequest.getRequestListeners(JettyHttpClient9TracingInterceptor.class); + + if (!current.isEmpty()) { + LOG.warn("A tracing interceptor is already in place for this request! "); + return; + } + + jettyRequest + .onRequestBegin(this) + .onRequestFailure(this) + .onResponseFailure(this) + .onResponseSuccess(this) + .onRequestCommit(this); + + startSpan(jettyRequest); + } + + private void startSpan(Request request) { + + if (this.parentContext != null) { + if (!tracer().shouldStartSpan(this.parentContext)) { + return; + } + Context context = tracer().startSpan(parentContext, request, request); + this.ctx = context; + + } else { + LOG.warn("StartSpan - could not find an otel context"); + } + } + + @Override + public void onBegin(Request request) { + if (this.ctx != null) { + Span span = Span.fromContext(this.ctx); + tracer().onRequest(span, request); + tracer().updateSpanName(span, request); + + try (Scope scope = span.makeCurrent()) {} + } + } + + @Override + public void onFailure(Request request, Throwable t) { + if (this.ctx != null) { + tracer().endExceptionally(this.ctx, t); + } + } + + @Override + public void onComplete(Result result) { + closeIfPossible(result.getResponse()); + } + + @Override + public void onSuccess(Response response) { + closeIfPossible(response); + } + + @Override + public void onFailure(Response response, Throwable t) { + if (this.ctx != null) { + tracer().endExceptionally(this.ctx, t); + } + } + + private void closeIfPossible(Response response) { + + if (this.ctx != null) { + tracer().end(this.ctx, response); + } else { + LOG.warn("onComplete - could not find an otel context"); + } + } + + @Override + public void onCommit(Request request) { + // Doing nothing here yet; + } +} diff --git a/instrumentation/jetty-httpclient/jetty-httpclient-9.0/library/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/httpclient/v9_0/RequestHeaderInjectorAdapter.java b/instrumentation/jetty-httpclient/jetty-httpclient-9.0/library/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/httpclient/v9_0/RequestHeaderInjectorAdapter.java new file mode 100644 index 000000000000..9327ab667453 --- /dev/null +++ b/instrumentation/jetty-httpclient/jetty-httpclient-9.0/library/src/main/java/io/opentelemetry/javaagent/instrumentation/jetty/httpclient/v9_0/RequestHeaderInjectorAdapter.java @@ -0,0 +1,32 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.jetty.httpclient.v9_0; + +import io.opentelemetry.context.propagation.TextMapSetter; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.http.HttpField; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class RequestHeaderInjectorAdapter implements TextMapSetter { + + public static final RequestHeaderInjectorAdapter SETTER = new RequestHeaderInjectorAdapter(); + + private static final Logger LOG = LoggerFactory.getLogger(RequestHeaderInjectorAdapter.class); + + @Override + public void set(@Nullable Request request, String key, String value) { + if (request != null) { + // dedupe header fields here + HttpField removed = request.getHeaders().remove(key); + if (removed != null) { + LOG.warn("Attempt to add a dupe header field {}, {}", key, removed.getValue()); + } + request.header(key, value); + } + } +} diff --git a/instrumentation/jetty-httpclient/jetty-httpclient-9.0/library/src/test/groovy/io/opentelemetry/javaagent/instrumentation/jetty/httpclient/v9_0/JettyHttpClient9LibraryTest.groovy b/instrumentation/jetty-httpclient/jetty-httpclient-9.0/library/src/test/groovy/io/opentelemetry/javaagent/instrumentation/jetty/httpclient/v9_0/JettyHttpClient9LibraryTest.groovy new file mode 100644 index 000000000000..b3f1fbd24b09 --- /dev/null +++ b/instrumentation/jetty-httpclient/jetty-httpclient-9.0/library/src/test/groovy/io/opentelemetry/javaagent/instrumentation/jetty/httpclient/v9_0/JettyHttpClient9LibraryTest.groovy @@ -0,0 +1,33 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.jetty.httpclient.v9_0 + +import io.opentelemetry.context.Context +import io.opentelemetry.instrumentation.test.LibraryTestTrait +import org.eclipse.jetty.client.api.Request + +import static io.opentelemetry.javaagent.instrumentation.jetty.httpclient.v9_0.JettyHttpClient9Tracer.tracer + +class JettyHttpClient9LibraryTest extends AbstractJettyClient9Test implements LibraryTestTrait { + + + void attachInterceptor(Request jettyRequest, Context parentContext) { + + if (!tracer().shouldStartSpan(parentContext)) { + return; + } + + JettyHttpClient9TracingInterceptor interceptor = new JettyHttpClient9TracingInterceptor(parentContext); + interceptor.attachToRequest(jettyRequest); + } + + @Override + boolean testWithClientParent() { + //As mentioned in other instrumentation, i.e. OKhttp-3.0, this does not work well in library tests + false + } + +} diff --git a/instrumentation/jetty-httpclient/jetty-httpclient-9.0/testing/jetty-httpclient-9.0-testing.gradle b/instrumentation/jetty-httpclient/jetty-httpclient-9.0/testing/jetty-httpclient-9.0-testing.gradle new file mode 100644 index 000000000000..dc30b9a81d75 --- /dev/null +++ b/instrumentation/jetty-httpclient/jetty-httpclient-9.0/testing/jetty-httpclient-9.0-testing.gradle @@ -0,0 +1,18 @@ +apply from: "$rootDir/gradle/java.gradle" + +dependencies { + api(project(':testing-common')) { +// exclude group: 'org.eclipse.jetty', module: 'jetty-client' +// exclude group: 'org.eclipse.jetty', module: 'jetty-server' + } + + + api "org.eclipse.jetty:jetty-client:9.3.28.v20191105" + + implementation deps.guava + implementation deps.junitApi + + implementation deps.groovy + implementation deps.opentelemetryApi + implementation deps.spock +} diff --git a/instrumentation/jetty-httpclient/jetty-httpclient-9.0/testing/src/main/groovy/io/opentelemetry/javaagent/instrumentation/jetty/httpclient/v9_0/AbstractJettyClient9Test.groovy b/instrumentation/jetty-httpclient/jetty-httpclient-9.0/testing/src/main/groovy/io/opentelemetry/javaagent/instrumentation/jetty/httpclient/v9_0/AbstractJettyClient9Test.groovy new file mode 100644 index 000000000000..158e1736d863 --- /dev/null +++ b/instrumentation/jetty-httpclient/jetty-httpclient-9.0/testing/src/main/groovy/io/opentelemetry/javaagent/instrumentation/jetty/httpclient/v9_0/AbstractJettyClient9Test.groovy @@ -0,0 +1,145 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.jetty.httpclient.v9_0 + +import io.opentelemetry.context.Context +import io.opentelemetry.context.Scope +import io.opentelemetry.instrumentation.test.base.HttpClientTest +import org.eclipse.jetty.client.HttpClient +import org.eclipse.jetty.client.api.ContentResponse +import org.eclipse.jetty.client.api.Request +import org.eclipse.jetty.client.api.Response +import org.eclipse.jetty.client.api.Result +import org.eclipse.jetty.http.HttpMethod +import org.eclipse.jetty.util.ssl.SslContextFactory +import org.junit.jupiter.api.Assertions +import spock.lang.Shared + +import java.util.concurrent.TimeUnit + +abstract class AbstractJettyClient9Test extends HttpClientTest { + + + abstract void attachInterceptor(Request jettyRequest, Context parentContext); + + + @Shared + def client = new HttpClient() + @Shared + def httpsClient = null + + def setupSpec() { + + try { + client.start() + } catch (Throwable t) { + Assertions.fail("Error during jetty client start", t) + } + + SslContextFactory tlsCtx = new SslContextFactory(); + tlsCtx.setExcludeProtocols("TLSv1.3"); + httpsClient = new HttpClient(tlsCtx) + httpsClient.setFollowRedirects(false) + try { + httpsClient.start() + } catch (Throwable t) { + Assertions.fail("Error during jetty client https start", t) + } + + } + + @Override + Request buildRequest(String method, URI uri, Map headers) { + + HttpClient theClient = uri.scheme == 'https' ? httpsClient : client + + Request request = theClient.newRequest(uri) + + HttpMethod methodObj = HttpMethod.valueOf(method) + request.method(methodObj) + request.timeout(CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS) + + return request + } + + @Override + String userAgent() { + return "Jetty" + } + + @Override + int sendRequest(Request request, String method, URI uri, Map headers) { + headers.each { k, v -> + request.header(k, v) + } + +// def interceptor = createInterceptor(Context.current()) + Context parentContext = Context.current() + Scope scope = parentContext.makeCurrent() + attachInterceptor(request, parentContext) + + ContentResponse response = request.send() + scope.close() + return response.status + } + + private static class JettyClientListener implements Request.FailureListener, Response.FailureListener { + + Throwable failure + + @Override + void onFailure(Request requestF, Throwable failure) { + this.failure = failure + + } + + @Override + void onFailure(Response responseF, Throwable failure) { + this.failure = failure + } + + } + + @Override + void sendRequestWithCallback(Request request, String method, URI uri, Map headers, RequestResult requestResult) { + + JettyClientListener jcl = new JettyClientListener() + + request.onRequestFailure(jcl) + request.onResponseFailure(jcl) + headers.each { k, v -> + request.header(k, v) + } + Context parentContext = Context.current() + Scope scope = parentContext.makeCurrent() + attachInterceptor(request, parentContext) + + request.send(new Response.CompleteListener() { + @Override + void onComplete(Result result) { + if (jcl.failure != null) { + requestResult.complete(jcl.failure) + return; + } + + requestResult.complete(result.response.status) + } + }) + scope.close() + } + + + @Override + boolean testRedirects() { + false + } + + @Override + boolean testCausality() { + true + } + +} diff --git a/settings.gradle b/settings.gradle index 7d8c7b335db6..234877b73240 100644 --- a/settings.gradle +++ b/settings.gradle @@ -176,6 +176,9 @@ include ':instrumentation:jedis:jedis-3.0:javaagent' include ':instrumentation:jetty:jetty-8.0:javaagent' include ':instrumentation:jetty:jetty-11.0:javaagent' include ':instrumentation:jetty:jetty-common:javaagent' +include ':instrumentation:jetty-httpclient:jetty-httpclient-9.0:javaagent' +include ':instrumentation:jetty-httpclient:jetty-httpclient-9.0:library' +include ':instrumentation:jetty-httpclient:jetty-httpclient-9.0:testing' include ':instrumentation:jms-1.1:javaagent' include ':instrumentation:jms-1.1:javaagent-unit-tests' include ':instrumentation:jsf:jsf-common:library'