Initial Map-backed implementation and test
MODIFY
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/HelperInjector.java
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/HelperInjector.java
@@ -39,6 +39,17 @@ public class HelperInjector implements Transformer {
this.helperClassNames = new LinkedHashSet<>(Arrays.asList(helperClassNames));
}
+ public HelperInjector(final Map<String, byte[]> helperMap) {
+ this.helperClassNames = helperMap.keySet();
+ this.helperMap = new LinkedHashMap<>(helperClassNames.size());
+ for (final String helperName : helperClassNames) {
+ final TypeDescription typeDesc =
+ new TypeDescription.Latent(
+ helperName, 0, null, Collections.<TypeDescription.Generic>emptyList());
+ this.helperMap.put(typeDesc, helperMap.get(helperName));
+ }
+ }
+
private synchronized Map<TypeDescription, byte[]> getHelperMap() throws IOException {
if (helperMap == null) {
helperMap = new LinkedHashMap<>(helperClassNames.size());

MODIFY
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/Instrumenter.java
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/Instrumenter.java
@@ -18,9 +18,6 @@ import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
-import net.bytebuddy.implementation.auxiliary.MethodCallProxy;
-import net.bytebuddy.implementation.bytecode.StackManipulation;
-import net.bytebuddy.implementation.bytecode.assign.Assigner;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.utility.JavaModule;
@@ -54,8 +51,7 @@ public interface Instrumenter {
@Slf4j
abstract class Default implements Instrumenter {
private final Set<String> instrumentationNames;
- // FIXME: make field private again
- public final String instrumentationPrimaryName;
+ private final String instrumentationPrimaryName;
// TODO: create a generic instrumentaton pipeline attacher?
private final InstrumentationContextProvider contextProvider;
protected final boolean enabled;
@@ -105,6 +101,21 @@ public interface Instrumenter {
.and(new MuzzleMatcher())
.transform(DDTransformers.defaultTransformers());
agentBuilder = injectHelperClasses(agentBuilder);
+ if (contextStore().size() > 0) {
+ agentBuilder =
+ agentBuilder.transform(
+ new AgentBuilder.Transformer() {
+ @Override
+ public DynamicType.Builder<?> transform(
+ DynamicType.Builder<?> builder,
+ TypeDescription typeDescription,
+ ClassLoader classLoader,
+ JavaModule module) {
+ return builder.visit(contextProvider.getInstrumentationVisitor());
+ }
+ });
+ agentBuilder = agentBuilder.transform(new HelperInjector(contextProvider.dynamicClasses()));
+ }
agentBuilder = applyInstrumentationTransformers(agentBuilder);
return agentBuilder;
}
@@ -126,14 +137,7 @@ public interface Instrumenter {
new AgentBuilder.Transformer.ForAdvice()
.include(Utils.getAgentClassLoader())
.withExceptionHandler(ExceptionHandlers.defaultExceptionHandler())
- .advice(entry.getKey(), entry.getValue()))
- .transform(new AgentBuilder.Transformer() {
- @Override
- public DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) {
- return builder.visit(contextProvider.getInstrumentationVisitor());
- }
- })
- ;
+ .advice(entry.getKey(), entry.getValue()));
}
return agentBuilder;
}
@@ -205,7 +209,8 @@ public interface Instrumenter {
public abstract Map<? extends ElementMatcher, String> transformers();
/**
- * A map of {class-name -> context-class-name}. Keys (and their subclasses) will be associated with a context of the value.
+ * A map of {class-name -> context-class-name}. Keys (and their subclasses) will be associated
+ * with a context of the value.
*/
public Map<String, String> contextStore() {
return Collections.EMPTY_MAP;

MODIFY
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/context/InstrumentationContext.java
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/context/InstrumentationContext.java
package datadog.trace.agent.tooling.context;
-/**
- * Instrumentation Context API
- */
+/** Instrumentation Context API */
public class InstrumentationContext {
private InstrumentationContext() {}
- public static <T> T get(Object contextInstance, Class<T> contextClass) {
+ public static <K, V> V get(Object userInstance, Class<K> userClass, Class<V> contextClass) {
throw new RuntimeException("calls to this method should be rewritten");
}
}

MODIFY
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/context/InstrumentationContextProvider.java
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/context/InstrumentationContextProvider.java
package datadog.trace.agent.tooling.context;
import datadog.trace.agent.tooling.Instrumenter;
+import java.util.Map;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.AsmVisitorWrapper;
-import java.util.List;
-
public interface InstrumentationContextProvider {
- /**
- * @return An AsmVisitorWrapper to run on the instrumentation's bytecode.
- */
+ /** @return An AsmVisitorWrapper to run on the instrumentation's bytecode. */
AsmVisitorWrapper getInstrumentationVisitor();
/**
- * @return A list of classes in byte-array format. These classes will be injected into the runtime classloader.
+ * @return A list of classes in byte-array format. These classes will be injected into the runtime
+ * classloader.
*/
- List<byte[]> dynamicClasses();
+ Map<String, byte[]> dynamicClasses();
AgentBuilder additionalInstrumentation(AgentBuilder builder);
@@ -24,7 +22,8 @@ public interface InstrumentationContextProvider {
class Creator {
private Creator() {}
- public static InstrumentationContextProvider contextProviderFor(Instrumenter.Default instrumenter) {
+ public static InstrumentationContextProvider contextProviderFor(
+ Instrumenter.Default instrumenter) {
return new MapBackedProvider(instrumenter);
}
}

MODIFY
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/context/MapBackedProvider.java
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/context/MapBackedProvider.java
package datadog.trace.agent.tooling.context;
import datadog.trace.agent.tooling.Instrumenter;
+import datadog.trace.agent.tooling.Utils;
+import datadog.trace.bootstrap.WeakMap;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicReference;
+import jdk.internal.org.objectweb.asm.ClassWriter;
+import lombok.extern.slf4j.Slf4j;
+import net.bytebuddy.ByteBuddy;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.AsmVisitorWrapper;
import net.bytebuddy.description.field.FieldDescription;
@@ -9,19 +19,35 @@ import net.bytebuddy.description.method.MethodList;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.implementation.Implementation;
import net.bytebuddy.jar.asm.ClassVisitor;
+import net.bytebuddy.jar.asm.Label;
import net.bytebuddy.jar.asm.MethodVisitor;
import net.bytebuddy.jar.asm.Opcodes;
+import net.bytebuddy.jar.asm.Type;
import net.bytebuddy.pool.TypePool;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-
-/**
- * InstrumentationContextProvider which stores context in a global map.
- */
+/** InstrumentationContextProvider which stores context in a global map. */
+@Slf4j
public class MapBackedProvider implements InstrumentationContextProvider {
+ private static final Method contextGetMethod;
+ private static final Method mapGetMethod;
+ /** dynamic-class-name -> dynamic-class-bytes */
+ private final AtomicReference<Map<String, byte[]>> dynamicClasses = new AtomicReference<>(null);
+
+ /** user-class-name -> dynamic-class-name */
+ private final AtomicReference<Map<String, String>> dynamicClassNames =
+ new AtomicReference<>(null);
+
+ static {
+ try {
+ contextGetMethod =
+ InstrumentationContext.class.getMethod("get", Object.class, Class.class, Class.class);
+ mapGetMethod =
+ MapHolder.class.getMethod("getOrCreate", Object.class, Class.class, Class.class);
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
private final Instrumenter.Default instrumenter;
public MapBackedProvider(Instrumenter.Default instrumenter) {
@@ -33,32 +59,131 @@ public class MapBackedProvider implements InstrumentationContextProvider {
return new AsmVisitorWrapper() {
@Override
public int mergeWriter(int flags) {
- // FIXME: any writer flags needed?
- return flags;
+ return flags | ClassWriter.COMPUTE_MAXS;
}
@Override
public int mergeReader(int flags) {
- // FIXME: any reader flags needed?
return flags;
}
@Override
- public ClassVisitor wrap(TypeDescription instrumentedType, ClassVisitor classVisitor, Implementation.Context implementationContext, TypePool typePool, FieldList<FieldDescription.InDefinedShape> fields, MethodList<?> methods, int writerFlags, int readerFlags) {
+ public ClassVisitor wrap(
+ final TypeDescription instrumentedType,
+ ClassVisitor classVisitor,
+ Implementation.Context implementationContext,
+ TypePool typePool,
+ FieldList<FieldDescription.InDefinedShape> fields,
+ MethodList<?> methods,
+ int writerFlags,
+ int readerFlags) {
+ generateMapHolderClasses();
return new ClassVisitor(Opcodes.ASM7, classVisitor) {
@Override
- public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
+ public void visit(
+ int version,
+ int access,
+ String name,
+ String signature,
+ String superName,
+ String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
}
@Override
- public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {
- // TODO
- System.out.println(instrumenter.instrumentationPrimaryName + " context visit method: " + name + descriptor);
- for (Map.Entry<String, String> entry : instrumenter.contextStore().entrySet()) {
- System.out.println(" -- remap: " + entry.getKey() + " -> " + entry.getValue());
+ public MethodVisitor visitMethod(
+ int access, String name, String descriptor, String signature, String[] exceptions) {
+ final MethodVisitor mv =
+ super.visitMethod(access, name, descriptor, signature, exceptions);
+ return new MethodVisitor(Opcodes.ASM7, mv) {
+ /** The most recent objects pushed to the stack. */
+ private final Object[] stack = {null, null};
+ /** Most recent instructions. */
+ private final int[] insnStack = {-1, -1, -1};
+
+ @Override
+ public void visitMethodInsn(
+ int opcode, String owner, String name, String descriptor, boolean isInterface) {
+ pushOpcode(opcode);
+ if (Utils.getInternalName(contextGetMethod.getDeclaringClass().getName())
+ .equals(owner)
+ && contextGetMethod.getName().equals(name)
+ && Type.getMethodDescriptor(contextGetMethod).equals(descriptor)) {
+ log.debug("Found context-store access in {}", instrumenter.getClass().getName());
+ if ((insnStack[0] == Opcodes.INVOKESTATIC
+ && insnStack[1] == Opcodes.LDC
+ && insnStack[2] == Opcodes.LDC)
+ && (stack[0] instanceof Type && stack[1] instanceof Type)) {
+ final String contextClassName = ((Type) stack[0]).getClassName();
+ final String userClassName = ((Type) stack[1]).getClassName();
+ final String mapHolderClass = dynamicClassNames.get().get(userClassName);
+ log.debug(
+ "Rewriting context-store map fetch for instrumenter {}: {} -> {}",
+ instrumenter.getClass().getName(),
+ userClassName,
+ contextClassName);
+ if (mapHolderClass != null
+ && contextClassName.equals(
+ instrumenter.contextStore().get(userClassName))) {
+ // stack: contextClass | userClass | instance
+ mv.visitMethodInsn(
+ Opcodes.INVOKESTATIC,
+ Utils.getInternalName(mapHolderClass),
+ mapGetMethod.getName(),
+ Type.getMethodDescriptor(mapGetMethod),
+ false);
+ return;
+ }
+ }
+ throw new IllegalStateException(
+ "Incorrect Context Api Usage detected. User and context class must be class-literals. Example of correct usage: InstrumentationContext.get(runnableInstance, Runnable.class, RunnableState.class)");
+ } else {
+ super.visitMethodInsn(opcode, owner, name, descriptor, isInterface);
+ }
+ }
+
+ private void pushOpcode(final int opcode) {
+ System.arraycopy(insnStack, 0, insnStack, 1, insnStack.length - 1);
+ insnStack[0] = opcode;
+ }
+
+ private void pushStack(Object o) {
+ System.arraycopy(stack, 0, stack, 1, stack.length - 1);
+ stack[0] = o;
+ }
+
+ @Override
+ public void visitInsn(int opcode) {
+ pushOpcode(opcode);
+ super.visitInsn(opcode);
+ }
+
+ @Override
+ public void visitJumpInsn(int opcode, Label label) {
+ pushOpcode(opcode);
+ super.visitJumpInsn(opcode, label);
+ }
+
+ @Override
+ public void visitIntInsn(int opcode, int operand) {
+ pushOpcode(opcode);
+ super.visitIntInsn(opcode, operand);
+ }
+
+ @Override
+ public void visitVarInsn(int opcode, int var) {
+ pushOpcode(opcode);
+ pushStack(var);
+ super.visitVarInsn(opcode, var);
+ }
+
+ @Override
+ public void visitLdcInsn(Object value) {
+ pushOpcode(Opcodes.LDC);
+ pushStack(value);
+ super.visitLdcInsn(value);
}
- return super.visitMethod(access, name, descriptor, signature, exceptions);
+ };
}
};
}
@@ -66,9 +191,8 @@ public class MapBackedProvider implements InstrumentationContextProvider {
}
@Override
- public List<byte[]> dynamicClasses() {
- // TODO: create a map-holder class for each key in the context-store
- return Collections.emptyList();
+ public Map<String, byte[]> dynamicClasses() {
+ return createOrGetDynamicClasses();
}
@Override
@@ -76,9 +200,71 @@ public class MapBackedProvider implements InstrumentationContextProvider {
return builder;
}
- /**
- * Template class used to generate the class holding the global map.
- */
+ private Map<String, byte[]> createOrGetDynamicClasses() {
+ if (dynamicClasses.get() == null) {
+ generateMapHolderClasses();
+ }
+ return dynamicClasses.get();
+ }
+
+ private synchronized void generateMapHolderClasses() {
+ if (dynamicClasses.get() == null) {
+ final Map<String, byte[]> dynamicClasses = new HashMap<>(instrumenter.contextStore().size());
+ final Map<String, String> dynamicClassNames =
+ new HashMap<>(instrumenter.contextStore().size());
+ for (final String userClassName : instrumenter.contextStore().keySet()) {
+ final String dynamicClassName =
+ instrumenter.getClass().getName()
+ + "$ContextStore"
+ + userClassName.replaceAll(".*([^\\.]+)$", "\\1")
+ + UUID.randomUUID().toString().replace('-', '_');
+
+ dynamicClassNames.put(userClassName, dynamicClassName);
+ dynamicClasses.put(dynamicClassName, makeMapHolderClass(dynamicClassName));
+ }
+ this.dynamicClassNames.compareAndSet(null, dynamicClassNames);
+ this.dynamicClasses.compareAndSet(null, dynamicClasses);
+ }
+ }
+
+ private byte[] makeMapHolderClass(String className) {
+ return new ByteBuddy()
+ .rebase(MapHolder.class)
+ .modifiers(Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL)
+ .name(className)
+ .make()
+ .getBytes();
+ }
+
+ /** Template class used to generate the class holding the global map. */
private static final class MapHolder {
+ public static final WeakMap MAP = WeakMap.Provider.newWeakMap();
+
+ public static Object getOrCreate(Object instance, Class userClass, Class contextClass) {
+ if (!userClass.isAssignableFrom(instance.getClass())) {
+ throw new RuntimeException(
+ "Illegal context lookup. "
+ + instance.getClass().getName()
+ + " cannot be cast to "
+ + userClass.getName());
+ }
+ Object contextInstance = MAP.get(instance);
+ if (null == contextInstance) {
+ synchronized (instance) {
+ contextInstance = MAP.get(instance);
+ if (null == contextInstance) {
+ try {
+ contextInstance = contextClass.newInstance();
+ MAP.put(instance, contextInstance);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+ return contextInstance;
+ }
+
+ private MapHolder() {}
}
}

MODIFY
dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/test/context/MapBackedProviderTest.groovy
dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/test/context/MapBackedProviderTest.groovy
package datadog.trace.agent.test.context
+import datadog.trace.agent.test.TestUtils
+import datadog.trace.agent.tooling.HelperInjector
import datadog.trace.agent.tooling.Instrumenter
import datadog.trace.agent.tooling.context.MapBackedProvider
import net.bytebuddy.agent.ByteBuddyAgent
@@ -12,6 +14,9 @@ import net.bytebuddy.utility.JavaModule
import spock.lang.Shared
import spock.lang.Specification
+import java.lang.ref.WeakReference
+import java.util.concurrent.Callable
+
import static net.bytebuddy.matcher.ElementMatchers.named
class MapBackedProviderTest extends Specification {
@@ -25,11 +30,30 @@ class MapBackedProviderTest extends Specification {
.disableClassFormatChanges()
.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
.type(named("datadog.trace.agent.test.context.ClassToRemap"))
+ .or(named("datadog.trace.agent.test.context.BadClassToRemap"))
.transform(new AgentBuilder.Transformer() {
@Override
DynamicType.Builder<?> transform(DynamicType.Builder<?> builder, TypeDescription typeDescription, ClassLoader classLoader, JavaModule module) {
return builder.visit(contextProvider.getInstrumentationVisitor())
+ }})
+ .transform(new HelperInjector(contextProvider.dynamicClasses()))
+ .with(new AgentBuilder.Listener() {
+ @Override
+ void onDiscovery(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) { }
+ @Override
+ void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, boolean loaded, DynamicType dynamicType) {
+ assert !"datadog.trace.agent.test.context.BadClassToRemap".equals(typeDescription.getName())
+ }
+ @Override
+ void onIgnored(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, boolean loaded) { }
+ @Override
+ void onError(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded, Throwable throwable) {
+ assert "datadog.trace.agent.test.context.BadClassToRemap".equals(typeName)
+ System.err.println("Exception during test")
+ throwable.printStackTrace()
}
+ @Override
+ void onComplete(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) { }
})
ByteBuddyAgent.install()
@@ -41,28 +65,66 @@ class MapBackedProviderTest extends Specification {
}
def "correct api usage stores state in map"() {
- setup:
+ when:
ClassToRemap instance1 = new ClassToRemap()
ClassToRemap.mapObject(instance1)
ClassToRemap.mapObject(instance1)
ClassToRemap instance2 = new ClassToRemap()
- ClassToRemap.mapObject(instance1)
+ ClassToRemap.mapObject(instance2)
ClassToRemap instance3 = new ClassToRemap()
- expect:
+ then:
ClassToRemap.mapObject(instance1) == 3
ClassToRemap.mapObject(instance2) == 2
ClassToRemap.mapObject(instance3) == 1
+
+ when:
+ Runnable r1 = new TestRunnable()
+ Runnable r2 = new TestRunnable()
+ ClassToRemap.mapOtherObject(r1)
+
+ then:
+ ClassToRemap.mapOtherObject(r1) == 2
+ ClassToRemap.mapOtherObject(r2) == 1
}
+ def "backing map should not create strong refs to user instances"() {
+ when:
+ ClassToRemap instance = new ClassToRemap()
+ int count = ClassToRemap.mapObject(instance)
+ WeakReference<ClassToRemap> instanceRef = new WeakReference(instance)
+ instance = null
+ TestUtils.awaitGC(instanceRef)
- // TODO
- def "incorrect api usage fails instrumentation"() {
- setup:
- println "loaded class: " + BadClassToRemap.getName()
- expect:
- 1 == 1
- // Instrumentation remapper does not apply.
+ then:
+ instanceRef.get() == null
+ count == 1
+ }
+
+ def "incorrect api usage fails at class load time"() {
+ when:
+ BadClassToRemap.getName()
+
+ then:
+ noExceptionThrown()
+
+ when:
+ BadClassToRemap.unmappedObjectError(new Callable() {
+ @Override
+ Object call() throws Exception {
+ return null
+ }
+ })
+
+ then:
+ thrown RuntimeException
+ }
+
+ def "context store fails if runtime types are incorrect" () {
+ when:
+ ClassToRemap.mapIncorrectObject()
+ then:
+ thrown RuntimeException
}
static class TestInstrumenter extends Instrumenter.Default {
@@ -88,4 +150,9 @@ class MapBackedProviderTest extends Specification {
return contextStore
}
}
+
+ static class TestRunnable implements Runnable {
+ @Override
+ void run() {}
+ }
}

MODIFY
dd-java-agent/agent-tooling/src/test/java/datadog/trace/agent/test/context/BadClassToRemap.java
dd-java-agent/agent-tooling/src/test/java/datadog/trace/agent/test/context/BadClassToRemap.java
package datadog.trace.agent.test.context;
import datadog.trace.agent.tooling.context.InstrumentationContext;
-
import java.util.concurrent.Callable;
-/**
- * A class which incorrectly uses the context api.
- */
+/** A class which incorrectly uses the context api. */
public class BadClassToRemap {
/**
- * Callable does not have context class of type State.
- * This should throw an exception during class transform.
+ * Callable does not have context class of type State. This should throw an exception during class
+ * transform.
*/
public static int unmappedObjectError(Callable callable) {
- final State state = InstrumentationContext.get(callable, State.class);
+ final State state = InstrumentationContext.get(callable, Callable.class, State.class);
return ++state.anInt;
}

MODIFY
dd-java-agent/agent-tooling/src/test/java/datadog/trace/agent/test/context/ClassToRemap.java
dd-java-agent/agent-tooling/src/test/java/datadog/trace/agent/test/context/ClassToRemap.java
@@ -2,30 +2,40 @@ package datadog.trace.agent.test.context;
import datadog.trace.agent.tooling.context.InstrumentationContext;
-/**
- * A class which correctly uses the context api.
- */
+/** A class which correctly uses the context api. */
public class ClassToRemap {
/**
* ClassToRemap has context class of type State.
+ *
* @return The value of State#anInt
*/
public static int mapObject(final ClassToRemap classToRemap) {
- final State state = InstrumentationContext.get(classToRemap, State.class);
+ final State state = InstrumentationContext.get(classToRemap, ClassToRemap.class, State.class);
return ++state.anInt;
}
/**
* Runnable has context class of type State.
+ *
* @return The value of State#anInt
*/
public static int mapOtherObject(final Runnable runnable) {
- final State state = InstrumentationContext.get(runnable, State.class);
+ State state = InstrumentationContext.get(runnable, Runnable.class, State.class);
return ++state.anInt;
}
+ /**
+ * Instance passed to the context api does not extend the user class. This will throw an
+ * exception.
+ */
+ public static int mapIncorrectObject() {
+ State state = InstrumentationContext.get(new Object(), Runnable.class, State.class);
+ return state.anInt;
+ }
+
public static class State {
- int anInt = 0;
+ public int anInt = 0;
+ public Object anObject = new Object();
}
}

DELETE
dd-java-agent/agent-tooling/src/test/java/datadog/trace/agent/test/context/UserClass.java
-package datadog.trace.agent.test.context;
-
-public class UserClass {
-}

MODIFY
dd-java-agent/instrumentation/http-url-connection/src/main/java/datadog/trace/instrumentation/http_url_connection/HttpUrlConnectionInstrumentation.java
dd-java-agent/instrumentation/http-url-connection/src/main/java/datadog/trace/instrumentation/http_url_connection/HttpUrlConnectionInstrumentation.java
package datadog.trace.instrumentation.http_url_connection;
import static datadog.trace.agent.tooling.ByteBuddyElementMatchers.safeHasSuperType;
-import static datadog.trace.bootstrap.WeakMap.Provider.newWeakMap;
import static io.opentracing.log.Fields.ERROR_OBJECT;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
@@ -14,7 +13,6 @@ import datadog.trace.agent.tooling.context.InstrumentationContext;
import datadog.trace.api.DDSpanTypes;
import datadog.trace.api.DDTags;
import datadog.trace.bootstrap.CallDepthThreadLocalMap;
-import datadog.trace.bootstrap.WeakMap;
import io.opentracing.Scope;
import io.opentracing.Span;
import io.opentracing.Tracer;
@@ -54,7 +52,8 @@ public class HttpUrlConnectionInstrumentation extends Instrumenter.Default {
}
public Map<String, String> contextStore() {
- return Collections.singletonMap("java.net.HttpURLConnection", getClass().getName() + "$HttpURLState");
+ return Collections.singletonMap(
+ "java.net.HttpURLConnection", getClass().getName() + "$HttpURLState");
}
@Override
@@ -74,7 +73,8 @@ public class HttpUrlConnectionInstrumentation extends Instrumenter.Default {
@Advice.FieldValue("connected") final boolean connected,
@Advice.Origin("#m") final String methodName) {
- final HttpURLState state = InstrumentationContext.get(thiz, HttpURLState.class);
+ final HttpURLState state =
+ InstrumentationContext.get(thiz, HttpURLConnection.class, HttpURLState.class);
String operationName = "http.request";

Initial Map-backed implementation and test