Skip to content

Commit 1bb01c2

Browse files
authored
Merge pull request #190 from DataDog/tyler/fix-injection-order
Ensure helper classes are injected in a fixed order
2 parents 55c8fdd + d68b970 commit 1bb01c2

File tree

3 files changed

+55
-28
lines changed

3 files changed

+55
-28
lines changed

dd-java-agent/testing/src/main/java/datadog/trace/agent/test/TestUtils.java

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
import java.io.IOException;
1010
import java.io.InputStream;
1111
import java.lang.reflect.Field;
12-
import java.lang.reflect.InvocationTargetException;
13-
import java.lang.reflect.Method;
1412
import java.net.URL;
1513
import java.util.UUID;
1614
import java.util.concurrent.Callable;
@@ -19,15 +17,6 @@
1917
import java.util.jar.Manifest;
2018

2119
public class TestUtils {
22-
private static Method findLoadedClassMethod = null;
23-
24-
static {
25-
try {
26-
findLoadedClassMethod = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
27-
} catch (NoSuchMethodException | SecurityException e) {
28-
throw new IllegalStateException(e);
29-
}
30-
}
3120

3221
public static void registerOrReplaceGlobalTracer(final Tracer tracer) {
3322
try {
@@ -65,18 +54,6 @@ public static <T extends Object> Object runUnderTrace(
6554
}
6655
}
6756

68-
public static boolean isClassLoaded(final String className, final ClassLoader classLoader) {
69-
try {
70-
findLoadedClassMethod.setAccessible(true);
71-
final Class<?> loadedClass = (Class<?>) findLoadedClassMethod.invoke(classLoader, className);
72-
return null != loadedClass && loadedClass.getClassLoader() == classLoader;
73-
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
74-
throw new IllegalStateException(e);
75-
} finally {
76-
findLoadedClassMethod.setAccessible(false);
77-
}
78-
}
79-
8057
/**
8158
* Create a temporary jar on the filesystem with the bytes of the given classes.
8259
*

dd-java-agent/tooling/src/main/java/datadog/trace/agent/tooling/HelperInjector.java

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
import java.io.IOException;
44
import java.util.Arrays;
55
import java.util.Collections;
6-
import java.util.HashMap;
76
import java.util.HashSet;
7+
import java.util.LinkedHashMap;
8+
import java.util.LinkedHashSet;
89
import java.util.Map;
910
import java.util.Set;
1011
import lombok.extern.slf4j.Slf4j;
@@ -26,15 +27,17 @@ public class HelperInjector implements Transformer {
2627
* Construct HelperInjector.
2728
*
2829
* @param helperClassNames binary names of the helper classes to inject. These class names must be
29-
* resolvable by the classloader returned by DDAdvice#getAgentClassLoader()
30+
* resolvable by the classloader returned by DDAdvice#getAgentClassLoader(). Classes are
31+
* injected in the order provided. This is important if there is interdependency between
32+
* helper classes that requires them to be injected in a specific order.
3033
*/
3134
public HelperInjector(final String... helperClassNames) {
32-
this.helperClassNames = new HashSet<>(Arrays.asList(helperClassNames));
35+
this.helperClassNames = new LinkedHashSet<>(Arrays.asList(helperClassNames));
3336
}
3437

3538
private synchronized Map<TypeDescription, byte[]> getHelperMap() throws IOException {
3639
if (helperMap == null) {
37-
helperMap = new HashMap<>(helperClassNames.size());
40+
helperMap = new LinkedHashMap<>(helperClassNames.size());
3841
for (final String helperName : helperClassNames) {
3942
final ClassFileLocator locator =
4043
ClassFileLocator.ForClassLoader.of(Utils.getAgentClassLoader());
@@ -58,7 +61,29 @@ public DynamicType.Builder<?> transform(
5861
synchronized (this) {
5962
if (!injectedClassLoaders.contains(classLoader)) {
6063
try {
61-
new ClassInjector.UsingReflection(classLoader).inject(getHelperMap());
64+
final Map<TypeDescription, byte[]> helperMap = getHelperMap();
65+
final Set<String> existingClasses = new HashSet<>();
66+
final ClassLoader systemCL = ClassLoader.getSystemClassLoader();
67+
if (!classLoader.equals(systemCL)) {
68+
// Build a list of existing helper classes.
69+
for (final TypeDescription def : helperMap.keySet()) {
70+
final String name = def.getName();
71+
if (Utils.isClassLoaded(name, systemCL)) {
72+
existingClasses.add(name);
73+
}
74+
}
75+
}
76+
new ClassInjector.UsingReflection(classLoader).inject(helperMap);
77+
if (!classLoader.equals(systemCL)) {
78+
for (final TypeDescription def : helperMap.keySet()) {
79+
// Ensure we didn't add any helper classes to the system CL.
80+
final String name = def.getName();
81+
if (!existingClasses.contains(name) && Utils.isClassLoaded(name, systemCL)) {
82+
throw new IllegalStateException(
83+
"Class was erroneously loaded on the System classloader: " + name);
84+
}
85+
}
86+
}
6287
} catch (final Exception e) {
6388
log.error("Failed to inject helper classes into " + classLoader, e);
6489
throw new RuntimeException(e);

dd-java-agent/tooling/src/main/java/datadog/trace/agent/tooling/Utils.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,19 @@
11
package datadog.trace.agent.tooling;
22

3+
import java.lang.reflect.InvocationTargetException;
4+
import java.lang.reflect.Method;
5+
36
public class Utils {
7+
private static Method findLoadedClassMethod = null;
8+
9+
static {
10+
try {
11+
findLoadedClassMethod = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
12+
} catch (NoSuchMethodException | SecurityException e) {
13+
throw new IllegalStateException(e);
14+
}
15+
}
16+
417
/** Return the classloader the core agent is running on. */
518
public static ClassLoader getAgentClassLoader() {
619
return AgentInstaller.class.getClassLoader();
@@ -11,5 +24,17 @@ public static String getResourceName(final String className) {
1124
return className.replace('.', '/') + ".class";
1225
}
1326

27+
public static boolean isClassLoaded(final String className, final ClassLoader classLoader) {
28+
try {
29+
findLoadedClassMethod.setAccessible(true);
30+
final Class<?> loadedClass = (Class<?>) findLoadedClassMethod.invoke(classLoader, className);
31+
return null != loadedClass && loadedClass.getClassLoader() == classLoader;
32+
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
33+
throw new IllegalStateException(e);
34+
} finally {
35+
findLoadedClassMethod.setAccessible(false);
36+
}
37+
}
38+
1439
private Utils() {}
1540
}

0 commit comments

Comments
 (0)