Skip to content

Commit 0bf1a9c

Browse files
authored
Properly test glassfish helpers during build-time muzzle checks (#10114)
The glassfish helpers require package-private access to an interface, so must be injected into the glassfish class-loader. At runtime this is achieved via the instrumentation-based class-injector. We can simulate this for the build-time muzzle checks by granting the same access via reflection.
1 parent 4eda0a1 commit 0bf1a9c

File tree

1 file changed

+30
-3
lines changed

1 file changed

+30
-3
lines changed

dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/muzzle/MuzzleVersionScanPlugin.java

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import de.thetaphi.forbiddenapis.SuppressForbidden;
99
import java.io.IOException;
1010
import java.io.PrintWriter;
11+
import java.lang.reflect.Method;
1112
import java.util.ArrayList;
1213
import java.util.Arrays;
1314
import java.util.HashSet;
@@ -16,6 +17,7 @@
1617
import java.util.Map;
1718
import java.util.ServiceLoader;
1819
import java.util.Set;
20+
import java.util.function.BiConsumer;
1921
import net.bytebuddy.dynamic.ClassFileLocator;
2022

2123
/**
@@ -75,9 +77,9 @@ public static void assertInstrumentationMuzzled(
7577
// verify helper consistency
7678
final String[] helperClassNames = module.helperClassNames();
7779
if (helperClassNames.length > 0) {
78-
HelperClassLoader helperClassLoader = new HelperClassLoader(testApplicationLoader);
80+
BiConsumer<String, byte[]> injectClassHelper = injectClassHelper(testApplicationLoader);
7981
for (Map.Entry<String, byte[]> helper : createHelperMap(module).entrySet()) {
80-
helperClassLoader.injectClass(helper.getKey(), helper.getValue());
82+
injectClassHelper.accept(helper.getKey(), helper.getValue());
8183
}
8284
}
8385
} catch (final Throwable e) {
@@ -90,6 +92,29 @@ public static void assertInstrumentationMuzzled(
9092
}
9193
}
9294

95+
/** Simulates instrumentation-based access to defineClass feature. */
96+
private static BiConsumer<String, byte[]> injectClassHelper(ClassLoader cl) {
97+
try {
98+
Method findLoadedClass = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
99+
Method defineClass =
100+
ClassLoader.class.getDeclaredMethod(
101+
"defineClass", String.class, byte[].class, int.class, int.class);
102+
findLoadedClass.setAccessible(true);
103+
defineClass.setAccessible(true);
104+
return (name, bytes) -> {
105+
try {
106+
if (findLoadedClass.invoke(cl, name) == null) {
107+
defineClass.invoke(cl, name, bytes, 0, bytes.length);
108+
}
109+
} catch (ReflectiveOperationException e) {
110+
throw new RuntimeException(e);
111+
}
112+
};
113+
} catch (ReflectiveOperationException e) {
114+
return new HelperClassLoader(cl)::injectClass;
115+
}
116+
}
117+
93118
// build modules to test while single-threaded to match installer assumptions
94119
private static synchronized List<InstrumenterModule> toBeTested(
95120
ClassLoader instrumentationLoader, String muzzleDirective) {
@@ -114,7 +139,9 @@ static final class HelperClassLoader extends ClassLoader {
114139
}
115140

116141
public void injectClass(String name, byte[] bytecode) {
117-
defineClass(name, bytecode, 0, bytecode.length);
142+
if (findLoadedClass(name) == null) {
143+
defineClass(name, bytecode, 0, bytecode.length);
144+
}
118145
}
119146
}
120147

0 commit comments

Comments
 (0)