Restructure how WeakMap is implemented
Use an interface.
Use a provider/supplier to avoid loading more onto the bootstrap classpath.
MODIFY
dd-java-agent/agent-bootstrap/agent-bootstrap.gradle
dd-java-agent/agent-bootstrap/agent-bootstrap.gradle
@@ -9,13 +9,10 @@ excludedClassesConverage += ['datadog.trace.bootstrap.*']
dependencies {
compile project(':dd-trace-api')
- compile group: 'com.blogspot.mydailyjava', name: 'weak-lock-free', version: '0.13'
compile deps.opentracing
compile deps.slf4j
compile group: 'org.slf4j', name: 'slf4j-simple', version: versions.slf4j
// ^ Generally a bad idea for libraries, but we're shadowing.
-
- testCompile project(':dd-java-agent:testing')
}
jar {

MODIFY
dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/JDBCMaps.java
dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/JDBCMaps.java
package datadog.trace.bootstrap;
-import static datadog.trace.bootstrap.WeakMapManager.newWeakMap;
+import static datadog.trace.bootstrap.WeakMap.Provider.newWeakMap;
-import com.blogspot.mydailyjava.weaklockfree.WeakConcurrentMap;
import java.sql.Connection;
import java.sql.PreparedStatement;
import lombok.Data;
@@ -13,9 +12,8 @@ import lombok.Data;
* <p>In the bootstrap project to ensure visibility by all classes.
*/
public class JDBCMaps {
- public static final WeakConcurrentMap<Connection, DBInfo> connectionInfo = newWeakMap();
- public static final WeakConcurrentMap<PreparedStatement, String> preparedStatements =
- newWeakMap();
+ public static final WeakMap<Connection, DBInfo> connectionInfo = newWeakMap();
+ public static final WeakMap<PreparedStatement, String> preparedStatements = newWeakMap();
public static final String DB_QUERY = "DB Query";

ADD
dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/WeakMap.java
+package datadog.trace.bootstrap;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.concurrent.atomic.AtomicReference;
+
+public interface WeakMap<K, V> {
+
+ int size();
+
+ boolean containsKey(K target);
+
+ V get(K key);
+
+ void put(K key, V value);
+
+ class Provider {
+ private static final AtomicReference<Supplier> provider =
+ new AtomicReference<>(Supplier.DEFAULT);
+
+ /* The interface would be better defined as a Supplier, because we don't want throw an exception,
+ * but that wasn't introduced until Java 8 and we must be compatible with 7.
+ */
+ public static void registerIfAbsent(final Supplier provider) {
+ if (provider != null && provider != Supplier.DEFAULT) {
+ Provider.provider.compareAndSet(Supplier.DEFAULT, provider);
+ }
+ }
+
+ public static <K, V> WeakMap<K, V> newWeakMap() {
+ return provider.get().get();
+ }
+ }
+
+ interface Supplier {
+ <K, V> WeakMap<K, V> get();
+
+ Supplier DEFAULT = new Default();
+
+ class Default implements Supplier {
+
+ @Override
+ public <K, V> WeakMap<K, V> get() {
+ return new Adapter<>(Collections.synchronizedMap(new WeakHashMap<K, V>()));
+ }
+
+ private static class Adapter<K, V> implements WeakMap<K, V> {
+ private final Map<K, V> map;
+
+ private Adapter(final Map<K, V> map) {
+ this.map = map;
+ }
+
+ @Override
+ public int size() {
+ return map.size();
+ }
+
+ @Override
+ public boolean containsKey(final K key) {
+ return map.containsKey(key);
+ }
+
+ @Override
+ public V get(final K key) {
+ return map.get(key);
+ }
+
+ @Override
+ public void put(final K key, final V value) {
+ map.put(key, value);
+ }
+ }
+ }
+ }
+}

DELETE
dd-java-agent/agent-bootstrap/src/test/groovy/datadog/trace/bootstrap/WeakMapManagerTest.groovy
-package datadog.trace.bootstrap
-
-import datadog.trace.agent.test.TestUtils
-import spock.lang.Specification
-
-class WeakMapManagerTest extends Specification {
-
- def setup() {
- WeakMapManager.maps.clear()
- }
-
- def "calling new adds to the list"() {
- when:
- def map1 = WeakMapManager.newWeakMap()
-
- then:
- WeakMapManager.maps == [map1]
-
- when:
- def map2 = WeakMapManager.newWeakMap()
-
- then:
- WeakMapManager.maps == [map1, map2]
- }
-
- def "calling cleanMaps does cleanup"() {
- setup:
- def map = WeakMapManager.newWeakMap()
- map.put(new Object(), "value")
- TestUtils.awaitGC()
-
- expect:
- map.approximateSize() == 1
-
- when:
- WeakMapManager.cleanMaps()
-
- then:
- map.approximateSize() == 0
- }
-}

MODIFY
dd-java-agent/agent-tooling/agent-tooling.gradle
dd-java-agent/agent-tooling/agent-tooling.gradle
@@ -9,6 +9,7 @@ configurations {
dependencies {
compile project(':dd-java-agent:agent-bootstrap')
+ compile group: 'com.blogspot.mydailyjava', name: 'weak-lock-free', version: '0.13'
compile deps.bytebuddy
compile deps.bytebuddyagent
annotationProcessor deps.autoservice
@@ -17,6 +18,7 @@ dependencies {
compileOnly project(':dd-trace-ot')
testCompile deps.opentracing
+ testCompile project(':dd-java-agent:testing')
instrumentationMuzzle sourceSets.main.output
instrumentationMuzzle configurations.compile

MODIFY
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/ClassLoaderMatcher.java
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/ClassLoaderMatcher.java
package datadog.trace.agent.tooling;
-import static datadog.trace.bootstrap.WeakMapManager.newWeakMap;
+import static datadog.trace.bootstrap.WeakMap.Provider.newWeakMap;
-import com.blogspot.mydailyjava.weaklockfree.WeakConcurrentMap;
import datadog.trace.bootstrap.DatadogClassLoader;
import datadog.trace.bootstrap.PatchLogger;
+import datadog.trace.bootstrap.WeakMap;
import io.opentracing.util.GlobalTracer;
import java.util.Collections;
import java.util.HashSet;
@@ -44,7 +44,7 @@ public class ClassLoaderMatcher {
extends ElementMatcher.Junction.AbstractBase<ClassLoader> {
public static final SkipClassLoaderMatcher INSTANCE = new SkipClassLoaderMatcher();
/* Cache of classloader-instance -> (true|false). True = skip instrumentation. False = safe to instrument. */
- private static final WeakConcurrentMap<ClassLoader, Boolean> SKIP_CACHE = newWeakMap();
+ private static final WeakMap<ClassLoader, Boolean> SKIP_CACHE = newWeakMap();
private static final Set<String> CLASSLOADER_CLASSES_TO_SKIP;
static {
@@ -131,7 +131,7 @@ public class ClassLoaderMatcher {
public static class ClassLoaderHasClassMatcher
extends ElementMatcher.Junction.AbstractBase<ClassLoader> {
- private final WeakConcurrentMap<ClassLoader, Boolean> cache = newWeakMap();
+ private final WeakMap<ClassLoader, Boolean> cache = newWeakMap();
private final String[] names;
@@ -163,7 +163,7 @@ public class ClassLoaderMatcher {
public static class ClassLoaderHasClassWithFieldMatcher
extends ElementMatcher.Junction.AbstractBase<ClassLoader> {
- private final WeakConcurrentMap<ClassLoader, Boolean> cache = newWeakMap();
+ private final WeakMap<ClassLoader, Boolean> cache = newWeakMap();
private final String className;
private final String fieldName;
@@ -201,7 +201,7 @@ public class ClassLoaderMatcher {
public static class ClassLoaderHasClassWithMethodMatcher
extends ElementMatcher.Junction.AbstractBase<ClassLoader> {
- private final WeakConcurrentMap<ClassLoader, Boolean> cache = newWeakMap();
+ private final WeakMap<ClassLoader, Boolean> cache = newWeakMap();
private final String className;
private final String methodName;

MODIFY
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/TracerInstaller.java
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/TracerInstaller.java
package datadog.trace.agent.tooling;
import datadog.opentracing.DDTracer;
+import datadog.trace.bootstrap.WeakMap;
import io.opentracing.Tracer;
import io.opentracing.util.GlobalTracer;
import lombok.extern.slf4j.Slf4j;
@@ -9,6 +10,8 @@ import lombok.extern.slf4j.Slf4j;
public class TracerInstaller {
/** Register a global tracer if no global tracer is already registered. */
public static synchronized void installGlobalTracer() {
+ registerWeakMapProvider();
+
if (!GlobalTracer.isRegistered()) {
final Tracer resolved = new DDTracer();
try {
@@ -27,4 +30,10 @@ public class TracerInstaller {
log.debug(
AgentInstaller.class.getName() + " loaded on " + AgentInstaller.class.getClassLoader());
}
+
+ private static void registerWeakMapProvider() {
+ WeakMap.Provider.registerIfAbsent(new WeakMapSuppliers.WCMManaged());
+ // WeakMap.Provider.registerIfAbsent(new WeakMapSuppliers.WCMInline());
+ // WeakMap.Provider.registerIfAbsent(new WeakMapSuppliers.Guava());
+ }
}

MODIFY
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/ReferenceMatcher.java
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/ReferenceMatcher.java
package datadog.trace.agent.tooling.muzzle;
-import static datadog.trace.bootstrap.WeakMapManager.newWeakMap;
+import static datadog.trace.bootstrap.WeakMap.Provider.newWeakMap;
import static net.bytebuddy.dynamic.loading.ClassLoadingStrategy.BOOTSTRAP_LOADER;
-import com.blogspot.mydailyjava.weaklockfree.WeakConcurrentMap;
import datadog.trace.agent.tooling.Utils;
+import datadog.trace.bootstrap.WeakMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
@@ -15,8 +15,7 @@ import lombok.extern.slf4j.Slf4j;
/** Matches a set of references against a classloader. */
@Slf4j
public class ReferenceMatcher {
- private final WeakConcurrentMap<ClassLoader, List<Reference.Mismatch>> mismatchCache =
- newWeakMap();
+ private final WeakMap<ClassLoader, List<Reference.Mismatch>> mismatchCache = newWeakMap();
private final Reference[] references;
private final Set<String> helperClassNames;

ADD
dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/tooling/WeakConcurrentMapSupplierTest.groovy
+package datadog.trace.agent.tooling
+
+import datadog.trace.agent.test.TestUtils
+import datadog.trace.bootstrap.WeakMap
+import spock.lang.Specification
+
+class WeakConcurrentMapSupplierTest extends Specification {
+ def supplier = new WeakMapSuppliers.WCMManaged()
+
+ def setup() {
+ WeakMap.Provider.provider.set(supplier)
+ }
+
+ def "calling new adds to the list"() {
+ when:
+ def map1 = WeakMap.Provider.newWeakMap().map
+
+ then:
+ supplier.maps.iterator().next().get() == map1
+
+ when:
+ def map2 = WeakMap.Provider.newWeakMap().map
+ def iterator = supplier.maps.iterator()
+
+ then:
+ iterator.next().get() == map1
+ iterator.next().get() == map2
+ }
+
+ def "calling cleanMaps does cleanup"() {
+ setup:
+ def map = WeakMap.Provider.newWeakMap()
+ map.put(new Object(), "value")
+ TestUtils.awaitGC()
+
+ expect:
+ map.size() == 1
+
+ when:
+ supplier.cleanMaps()
+
+ then:
+ map.size() == 0
+ }
+}

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.WeakMapManager.newWeakMap;
+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;
@@ -9,12 +9,12 @@ import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not;
-import com.blogspot.mydailyjava.weaklockfree.WeakConcurrentMap;
import com.google.auto.service.AutoService;
import datadog.trace.agent.tooling.Instrumenter;
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;
@@ -175,8 +175,7 @@ public class HttpUrlConnectionInstrumentation extends Instrumenter.Default {
}
public static class HttpURLState {
- private static final WeakConcurrentMap<HttpURLConnection, HttpURLState> STATE_MAP =
- newWeakMap();
+ private static final WeakMap<HttpURLConnection, HttpURLState> STATE_MAP = newWeakMap();
public static HttpURLState get(final HttpURLConnection connection) {
HttpURLState state = STATE_MAP.get(connection);

Restructure how WeakMap is implemented

Use an interface.
Use a provider/supplier to avoid loading more onto the bootstrap classpath.