Merge pull request #190 from DataDog/tyler/fix-injection-order
Ensure helper classes are injected in a fixed order
This commit is contained in:
commit
1bb01c256b
|
@ -9,8 +9,6 @@ import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
|
@ -19,15 +17,6 @@ import java.util.jar.JarOutputStream;
|
||||||
import java.util.jar.Manifest;
|
import java.util.jar.Manifest;
|
||||||
|
|
||||||
public class TestUtils {
|
public class TestUtils {
|
||||||
private static Method findLoadedClassMethod = null;
|
|
||||||
|
|
||||||
static {
|
|
||||||
try {
|
|
||||||
findLoadedClassMethod = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
|
|
||||||
} catch (NoSuchMethodException | SecurityException e) {
|
|
||||||
throw new IllegalStateException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void registerOrReplaceGlobalTracer(final Tracer tracer) {
|
public static void registerOrReplaceGlobalTracer(final Tracer tracer) {
|
||||||
try {
|
try {
|
||||||
|
@ -65,18 +54,6 @@ public class TestUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isClassLoaded(final String className, final ClassLoader classLoader) {
|
|
||||||
try {
|
|
||||||
findLoadedClassMethod.setAccessible(true);
|
|
||||||
final Class<?> loadedClass = (Class<?>) findLoadedClassMethod.invoke(classLoader, className);
|
|
||||||
return null != loadedClass && loadedClass.getClassLoader() == classLoader;
|
|
||||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
|
||||||
throw new IllegalStateException(e);
|
|
||||||
} finally {
|
|
||||||
findLoadedClassMethod.setAccessible(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a temporary jar on the filesystem with the bytes of the given classes.
|
* Create a temporary jar on the filesystem with the bytes of the given classes.
|
||||||
*
|
*
|
||||||
|
|
|
@ -3,8 +3,9 @@ package datadog.trace.agent.tooling;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
@ -26,15 +27,17 @@ public class HelperInjector implements Transformer {
|
||||||
* Construct HelperInjector.
|
* Construct HelperInjector.
|
||||||
*
|
*
|
||||||
* @param helperClassNames binary names of the helper classes to inject. These class names must be
|
* @param helperClassNames binary names of the helper classes to inject. These class names must be
|
||||||
* resolvable by the classloader returned by DDAdvice#getAgentClassLoader()
|
* resolvable by the classloader returned by DDAdvice#getAgentClassLoader(). Classes are
|
||||||
|
* injected in the order provided. This is important if there is interdependency between
|
||||||
|
* helper classes that requires them to be injected in a specific order.
|
||||||
*/
|
*/
|
||||||
public HelperInjector(final String... helperClassNames) {
|
public HelperInjector(final String... helperClassNames) {
|
||||||
this.helperClassNames = new HashSet<>(Arrays.asList(helperClassNames));
|
this.helperClassNames = new LinkedHashSet<>(Arrays.asList(helperClassNames));
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized Map<TypeDescription, byte[]> getHelperMap() throws IOException {
|
private synchronized Map<TypeDescription, byte[]> getHelperMap() throws IOException {
|
||||||
if (helperMap == null) {
|
if (helperMap == null) {
|
||||||
helperMap = new HashMap<>(helperClassNames.size());
|
helperMap = new LinkedHashMap<>(helperClassNames.size());
|
||||||
for (final String helperName : helperClassNames) {
|
for (final String helperName : helperClassNames) {
|
||||||
final ClassFileLocator locator =
|
final ClassFileLocator locator =
|
||||||
ClassFileLocator.ForClassLoader.of(Utils.getAgentClassLoader());
|
ClassFileLocator.ForClassLoader.of(Utils.getAgentClassLoader());
|
||||||
|
@ -58,7 +61,29 @@ public class HelperInjector implements Transformer {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (!injectedClassLoaders.contains(classLoader)) {
|
if (!injectedClassLoaders.contains(classLoader)) {
|
||||||
try {
|
try {
|
||||||
new ClassInjector.UsingReflection(classLoader).inject(getHelperMap());
|
final Map<TypeDescription, byte[]> helperMap = getHelperMap();
|
||||||
|
final Set<String> existingClasses = new HashSet<>();
|
||||||
|
final ClassLoader systemCL = ClassLoader.getSystemClassLoader();
|
||||||
|
if (!classLoader.equals(systemCL)) {
|
||||||
|
// Build a list of existing helper classes.
|
||||||
|
for (final TypeDescription def : helperMap.keySet()) {
|
||||||
|
final String name = def.getName();
|
||||||
|
if (Utils.isClassLoaded(name, systemCL)) {
|
||||||
|
existingClasses.add(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
new ClassInjector.UsingReflection(classLoader).inject(helperMap);
|
||||||
|
if (!classLoader.equals(systemCL)) {
|
||||||
|
for (final TypeDescription def : helperMap.keySet()) {
|
||||||
|
// Ensure we didn't add any helper classes to the system CL.
|
||||||
|
final String name = def.getName();
|
||||||
|
if (!existingClasses.contains(name) && Utils.isClassLoaded(name, systemCL)) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Class was erroneously loaded on the System classloader: " + name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
log.error("Failed to inject helper classes into " + classLoader, e);
|
log.error("Failed to inject helper classes into " + classLoader, e);
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
|
|
|
@ -1,6 +1,19 @@
|
||||||
package datadog.trace.agent.tooling;
|
package datadog.trace.agent.tooling;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
public class Utils {
|
public class Utils {
|
||||||
|
private static Method findLoadedClassMethod = null;
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
findLoadedClassMethod = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
|
||||||
|
} catch (NoSuchMethodException | SecurityException e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Return the classloader the core agent is running on. */
|
/** Return the classloader the core agent is running on. */
|
||||||
public static ClassLoader getAgentClassLoader() {
|
public static ClassLoader getAgentClassLoader() {
|
||||||
return AgentInstaller.class.getClassLoader();
|
return AgentInstaller.class.getClassLoader();
|
||||||
|
@ -11,5 +24,17 @@ public class Utils {
|
||||||
return className.replace('.', '/') + ".class";
|
return className.replace('.', '/') + ".class";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isClassLoaded(final String className, final ClassLoader classLoader) {
|
||||||
|
try {
|
||||||
|
findLoadedClassMethod.setAccessible(true);
|
||||||
|
final Class<?> loadedClass = (Class<?>) findLoadedClassMethod.invoke(classLoader, className);
|
||||||
|
return null != loadedClass && loadedClass.getClassLoader() == classLoader;
|
||||||
|
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
|
} finally {
|
||||||
|
findLoadedClassMethod.setAccessible(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Utils() {}
|
private Utils() {}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue