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 super TypeDescription> typeMatcher() {
+ return named("com.google.gwt.user.server.rpc.RPC");
+ }
+
+ @Override
+ public Map extends ElementMatcher super MethodDescription>, 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