From 3c11613aed570ddc3e9afc3c461830ed499e3405 Mon Sep 17 00:00:00 2001 From: Lauri Tulmin Date: Mon, 29 Mar 2021 19:41:03 +0300 Subject: [PATCH] Add support for GWT (#2652) * Add support for GWT * formatting * review fixes --- docs/supported-libraries.md | 1 + .../javaagent/gwt-2.0-javaagent.gradle | 93 ++++++++++ .../gwt/GwtInstrumentationModule.java | 114 +++++++++++++ .../instrumentation/gwt/GwtTracer.java | 56 ++++++ .../javaagent/src/test/groovy/GwtTest.groovy | 161 ++++++++++++++++++ .../testapp/java/test/gwt/Greeting.gwt.xml | 8 + .../test/gwt/client/GreetingEntryPoint.java | 70 ++++++++ .../test/gwt/server/MessageServiceImpl.java | 22 +++ .../java/test/gwt/shared/MessageService.java | 15 ++ .../test/gwt/shared/MessageServiceAsync.java | 13 ++ .../src/testapp/webapp/WEB-INF/web.xml | 20 +++ .../src/testapp/webapp/greeting.html | 13 ++ settings.gradle | 1 + 13 files changed, 587 insertions(+) create mode 100644 instrumentation/gwt-2.0/javaagent/gwt-2.0-javaagent.gradle create mode 100644 instrumentation/gwt-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/gwt/GwtInstrumentationModule.java create mode 100644 instrumentation/gwt-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/gwt/GwtTracer.java create mode 100644 instrumentation/gwt-2.0/javaagent/src/test/groovy/GwtTest.groovy create mode 100644 instrumentation/gwt-2.0/javaagent/src/testapp/java/test/gwt/Greeting.gwt.xml create mode 100644 instrumentation/gwt-2.0/javaagent/src/testapp/java/test/gwt/client/GreetingEntryPoint.java create mode 100644 instrumentation/gwt-2.0/javaagent/src/testapp/java/test/gwt/server/MessageServiceImpl.java create mode 100644 instrumentation/gwt-2.0/javaagent/src/testapp/java/test/gwt/shared/MessageService.java create mode 100644 instrumentation/gwt-2.0/javaagent/src/testapp/java/test/gwt/shared/MessageServiceAsync.java create mode 100644 instrumentation/gwt-2.0/javaagent/src/testapp/webapp/WEB-INF/web.xml create mode 100644 instrumentation/gwt-2.0/javaagent/src/testapp/webapp/greeting.html diff --git a/docs/supported-libraries.md b/docs/supported-libraries.md index b60cc7a42cb8..e28d23ed0b0c 100644 --- a/docs/supported-libraries.md +++ b/docs/supported-libraries.md @@ -41,6 +41,7 @@ These are the supported libraries and frameworks: | [Google HTTP Client](https://github.com/googleapis/google-http-java-client) | 1.19+ | | [Grizzly](https://javaee.github.io/grizzly/httpserverframework.html) | 2.0+ (disabled by default) | | [gRPC](https://github.com/grpc/grpc-java) | 1.5+ | +| [GWT](http://www.gwtproject.org/) | 2.0+ | | [Hibernate](https://github.com/hibernate/hibernate-orm) | 3.3+ | | [HttpURLConnection](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/net/HttpURLConnection.html) | Java 7+ | | [http4k ](https://www.http4k.org/guide/modules/opentelemetry/) | 3.270.0+ | diff --git a/instrumentation/gwt-2.0/javaagent/gwt-2.0-javaagent.gradle b/instrumentation/gwt-2.0/javaagent/gwt-2.0-javaagent.gradle new file mode 100644 index 000000000000..e3da10fe66c9 --- /dev/null +++ b/instrumentation/gwt-2.0/javaagent/gwt-2.0-javaagent.gradle @@ -0,0 +1,93 @@ +apply from: "$rootDir/gradle/instrumentation.gradle" + +muzzle { + pass { + group = "com.google.gwt" + module = "gwt-servlet" + versions = "[2.0.0,)" + assertInverse = true + } +} + +sourceSets { + testapp { + java + resources { + srcDirs("src/webapp") + } + java.outputDir = file("$buildDir/testapp/classes") + compileClasspath += sourceSets.main.compileClasspath + } +} + +dependencies { + // these are needed for compileGwt task + if (findProperty('testLatestDeps')) { + compileOnly 'com.google.gwt:gwt-user:+' + compileOnly 'com.google.gwt:gwt-dev:+' + } else { + compileOnly 'com.google.gwt:gwt-user:2.0.0' + compileOnly 'com.google.gwt:gwt-dev:2.0.0' + } + + library 'com.google.gwt:gwt-servlet:2.0.0' + + testInstrumentation project(':instrumentation:servlet:servlet-3.0:javaagent') + testInstrumentation project(':instrumentation:servlet:servlet-javax-common:javaagent') + testInstrumentation project(':instrumentation:jetty-8.0:javaagent') + + testImplementation "org.testcontainers:selenium:${versions.testcontainers}" + testImplementation 'org.seleniumhq.selenium:selenium-java:3.141.59' + + testImplementation(project(':testing-common')) { + exclude group: 'org.eclipse.jetty', module: 'jetty-server' + } + testImplementation group: 'org.eclipse.jetty', name: 'jetty-webapp', version: '9.4.35.v20201120' +} + +task copyTestWebapp(type: Copy) { + from file("src/testapp/webapp") + into file("$buildDir/testapp/war") +} + +task compileGwt(dependsOn: classes, type: JavaExec) { + // versions before 2.9 require java8 + javaLauncher = javaToolchains.launcherFor { + languageVersion = JavaLanguageVersion.of(8) + } + + def extraDir = "$buildDir/testapp/extra" + def warDir = "$buildDir/testapp/war" + + outputs.dir warDir + + doFirst { + file(warDir).mkdirs() + } + + main = 'com.google.gwt.dev.Compiler' + + classpath { + [ + sourceSets.testapp.java.srcDirs, + sourceSets.testapp.compileClasspath + ] + } + + args = [ + 'test.gwt.Greeting', // gwt module + '-war', warDir, + '-logLevel', 'INFO', + '-localWorkers', '2', + '-compileReport', + '-extra', extraDir, + '-draftCompile' // makes compile a bit faster + ] +} + +test.dependsOn sourceSets.testapp.output, compileGwt, copyTestWebapp + +test { + // add test app classes to classpath + classpath = project.sourceSets.test.runtimeClasspath + files("$buildDir/testapp/classes") +} diff --git a/instrumentation/gwt-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/gwt/GwtInstrumentationModule.java b/instrumentation/gwt-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/gwt/GwtInstrumentationModule.java new file mode 100644 index 000000000000..74b02fd21b40 --- /dev/null +++ b/instrumentation/gwt-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/gwt/GwtInstrumentationModule.java @@ -0,0 +1,114 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.gwt; + +import static io.opentelemetry.javaagent.instrumentation.gwt.GwtTracer.tracer; +import static io.opentelemetry.javaagent.tooling.bytebuddy.matcher.ClassLoaderMatcher.hasClassesNamed; +import static java.util.Collections.singletonList; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +import com.google.auto.service.AutoService; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.javaagent.tooling.InstrumentationModule; +import io.opentelemetry.javaagent.tooling.TypeInstrumentation; +import java.lang.reflect.Method; +import java.util.HashMap; +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; + +@AutoService(InstrumentationModule.class) +public class GwtInstrumentationModule extends InstrumentationModule { + + public GwtInstrumentationModule() { + super("gwt", "gwt-2.0"); + } + + @Override + public ElementMatcher.Junction classLoaderMatcher() { + // class added in gwt 2.0 + return hasClassesNamed("com.google.gwt.uibinder.client.UiBinder"); + } + + @Override + public List typeInstrumentations() { + return singletonList(new RpcInstrumentation()); + } + + public static class RpcInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return named("com.google.gwt.user.server.rpc.RPC"); + } + + @Override + public Map, String> transformers() { + Map, String> transformers = new HashMap<>(); + + transformers.put( + named("invokeAndEncodeResponse") + .and(takesArguments(5)) + .and(takesArgument(0, Object.class)) + .and(takesArgument(1, Method.class)) + .and(takesArgument(2, Object[].class)) + .and(takesArgument(3, named("com.google.gwt.user.server.rpc.SerializationPolicy"))) + .and(takesArgument(4, int.class)), + RpcInstrumentation.class.getName() + "$RpcInvokeAdvice"); + + // encodeResponseForFailure is called by invokeAndEncodeResponse in case of failure + transformers.put( + named("encodeResponseForFailure") + .and(takesArguments(4)) + .and(takesArgument(0, Method.class)) + .and(takesArgument(1, Throwable.class)) + .and(takesArgument(2, named("com.google.gwt.user.server.rpc.SerializationPolicy"))) + .and(takesArgument(3, int.class)), + RpcInstrumentation.class.getName() + "$RpcFailureAdvice"); + + return transformers; + } + + public static class RpcInvokeAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void onEnter( + @Advice.Argument(0) Object target, + @Advice.Argument(1) Method method, + @Advice.Local("otelContext") Context context, + @Advice.Local("otelScope") Scope scope) { + + context = tracer().startRpcSpan(target, method); + scope = context.makeCurrent(); + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void onExit( + @Advice.Thrown Throwable throwable, + @Advice.Local("otelContext") Context context, + @Advice.Local("otelScope") Scope scope) { + scope.close(); + + tracer().endSpan(context, throwable); + } + } + + public static class RpcFailureAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void onEnter(@Advice.Argument(1) Throwable throwable) { + if (throwable == null) { + return; + } + tracer().rpcFailure(throwable); + } + } + } +} diff --git a/instrumentation/gwt-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/gwt/GwtTracer.java b/instrumentation/gwt-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/gwt/GwtTracer.java new file mode 100644 index 000000000000..3ba4b56d745f --- /dev/null +++ b/instrumentation/gwt-2.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/gwt/GwtTracer.java @@ -0,0 +1,56 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.gwt; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.ContextKey; +import io.opentelemetry.instrumentation.api.tracer.BaseTracer; +import java.lang.reflect.Method; + +public class GwtTracer extends BaseTracer { + private static final ContextKey RPC_CONTEXT_KEY = + ContextKey.named("opentelemetry-gwt-rpc-context-key"); + + private static final GwtTracer TRACER = new GwtTracer(); + + public static GwtTracer tracer() { + return TRACER; + } + + private GwtTracer() { + super(GlobalOpenTelemetry.get()); + } + + public Context startRpcSpan(Object target, Method method) { + String spanName = spanNameForMethod(target.getClass(), method); + Context context = super.startSpan(spanName); + return context.with(RPC_CONTEXT_KEY, Boolean.TRUE); + } + + public void endSpan(Context context, Throwable throwable) { + if (throwable != null) { + endExceptionally(context, throwable); + } else { + end(context); + } + } + + public void rpcFailure(Throwable throwable) { + Context context = Context.current(); + if (context.get(RPC_CONTEXT_KEY) == null) { + // not inside rpc invocation + return; + } + + tracer().onException(context, throwable); + } + + @Override + protected String getInstrumentationName() { + return "io.opentelemetry.javaagent.gwt-2.0"; + } +} diff --git a/instrumentation/gwt-2.0/javaagent/src/test/groovy/GwtTest.groovy b/instrumentation/gwt-2.0/javaagent/src/test/groovy/GwtTest.groovy new file mode 100644 index 000000000000..07f31bdbc4a8 --- /dev/null +++ b/instrumentation/gwt-2.0/javaagent/src/test/groovy/GwtTest.groovy @@ -0,0 +1,161 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +import static io.opentelemetry.instrumentation.test.utils.TraceUtils.basicSpan + +import io.opentelemetry.api.trace.SpanKind +import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification +import io.opentelemetry.instrumentation.test.asserts.TraceAssert +import io.opentelemetry.instrumentation.test.base.HttpServerTestTrait +import java.util.concurrent.TimeUnit +import okhttp3.HttpUrl +import okhttp3.Request +import okhttp3.RequestBody +import org.eclipse.jetty.server.Server +import org.eclipse.jetty.util.resource.Resource +import org.eclipse.jetty.webapp.WebAppContext +import org.openqa.selenium.chrome.ChromeOptions +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import org.testcontainers.Testcontainers +import org.testcontainers.containers.BrowserWebDriverContainer +import org.testcontainers.containers.output.Slf4jLogConsumer +import spock.lang.Shared + +class GwtTest extends AgentInstrumentationSpecification implements HttpServerTestTrait { + private static final Logger logger = LoggerFactory.getLogger(GwtTest) + + @Shared + BrowserWebDriverContainer chrome + + @Override + Server startServer(int port) { + WebAppContext webAppContext = new WebAppContext() + webAppContext.setContextPath(getContextPath()) + webAppContext.setBaseResource(Resource.newResource(new File("build/testapp/war"))) + + def jettyServer = new Server(port) + jettyServer.connectors.each { + it.setHost('localhost') + } + + jettyServer.setHandler(webAppContext) + jettyServer.start() + + return jettyServer + } + + @Override + void stopServer(Server server) { + server.stop() + server.destroy() + } + + def setupSpec() { + Testcontainers.exposeHostPorts(port) + + chrome = new BrowserWebDriverContainer<>() + .withCapabilities(new ChromeOptions()) + .withLogConsumer(new Slf4jLogConsumer(logger)) + chrome.start() + + address = new URI("http://host.testcontainers.internal:$port" + getContextPath() + "/") + } + + def cleanupSpec() { + chrome?.stop() + } + + @Override + String getContextPath() { + return "/xyz" + } + + def getDriver() { + def driver = chrome.getWebDriver() + driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS) + return driver + } + + def "test gwt"() { + setup: + def driver = getDriver() + + // fetch the test page + driver.get(address.resolve("greeting.html").toString()) + + expect: + // wait for page to load + driver.findElementByClassName("greeting.button") + assertTraces(4) { + // /xyz/greeting.html + trace(0, 1) { + serverSpan(it, 0, getContextPath() + "/*") + } + // /xyz/greeting/greeting.nocache.js + trace(1, 1) { + serverSpan(it, 0, getContextPath() + "/*") + } + // /xyz/greeting/1B105441581A8F41E49D5DF3FB5B55BA.cache.html + trace(2, 1) { + serverSpan(it, 0, getContextPath() + "/*") + } + // /favicon.ico + trace(3, 1) { + serverSpan(it, 0, "HTTP GET") + } + } + clearExportedData() + + when: + // click a button to trigger calling java code + driver.findElementByClassName("greeting.button").click() + + then: + // wait for response + "Hello, Otel" == driver.findElementByClassName("message.received").getText() + assertTraces(1) { + trace(0, 2) { + serverSpan(it, 0, getContextPath() + "/greeting/greet") + basicSpan(it, 1, "MessageServiceImpl.sendMessage", span(0)) + } + } + clearExportedData() + + when: + // click a button to trigger calling java code + driver.findElementByClassName("error.button").click() + + then: + // wait for response + "Error" == driver.findElementByClassName("error.received").getText() + assertTraces(1) { + trace(0, 2) { + serverSpan(it, 0, getContextPath() + "/greeting/greet") + basicSpan(it, 1, "MessageServiceImpl.sendMessage", span(0), new IllegalArgumentException()) + } + } + + cleanup: + driver.close() + } + + static serverSpan(TraceAssert trace, int index, String spanName) { + trace.span(index) { + hasNoParent() + + name spanName + kind SpanKind.SERVER + } + } + + Request.Builder request(HttpUrl url, String method, RequestBody body) { + return new Request.Builder() + .url(url) + .method(method, body) + .header("User-Agent", TEST_USER_AGENT) + .header("X-Forwarded-For", TEST_CLIENT_IP) + } +} diff --git a/instrumentation/gwt-2.0/javaagent/src/testapp/java/test/gwt/Greeting.gwt.xml b/instrumentation/gwt-2.0/javaagent/src/testapp/java/test/gwt/Greeting.gwt.xml new file mode 100644 index 000000000000..13cec2829b2f --- /dev/null +++ b/instrumentation/gwt-2.0/javaagent/src/testapp/java/test/gwt/Greeting.gwt.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/instrumentation/gwt-2.0/javaagent/src/testapp/java/test/gwt/client/GreetingEntryPoint.java b/instrumentation/gwt-2.0/javaagent/src/testapp/java/test/gwt/client/GreetingEntryPoint.java new file mode 100644 index 000000000000..77bfad526da0 --- /dev/null +++ b/instrumentation/gwt-2.0/javaagent/src/testapp/java/test/gwt/client/GreetingEntryPoint.java @@ -0,0 +1,70 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package test.gwt.client; + +import com.google.gwt.core.client.EntryPoint; +import com.google.gwt.core.client.GWT; +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.RootPanel; +import test.gwt.shared.MessageService; +import test.gwt.shared.MessageServiceAsync; + +public class GreetingEntryPoint implements EntryPoint { + private final MessageServiceAsync messageServiceAsync = GWT.create(MessageService.class); + + public void onModuleLoad() { + Button greetingButton = new Button("Greeting"); + greetingButton.addStyleName("greeting.button"); + + Button errorButton = new Button("Error"); + errorButton.addStyleName("error.button"); + + RootPanel.get("buttonContainer").add(greetingButton); + RootPanel.get("buttonContainer").add(errorButton); + + final Label messageLabel = new Label(); + RootPanel.get("messageContainer").add(messageLabel); + + class MyHandler implements ClickHandler { + private final String message; + + MyHandler(String message) { + this.message = message; + } + + @Override + public void onClick(ClickEvent event) { + sendMessageToServer(); + } + + private void sendMessageToServer() { + messageLabel.setText(""); + messageLabel.setStyleName(""); + + messageServiceAsync.sendMessage( + message, + new AsyncCallback() { + public void onFailure(Throwable caught) { + messageLabel.setText("Error"); + messageLabel.addStyleName("error.received"); + } + + public void onSuccess(String result) { + messageLabel.setText(result); + messageLabel.addStyleName("message.received"); + } + }); + } + } + + greetingButton.addClickHandler(new MyHandler("Otel")); + errorButton.addClickHandler(new MyHandler("Error")); + } +} diff --git a/instrumentation/gwt-2.0/javaagent/src/testapp/java/test/gwt/server/MessageServiceImpl.java b/instrumentation/gwt-2.0/javaagent/src/testapp/java/test/gwt/server/MessageServiceImpl.java new file mode 100644 index 000000000000..9644416fa0df --- /dev/null +++ b/instrumentation/gwt-2.0/javaagent/src/testapp/java/test/gwt/server/MessageServiceImpl.java @@ -0,0 +1,22 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package test.gwt.server; + +import com.google.gwt.user.server.rpc.RemoteServiceServlet; +import test.gwt.shared.MessageService; + +/** The server-side implementation of the RPC service. */ +@SuppressWarnings("serial") +public class MessageServiceImpl extends RemoteServiceServlet implements MessageService { + + public String sendMessage(String message) throws IllegalArgumentException { + if (message == null || "Error".equals(message)) { + throw new IllegalArgumentException(); + } + + return "Hello, " + message; + } +} diff --git a/instrumentation/gwt-2.0/javaagent/src/testapp/java/test/gwt/shared/MessageService.java b/instrumentation/gwt-2.0/javaagent/src/testapp/java/test/gwt/shared/MessageService.java new file mode 100644 index 000000000000..3e1fb1fdeaf1 --- /dev/null +++ b/instrumentation/gwt-2.0/javaagent/src/testapp/java/test/gwt/shared/MessageService.java @@ -0,0 +1,15 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package test.gwt.shared; + +import com.google.gwt.user.client.rpc.RemoteService; +import com.google.gwt.user.client.rpc.RemoteServiceRelativePath; + +/** The client-side stub for the RPC service. */ +@RemoteServiceRelativePath("greet") +public interface MessageService extends RemoteService { + String sendMessage(String message) throws IllegalArgumentException; +} diff --git a/instrumentation/gwt-2.0/javaagent/src/testapp/java/test/gwt/shared/MessageServiceAsync.java b/instrumentation/gwt-2.0/javaagent/src/testapp/java/test/gwt/shared/MessageServiceAsync.java new file mode 100644 index 000000000000..7a8603db0d5c --- /dev/null +++ b/instrumentation/gwt-2.0/javaagent/src/testapp/java/test/gwt/shared/MessageServiceAsync.java @@ -0,0 +1,13 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package test.gwt.shared; + +import com.google.gwt.user.client.rpc.AsyncCallback; + +/** The async counterpart of MessageService. */ +public interface MessageServiceAsync { + void sendMessage(String input, AsyncCallback callback) throws IllegalArgumentException; +} diff --git a/instrumentation/gwt-2.0/javaagent/src/testapp/webapp/WEB-INF/web.xml b/instrumentation/gwt-2.0/javaagent/src/testapp/webapp/WEB-INF/web.xml new file mode 100644 index 000000000000..215442e2bb68 --- /dev/null +++ b/instrumentation/gwt-2.0/javaagent/src/testapp/webapp/WEB-INF/web.xml @@ -0,0 +1,20 @@ + + + + + greetServlet + test.gwt.server.MessageServiceImpl + + + + greetServlet + /greeting/greet + + + + greeting.html + + diff --git a/instrumentation/gwt-2.0/javaagent/src/testapp/webapp/greeting.html b/instrumentation/gwt-2.0/javaagent/src/testapp/webapp/greeting.html new file mode 100644 index 000000000000..acd563c8cafd --- /dev/null +++ b/instrumentation/gwt-2.0/javaagent/src/testapp/webapp/greeting.html @@ -0,0 +1,13 @@ + + + + +Example + + + + +

+

+ + \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 91847d0656cd..9d8026a026f4 100644 --- a/settings.gradle +++ b/settings.gradle @@ -117,6 +117,7 @@ include ':instrumentation:grpc-1.5:javaagent' include ':instrumentation:grpc-1.5:library' include ':instrumentation:grpc-1.5:testing' include ':instrumentation:guava-10.0:javaagent' +include ':instrumentation:gwt-2.0:javaagent' include ':instrumentation:hibernate:hibernate-3.3:javaagent' include ':instrumentation:hibernate:hibernate-4.0:javaagent' include ':instrumentation:hibernate:hibernate-4.3:javaagent'