Extract helper injection verification logic from plugin util into standalone class
MODIFY
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/matcher/MuzzleGradlePluginUtil.java
javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/matcher/MuzzleGradlePluginUtil.java
@@ -12,17 +12,12 @@ import io.opentelemetry.javaagent.extension.muzzle.ClassRef;
import io.opentelemetry.javaagent.extension.muzzle.FieldRef;
import io.opentelemetry.javaagent.extension.muzzle.MethodRef;
import io.opentelemetry.javaagent.extension.muzzle.Source;
-import io.opentelemetry.javaagent.tooling.HelperInjector;
import io.opentelemetry.javaagent.tooling.muzzle.ClassLoaderMatcher;
import io.opentelemetry.javaagent.tooling.muzzle.Mismatch;
import io.opentelemetry.javaagent.tooling.muzzle.ReferenceMatcher;
-import java.io.IOException;
-import java.util.Collection;
-import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
-import net.bytebuddy.dynamic.ClassFileLocator;
/** Entry point for the muzzle gradle plugin. */
// Runs in special classloader so tedious to provide access to the Gradle logger.
@@ -53,15 +48,14 @@ public final class MuzzleGradlePluginUtil {
* version passes different {@code userClassLoader}.
*/
public static void assertInstrumentationMuzzled(
- ClassLoader agentClassLoader, ClassLoader userClassLoader, boolean assertPass)
- throws Exception {
+ ClassLoader agentClassLoader, ClassLoader userClassLoader, boolean assertPass) {
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(agentClassLoader);
Map<String, List<Mismatch>> allMismatches;
try {
- allMismatches = ClassLoaderMatcher.matchesAll(userClassLoader);
+ allMismatches = ClassLoaderMatcher.matchesAll(userClassLoader, assertPass);
allMismatches.forEach(
(moduleName, mismatches) -> {
@@ -84,27 +78,6 @@ public final class MuzzleGradlePluginUtil {
Thread.currentThread().setContextClassLoader(contextClassLoader);
}
- // run helper injector on all instrumentation modules
- if (assertPass) {
- for (InstrumentationModule instrumentationModule :
- ServiceLoader.load(InstrumentationModule.class, agentClassLoader)) {
- try {
- // verify helper injector works
- List<String> allHelperClasses = instrumentationModule.getMuzzleHelperClassNames();
- if (!allHelperClasses.isEmpty()) {
- new HelperInjector(
- MuzzleGradlePluginUtil.class.getSimpleName(),
- createHelperMap(allHelperClasses, agentClassLoader))
- .transform(null, null, userClassLoader, null);
- }
- } catch (RuntimeException e) {
- System.err.println(
- "FAILED HELPER INJECTION. Are Helpers being injected in the correct order?");
- throw e;
- }
- }
- }
-
int validatedModulesCount = allMismatches.size();
if (validatedModulesCount == 0) {
String errorMessage = "Did not found any InstrumentationModule to validate!";
@@ -113,17 +86,6 @@ public final class MuzzleGradlePluginUtil {
}
}
- private static Map<String, byte[]> createHelperMap(
- Collection<String> helperClassNames, ClassLoader agentClassLoader) throws IOException {
- Map<String, byte[]> helperMap = new LinkedHashMap<>(helperClassNames.size());
- for (String helperName : helperClassNames) {
- ClassFileLocator locator = ClassFileLocator.ForClassLoader.of(agentClassLoader);
- byte[] classBytes = locator.locate(helperName).resolve();
- helperMap.put(helperName, classBytes);
- }
- return helperMap;
- }
-
/**
* Prints all references from all instrumentation modules present in the passed {@code
* instrumentationClassLoader}.

MODIFY
muzzle/src/main/java/io/opentelemetry/javaagent/tooling/HelperInjector.java
muzzle/src/main/java/io/opentelemetry/javaagent/tooling/HelperInjector.java
@@ -88,6 +88,7 @@ public class HelperInjector implements Transformer {
String requestingName,
List<String> helperClassNames,
List<String> helperResourceNames,
+ // TODO can this be replaced with the context classloader?
ClassLoader helpersSource,
Instrumentation instrumentation) {
this.requestingName = requestingName;
@@ -98,11 +99,6 @@ public class HelperInjector implements Transformer {
this.instrumentation = instrumentation;
}
- /** Must be used ONLY by gradle muzzle verification plugin. */
- public HelperInjector(String requestingName, Map<String, byte[]> helperMap) {
- this(requestingName, helperMap, null);
- }
-
private HelperInjector(
String requestingName, Map<String, byte[]> helperMap, Instrumentation instrumentation) {
this.requestingName = requestingName;

MODIFY
muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/ClassLoaderMatcher.java
muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/ClassLoaderMatcher.java
package io.opentelemetry.javaagent.tooling.muzzle;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
+import io.opentelemetry.javaagent.tooling.HelperInjector;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -23,16 +24,18 @@ public class ClassLoaderMatcher {
/**
* For all {@link InstrumentationModule}s found in the current thread's context classloader calls
- * {@link #matches(InstrumentationModule, ClassLoader)} and returns the aggregated result.
+ * {@link #matches(InstrumentationModule, ClassLoader, boolean)} and returns the aggregated
+ * result.
*
* <p>The returned map will be empty if and only if no instrumentation modules were found.
*/
- public static Map<String, List<Mismatch>> matchesAll(ClassLoader classLoader) {
+ public static Map<String, List<Mismatch>> matchesAll(
+ ClassLoader classLoader, boolean injectHelpers) {
Map<String, List<Mismatch>> result = new HashMap<>();
ServiceLoader.load(InstrumentationModule.class)
.forEach(
module -> {
- result.put(module.getClass().getName(), matches(module, classLoader));
+ result.put(module.getClass().getName(), matches(module, classLoader, injectHelpers));
});
return result;
}
@@ -42,19 +45,55 @@ public class ClassLoaderMatcher {
* InstrumentationModule} and what the given ClassLoader can provide.
*/
private static List<Mismatch> matches(
+ InstrumentationModule instrumentationModule, ClassLoader classLoader, boolean injectHelpers) {
+ List<Mismatch> mismatches = checkReferenceMatcher(instrumentationModule, classLoader);
+ mismatches = checkModuleClassLoaderMatcher(instrumentationModule, classLoader, mismatches);
+ if (injectHelpers) {
+ mismatches = checkHelperInjection(instrumentationModule, classLoader, mismatches);
+ }
+ return mismatches;
+ }
+
+ private static List<Mismatch> checkReferenceMatcher(
InstrumentationModule instrumentationModule, ClassLoader classLoader) {
ReferenceMatcher muzzle =
new ReferenceMatcher(
instrumentationModule.getMuzzleHelperClassNames(),
instrumentationModule.getMuzzleReferences(),
instrumentationModule::isHelperClass);
- List<Mismatch> mismatches = muzzle.getMismatchedReferenceSources(classLoader);
+ return muzzle.getMismatchedReferenceSources(classLoader);
+ }
+ private static List<Mismatch> checkModuleClassLoaderMatcher(
+ InstrumentationModule instrumentationModule,
+ ClassLoader classLoader,
+ List<Mismatch> mismatches) {
if (!instrumentationModule.classLoaderMatcher().matches(classLoader)) {
mismatches =
ReferenceMatcher.add(mismatches, new Mismatch.InstrumentationModuleClassLoaderMismatch());
}
+ return mismatches;
+ }
+ private static List<Mismatch> checkHelperInjection(
+ InstrumentationModule instrumentationModule,
+ ClassLoader classLoader,
+ List<Mismatch> mismatches) {
+ try {
+ // verify helper injector works
+ List<String> allHelperClasses = instrumentationModule.getMuzzleHelperClassNames();
+ if (!allHelperClasses.isEmpty()) {
+ new HelperInjector(
+ instrumentationModule.instrumentationName(),
+ allHelperClasses,
+ instrumentationModule.helperResourceNames(),
+ Thread.currentThread().getContextClassLoader(),
+ null)
+ .transform(null, null, classLoader, null);
+ }
+ } catch (RuntimeException e) {
+ mismatches = ReferenceMatcher.add(mismatches, new Mismatch.HelperClassesInjectionError());
+ }
return mismatches;
}

MODIFY
muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/Mismatch.java
muzzle/src/main/java/io/opentelemetry/javaagent/tooling/muzzle/Mismatch.java
@@ -183,4 +183,20 @@ public abstract class Mismatch {
return "InstrumentationModule classloader check";
}
}
+
+ /**
+ * Represents failure to inject {@link InstrumentationModule#getMuzzleHelperClassNames()} into
+ * some classloader.
+ */
+ public static class HelperClassesInjectionError extends Mismatch {
+
+ public HelperClassesInjectionError() {
+ super(Collections.emptyList());
+ }
+
+ @Override
+ String getMismatchDetails() {
+ return "Failed to inject helper classes. Are Helpers being injected in the correct order?";
+ }
+ }
}

Extract helper injection verification logic from plugin util into standalone class (#3688)