in apm-agent-core/src/main/java/co/elastic/apm/agent/bci/IndyBootstrap.java [428:513]
private static ConstantCallSite internalBootstrap(MethodHandles.Lookup lookup, String adviceMethodName, MethodType adviceMethodType, Object[] args) {
try {
if (callDepth.isNestedCallAndIncrement()) {
// avoid re-entrancy and stack overflow errors
// may happen when bootstrapping an instrumentation that also gets triggered during the bootstrap
// for example, adding correlation ids to the thread context when executing logger.debug.
if (!loggingForNestedCall.isNestedCallAndIncrement()) {
// We might be unlucky and cause an infinite recurison through logger()
// for this reason we have the loggingForNestedCall in place to prevent this recursion
logger().warn("Nested instrumented invokedynamic instruction linkage detected", new Throwable());
}
loggingForNestedCall.decrement();
return null;
}
String adviceClassName = (String) args[0];
int enter = (Integer) args[1];
Class<?> instrumentedType = (Class<?>) args[2];
String instrumentedMethodName = (String) args[3];
MethodHandle instrumentedMethod = args.length >= 5 ? (MethodHandle) args[4] : null;
ClassLoader instrumentationClassLoader = ElasticApmAgent.getInstrumentationClassLoader(adviceClassName);
ClassLoader targetClassLoader = lookup.lookupClass().getClassLoader();
ClassFileLocator classFileLocator;
List<String> pluginClasses = new ArrayList<>();
Map<String, List<String>> requiredModuleOpens = Collections.emptyMap();
boolean allowOtelLookupFromParent;
if (instrumentationClassLoader instanceof ExternalPluginClassLoader) {
allowOtelLookupFromParent = true;
List<String> externalPluginClasses = ((ExternalPluginClassLoader) instrumentationClassLoader).getClassNames();
for (String externalPluginClass : externalPluginClasses) {
if (// API classes have no dependencies and don't need to be loaded by an IndyPluginCL
!(externalPluginClass.startsWith("co.elastic.apm.api")) &&
!(externalPluginClass.startsWith("co.elastic.apm.opentracing"))
) {
pluginClasses.add(externalPluginClass);
}
}
File agentJarFile = ElasticApmAgent.getAgentJarFile();
if (agentJarFile == null) {
throw new IllegalStateException("External plugin cannot be applied - can't find agent jar");
}
classFileLocator = new ClassFileLocator.Compound(
ClassFileLocator.ForClassLoader.of(instrumentationClassLoader),
ClassFileLocator.ForJarFile.of(agentJarFile)
);
} else {
allowOtelLookupFromParent = false;
String pluginPackage = PluginClassLoaderRootPackageCustomizer.getPluginPackageFromClassName(adviceClassName);
pluginClasses.addAll(getClassNamesFromBundledPlugin(pluginPackage, instrumentationClassLoader));
requiredModuleOpens = ElasticApmAgent.getRequiredPluginModuleOpens(pluginPackage);
classFileLocator = ClassFileLocator.ForClassLoader.of(instrumentationClassLoader);
}
pluginClasses.add(LOOKUP_EXPOSER_CLASS_NAME);
ClassLoader pluginClassLoader = IndyPluginClassLoaderFactory.getOrCreatePluginClassLoader(
targetClassLoader,
pluginClasses,
// we provide the instrumentation class loader as the agent class loader, but it could actually be an
// ExternalPluginClassLoader, of which parent is the agent class loader, so this works as well.
instrumentationClassLoader,
classFileLocator,
isAnnotatedWith(named(GlobalState.class.getName()))
// if config classes would be loaded from the plugin CL,
// tracer.getConfig(Config.class) would return null when called from an advice as the classes are not the same
.or(nameContains("Config").and(hasSuperType(is(ConfigurationOptionProvider.class)))),
allowOtelLookupFromParent);
if (ElasticApmAgent.areModulesSupported() && !requiredModuleOpens.isEmpty()) {
boolean success = addRequiredModuleOpens(requiredModuleOpens, targetClassLoader, pluginClassLoader);
if (!success) {
logger().error("Cannot bootstrap advice because required modules could not be opened!");
return null;
}
}
Class<?> adviceInPluginCL = pluginClassLoader.loadClass(adviceClassName);
Class<LookupExposer> lookupExposer = (Class<LookupExposer>) pluginClassLoader.loadClass(LOOKUP_EXPOSER_CLASS_NAME);
// can't use MethodHandle.lookup(), see also https://github.com/elastic/apm-agent-java/issues/1450
MethodHandles.Lookup indyLookup = (MethodHandles.Lookup) lookupExposer.getMethod("getLookup").invoke(null);
// When calling findStatic now, the lookup class will be one that is loaded by the plugin class loader
MethodHandle methodHandle = indyLookup.findStatic(adviceInPluginCL, adviceMethodName, adviceMethodType);
return new ConstantCallSite(methodHandle);
} catch (Throwable e) {
logger().error(e.getMessage(), e);
return null;
} finally {
callDepth.decrement();
}
}