Skip to content

Commit 54b1788

Browse files
Minor cleanups
1 parent 4998905 commit 54b1788

File tree

2 files changed

+61
-82
lines changed

2 files changed

+61
-82
lines changed

dd-java-agent/instrumentation-testing/src/main/groovy/datadog/trace/agent/test/BootstrapClasspathSetup.java

Lines changed: 24 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,9 @@
1111
import de.thetaphi.forbiddenapis.SuppressForbidden;
1212
import java.io.File;
1313
import java.io.IOException;
14-
import java.lang.reflect.Field;
15-
import java.lang.reflect.Method;
1614
import java.net.MalformedURLException;
1715
import java.net.URL;
1816
import java.net.URLClassLoader;
19-
import java.util.Arrays;
2017
import java.util.HashSet;
2118
import java.util.Set;
2219
import java.util.TreeSet;
@@ -29,16 +26,15 @@ public class BootstrapClasspathSetup implements LauncherSessionListener {
2926

3027
@Override
3128
public void launcherSessionOpened(LauncherSession session) {
32-
// do nothing
29+
// this method is only needed to trigger this class' static initializer before JUnit does
30+
// classpath scanning
3331
}
3432

35-
/**
36-
* An exact copy of {@link datadog.trace.bootstrap.Constants#BOOTSTRAP_PACKAGE_PREFIXES}.
37-
*
38-
* <p>This list is needed to initialize the bootstrap classpath because Utils' static initializer
39-
* references bootstrap classes (e.g. DatadogClassLoader).
40-
*/
41-
public static final String[] BOOTSTRAP_PACKAGE_PREFIXES_COPY = {
33+
private static final String[] TEST_EXCLUDED_BOOTSTRAP_PACKAGE_PREFIXES = {
34+
"ch.qos.logback.classic.servlet", // this draws javax.servlet deps that are not needed
35+
};
36+
37+
private static final String[] TEST_BOOTSTRAP_PREFIXES = {
4238
"datadog.slf4j",
4339
"datadog.context",
4440
"datadog.environment",
@@ -51,32 +47,14 @@ public void launcherSessionOpened(LauncherSession session) {
5147
"datadog.trace.instrumentation.api",
5248
"datadog.trace.logging",
5349
"datadog.trace.util",
50+
"org.slf4j",
51+
"ch.qos.logback",
5452
};
5553

56-
private static final String[] TEST_EXCLUDED_BOOTSTRAP_PACKAGE_PREFIXES = {
57-
"ch.qos.logback.classic.servlet", // this draws javax.servlet deps that are not needed
58-
};
59-
60-
private static final String[] TEST_BOOTSTRAP_PREFIXES;
61-
62-
public static final ClassPath TEST_CLASSPATH;
54+
public static final ClassPath TEST_CLASSPATH = computeTestClasspath();
6355

6456
static {
6557
ByteBuddyAgent.install();
66-
final String[] testBS = {
67-
"org.slf4j", "ch.qos.logback",
68-
};
69-
70-
TEST_BOOTSTRAP_PREFIXES =
71-
Arrays.copyOf(
72-
BOOTSTRAP_PACKAGE_PREFIXES_COPY,
73-
BOOTSTRAP_PACKAGE_PREFIXES_COPY.length + testBS.length);
74-
for (int i = 0; i < testBS.length; ++i) {
75-
TEST_BOOTSTRAP_PREFIXES[i + BOOTSTRAP_PACKAGE_PREFIXES_COPY.length] = testBS[i];
76-
}
77-
78-
TEST_CLASSPATH = computeTestClasspath();
79-
8058
setupBootstrapClasspath();
8159
}
8260

@@ -116,41 +94,6 @@ private static ClassLoader buildJavaClassPathClassLoader() {
11694
return new URLClassLoader(urls.build().toArray(new URL[0]), null);
11795
}
11896

119-
public static void assertNoBootstrapClassesInTestClass(final Class<?> testClass) {
120-
for (final Field field : testClass.getDeclaredFields()) {
121-
assertNotBootstrapClass(testClass, field.getType());
122-
}
123-
for (final Method method : testClass.getDeclaredMethods()) {
124-
assertNotBootstrapClass(testClass, method.getReturnType());
125-
for (final Class<?> paramType : method.getParameterTypes()) {
126-
assertNotBootstrapClass(testClass, paramType);
127-
}
128-
}
129-
}
130-
131-
private static void assertNotBootstrapClass(final Class<?> testClass, final Class<?> clazz) {
132-
if (!clazz.isPrimitive() && isBootstrapClass(clazz.getName())) {
133-
throw new IllegalStateException(
134-
testClass.getName()
135-
+ ": Bootstrap classes are not allowed in test class field or method signatures. Offending class: "
136-
+ clazz.getName());
137-
}
138-
}
139-
140-
private static boolean isBootstrapClass(final String name) {
141-
for (String prefix : TEST_BOOTSTRAP_PREFIXES) {
142-
if (name.startsWith(prefix)) {
143-
for (String excluded : TEST_EXCLUDED_BOOTSTRAP_PACKAGE_PREFIXES) {
144-
if (name.startsWith(excluded)) {
145-
return false;
146-
}
147-
}
148-
return true;
149-
}
150-
}
151-
return false;
152-
}
153-
15497
private static void setupBootstrapClasspath() {
15598
// Ensure there weren't any bootstrap classes loaded prematurely.
15699
Set<String> prematureBootstrapClasses = new TreeSet<>();
@@ -172,8 +115,6 @@ private static void setupBootstrapClasspath() {
172115
final File bootstrapJar = createBootstrapJar();
173116
ByteBuddyAgent.getInstrumentation()
174117
.appendToBootstrapClassLoaderSearch(new JarFile(bootstrapJar));
175-
// Utils cannot be referenced before this line, as its static initializers load bootstrap
176-
// classes (for example, the bootstrap proxy).
177118
BootstrapProxy.addBootstrapResource(bootstrapJar.toURI().toURL());
178119
} catch (final IOException e) {
179120
throw new RuntimeException(e);
@@ -192,4 +133,18 @@ private static File createBootstrapJar() throws IOException {
192133
SpockExtension.class.getClassLoader(), bootstrapClasses.toArray(new String[0]));
193134
return new File(jar.getFile());
194135
}
136+
137+
public static boolean isBootstrapClass(final String name) {
138+
for (String prefix : TEST_BOOTSTRAP_PREFIXES) {
139+
if (name.startsWith(prefix)) {
140+
for (String excluded : TEST_EXCLUDED_BOOTSTRAP_PACKAGE_PREFIXES) {
141+
if (name.startsWith(excluded)) {
142+
return false;
143+
}
144+
}
145+
return true;
146+
}
147+
}
148+
return false;
149+
}
195150
}

dd-java-agent/instrumentation-testing/src/main/groovy/datadog/trace/agent/test/SpockExtension.java

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package datadog.trace.agent.test;
22

33
import java.io.IOException;
4+
import java.lang.reflect.Field;
5+
import java.lang.reflect.Method;
46
import java.util.List;
57
import net.bytebuddy.dynamic.ClassFileLocator;
68
import org.junit.jupiter.api.extension.AfterAllCallback;
@@ -20,16 +22,40 @@ public final class SpockExtension
2022
TestExecutionExceptionHandler {
2123

2224
private static final ExtensionContext.Namespace NAMESPACE =
23-
ExtensionContext.Namespace.create("dd", "spock", "loader");
25+
ExtensionContext.Namespace.create("dd", "spock");
26+
27+
private static final String INSTRUMENTATION_CLASSLOADER = "instrumentation-class-loader";
28+
private static final String ORIGINAL_CLASSLOADER = "original-thread-context-class-loader";
2429

2530
@Override
2631
public void beforeAll(ExtensionContext ctx) {
2732
Class<?> testClass = ctx.getRequiredTestClass();
28-
BootstrapClasspathSetup.assertNoBootstrapClassesInTestClass(testClass);
33+
assertNoBootstrapClassesInTestClass(testClass);
2934

3035
InstrumentationClassLoader custom =
3136
new InstrumentationClassLoader(testClass.getClassLoader(), testClass.getName());
32-
ctx.getStore(NAMESPACE).put("loader", custom);
37+
ctx.getStore(NAMESPACE).put(INSTRUMENTATION_CLASSLOADER, custom);
38+
}
39+
40+
public static void assertNoBootstrapClassesInTestClass(final Class<?> testClass) {
41+
for (final Field field : testClass.getDeclaredFields()) {
42+
assertNotBootstrapClass(testClass, field.getType());
43+
}
44+
for (final Method method : testClass.getDeclaredMethods()) {
45+
assertNotBootstrapClass(testClass, method.getReturnType());
46+
for (final Class<?> paramType : method.getParameterTypes()) {
47+
assertNotBootstrapClass(testClass, paramType);
48+
}
49+
}
50+
}
51+
52+
private static void assertNotBootstrapClass(final Class<?> testClass, final Class<?> clazz) {
53+
if (!clazz.isPrimitive() && BootstrapClasspathSetup.isBootstrapClass(clazz.getName())) {
54+
throw new IllegalStateException(
55+
testClass.getName()
56+
+ ": Bootstrap classes are not allowed in test class field or method signatures. Offending class: "
57+
+ clazz.getName());
58+
}
3359
}
3460

3561
@Override
@@ -39,14 +65,16 @@ public void afterAll(ExtensionContext ctx) {
3965

4066
@Override
4167
public void beforeEach(ExtensionContext ctx) {
42-
ClassLoader custom = ctx.getStore(NAMESPACE).get("loader", ClassLoader.class);
43-
ctx.getStore(NAMESPACE).put("prevTCCL", Thread.currentThread().getContextClassLoader());
44-
Thread.currentThread().setContextClassLoader(custom);
68+
ClassLoader instrumentationClassloader =
69+
ctx.getStore(NAMESPACE).get(INSTRUMENTATION_CLASSLOADER, ClassLoader.class);
70+
ctx.getStore(NAMESPACE)
71+
.put(ORIGINAL_CLASSLOADER, Thread.currentThread().getContextClassLoader());
72+
Thread.currentThread().setContextClassLoader(instrumentationClassloader);
4573
}
4674

4775
@Override
4876
public void afterEach(ExtensionContext ctx) {
49-
ClassLoader prev = ctx.getStore(NAMESPACE).remove("prevTCCL", ClassLoader.class);
77+
ClassLoader prev = ctx.getStore(NAMESPACE).remove(ORIGINAL_CLASSLOADER, ClassLoader.class);
5078
if (prev != null) {
5179
Thread.currentThread().setContextClassLoader(prev);
5280
}
@@ -86,11 +114,7 @@ static void fixTooManyInvocationsError(final TooManyInvocationsError error) {
86114
}
87115
}
88116

89-
/**
90-
* Class‑loader that <em>shadows</em> the test class, so that any re‑definitions via ByteBuddy
91-
* operate on a loader‑private copy instead of the one used by the build toolʼs class‑path
92-
* scanning. Delegation order = child‑first for the testʼs own package, parent‑first otherwise.
93-
*/
117+
/** Run test classes in a classloader which loads test classes before delegating. */
94118
private static class InstrumentationClassLoader extends ClassLoader {
95119
private final ClassLoader parent;
96120
private final String shadowPrefix;
@@ -101,7 +125,7 @@ private static class InstrumentationClassLoader extends ClassLoader {
101125
this.shadowPrefix = shadowPrefix;
102126
}
103127

104-
/** Inject the bytes of {@code clazz} into <b>this</b> loader, producing a shadow copy. */
128+
/** Forcefully inject the bytes of clazz into this classloader. */
105129
Class<?> shadow(Class<?> clazz) throws IOException {
106130
Class<?> loaded = findLoadedClass(clazz.getName());
107131
if (loaded != null && loaded.getClassLoader() == this) {

0 commit comments

Comments
 (0)