Versioned helper resources, part 2 (#3880)
* Versioned helper resources, part 2 * Remove accidentally added javadoc param * Spotless * Fix test
This commit is contained in:
parent
c96af0d51a
commit
5ff7901efc
|
@ -58,15 +58,15 @@ public final class InstrumentationModuleInstaller {
|
||||||
return parentAgentBuilder;
|
return parentAgentBuilder;
|
||||||
}
|
}
|
||||||
List<String> helperClassNames = instrumentationModule.getMuzzleHelperClassNames();
|
List<String> helperClassNames = instrumentationModule.getMuzzleHelperClassNames();
|
||||||
HelperResourceBuilderImpl helperResources = new HelperResourceBuilderImpl();
|
HelperResourceBuilderImpl helperResourceBuilder = new HelperResourceBuilderImpl();
|
||||||
List<String> helperResourceNames = instrumentationModule.helperResourceNames();
|
List<String> helperResourceNames = instrumentationModule.helperResourceNames();
|
||||||
for (String helperResourceName : helperResourceNames) {
|
for (String helperResourceName : helperResourceNames) {
|
||||||
helperResources.register(helperResourceName);
|
helperResourceBuilder.register(helperResourceName);
|
||||||
}
|
}
|
||||||
instrumentationModule.registerHelperResources(helperResources);
|
instrumentationModule.registerHelperResources(helperResourceBuilder);
|
||||||
List<TypeInstrumentation> typeInstrumentations = instrumentationModule.typeInstrumentations();
|
List<TypeInstrumentation> typeInstrumentations = instrumentationModule.typeInstrumentations();
|
||||||
if (typeInstrumentations.isEmpty()) {
|
if (typeInstrumentations.isEmpty()) {
|
||||||
if (!helperClassNames.isEmpty() || !helperResources.getResourcePathMappings().isEmpty()) {
|
if (!helperClassNames.isEmpty() || !helperResourceBuilder.getResources().isEmpty()) {
|
||||||
logger.warn(
|
logger.warn(
|
||||||
"Helper classes and resources won't be injected if no types are instrumented: {}",
|
"Helper classes and resources won't be injected if no types are instrumented: {}",
|
||||||
instrumentationModule.instrumentationName());
|
instrumentationModule.instrumentationName());
|
||||||
|
@ -82,7 +82,7 @@ public final class InstrumentationModuleInstaller {
|
||||||
new HelperInjector(
|
new HelperInjector(
|
||||||
instrumentationModule.instrumentationName(),
|
instrumentationModule.instrumentationName(),
|
||||||
helperClassNames,
|
helperClassNames,
|
||||||
helperResources.getResourcePathMappings(),
|
helperResourceBuilder.getResources(),
|
||||||
Utils.getExtensionsClassLoader(),
|
Utils.getExtensionsClassLoader(),
|
||||||
instrumentation);
|
instrumentation);
|
||||||
InstrumentationContextProvider contextProvider =
|
InstrumentationContextProvider contextProvider =
|
||||||
|
|
|
@ -28,7 +28,7 @@ class HelperInjectionTest extends Specification {
|
||||||
ClassLoader helpersSourceLoader = new URLClassLoader(helpersSourceUrls)
|
ClassLoader helpersSourceLoader = new URLClassLoader(helpersSourceUrls)
|
||||||
|
|
||||||
String helperClassName = HelperInjectionTest.getPackage().getName() + '.HelperClass'
|
String helperClassName = HelperInjectionTest.getPackage().getName() + '.HelperClass'
|
||||||
HelperInjector injector = new HelperInjector("test", [helperClassName], [:], helpersSourceLoader, null)
|
HelperInjector injector = new HelperInjector("test", [helperClassName], [], helpersSourceLoader, null)
|
||||||
AtomicReference<URLClassLoader> emptyLoader = new AtomicReference<>(new URLClassLoader(new URL[0], (ClassLoader) null))
|
AtomicReference<URLClassLoader> emptyLoader = new AtomicReference<>(new URLClassLoader(new URL[0], (ClassLoader) null))
|
||||||
|
|
||||||
when:
|
when:
|
||||||
|
@ -60,7 +60,7 @@ class HelperInjectionTest extends Specification {
|
||||||
ByteBuddyAgent.install()
|
ByteBuddyAgent.install()
|
||||||
AgentInstaller.installBytebuddyAgent(ByteBuddyAgent.getInstrumentation())
|
AgentInstaller.installBytebuddyAgent(ByteBuddyAgent.getInstrumentation())
|
||||||
String helperClassName = HelperInjectionTest.getPackage().getName() + '.HelperClass'
|
String helperClassName = HelperInjectionTest.getPackage().getName() + '.HelperClass'
|
||||||
HelperInjector injector = new HelperInjector("test", [helperClassName], [:], this.class.classLoader, ByteBuddyAgent.getInstrumentation())
|
HelperInjector injector = new HelperInjector("test", [helperClassName], [], this.class.classLoader, ByteBuddyAgent.getInstrumentation())
|
||||||
URLClassLoader bootstrapChild = new URLClassLoader(new URL[0], (ClassLoader) null)
|
URLClassLoader bootstrapChild = new URLClassLoader(new URL[0], (ClassLoader) null)
|
||||||
|
|
||||||
when:
|
when:
|
||||||
|
|
|
@ -9,6 +9,9 @@ dependencies {
|
||||||
// Only used during compilation by bytebuddy plugin
|
// Only used during compilation by bytebuddy plugin
|
||||||
compileOnly("com.google.guava:guava")
|
compileOnly("com.google.guava:guava")
|
||||||
|
|
||||||
|
compileOnly("com.google.auto.value:auto-value-annotations")
|
||||||
|
annotationProcessor("com.google.auto.value:auto-value")
|
||||||
|
|
||||||
api("net.bytebuddy:byte-buddy")
|
api("net.bytebuddy:byte-buddy")
|
||||||
|
|
||||||
implementation(project(":javaagent-bootstrap"))
|
implementation(project(":javaagent-bootstrap"))
|
||||||
|
|
|
@ -7,6 +7,7 @@ package io.opentelemetry.javaagent.tooling;
|
||||||
|
|
||||||
import io.opentelemetry.instrumentation.api.caching.Cache;
|
import io.opentelemetry.instrumentation.api.caching.Cache;
|
||||||
import io.opentelemetry.javaagent.bootstrap.HelperResources;
|
import io.opentelemetry.javaagent.bootstrap.HelperResources;
|
||||||
|
import io.opentelemetry.javaagent.tooling.muzzle.HelperResource;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.instrument.Instrumentation;
|
import java.lang.instrument.Instrumentation;
|
||||||
|
@ -63,7 +64,7 @@ public class HelperInjector implements Transformer {
|
||||||
private final String requestingName;
|
private final String requestingName;
|
||||||
|
|
||||||
private final Set<String> helperClassNames;
|
private final Set<String> helperClassNames;
|
||||||
private final Map<String, String> helperResourcePathMappings;
|
private final List<HelperResource> helperResources;
|
||||||
@Nullable private final ClassLoader helpersSource;
|
@Nullable private final ClassLoader helpersSource;
|
||||||
@Nullable private final Instrumentation instrumentation;
|
@Nullable private final Instrumentation instrumentation;
|
||||||
private final Map<String, byte[]> dynamicTypeMap = new LinkedHashMap<>();
|
private final Map<String, byte[]> dynamicTypeMap = new LinkedHashMap<>();
|
||||||
|
@ -87,14 +88,14 @@ public class HelperInjector implements Transformer {
|
||||||
public HelperInjector(
|
public HelperInjector(
|
||||||
String requestingName,
|
String requestingName,
|
||||||
List<String> helperClassNames,
|
List<String> helperClassNames,
|
||||||
Map<String, String> helperResourcePathMappings,
|
List<HelperResource> helperResources,
|
||||||
// TODO can this be replaced with the context classloader?
|
// TODO can this be replaced with the context classloader?
|
||||||
ClassLoader helpersSource,
|
ClassLoader helpersSource,
|
||||||
Instrumentation instrumentation) {
|
Instrumentation instrumentation) {
|
||||||
this.requestingName = requestingName;
|
this.requestingName = requestingName;
|
||||||
|
|
||||||
this.helperClassNames = new LinkedHashSet<>(helperClassNames);
|
this.helperClassNames = new LinkedHashSet<>(helperClassNames);
|
||||||
this.helperResourcePathMappings = helperResourcePathMappings;
|
this.helperResources = helperResources;
|
||||||
this.helpersSource = helpersSource;
|
this.helpersSource = helpersSource;
|
||||||
this.instrumentation = instrumentation;
|
this.instrumentation = instrumentation;
|
||||||
}
|
}
|
||||||
|
@ -106,7 +107,7 @@ public class HelperInjector implements Transformer {
|
||||||
this.helperClassNames = helperMap.keySet();
|
this.helperClassNames = helperMap.keySet();
|
||||||
this.dynamicTypeMap.putAll(helperMap);
|
this.dynamicTypeMap.putAll(helperMap);
|
||||||
|
|
||||||
this.helperResourcePathMappings = Collections.emptyMap();
|
this.helperResources = Collections.emptyList();
|
||||||
this.helpersSource = null;
|
this.helpersSource = null;
|
||||||
this.instrumentation = instrumentation;
|
this.instrumentation = instrumentation;
|
||||||
}
|
}
|
||||||
|
@ -149,19 +150,20 @@ public class HelperInjector implements Transformer {
|
||||||
classLoader = injectHelperClasses(typeDescription, classLoader, module);
|
classLoader = injectHelperClasses(typeDescription, classLoader, module);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (helpersSource != null && !helperResourcePathMappings.isEmpty()) {
|
if (helpersSource != null && !helperResources.isEmpty()) {
|
||||||
for (Map.Entry<String, String> entry : helperResourcePathMappings.entrySet()) {
|
for (HelperResource helperResource : helperResources) {
|
||||||
String agentResourcePath = entry.getValue();
|
URL resource = helpersSource.getResource(helperResource.getAgentPath());
|
||||||
String applicationResourcePath = entry.getKey();
|
|
||||||
URL resource = helpersSource.getResource(agentResourcePath);
|
|
||||||
if (resource == null) {
|
if (resource == null) {
|
||||||
logger.debug("Helper resource {} requested but not found.", agentResourcePath);
|
logger.debug(
|
||||||
|
"Helper resource {} requested but not found.", helperResource.getAgentPath());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
"Injecting resource onto classloader {} -> {}", classLoader, applicationResourcePath);
|
"Injecting resource onto classloader {} -> {}",
|
||||||
HelperResources.register(classLoader, applicationResourcePath, resource);
|
classLoader,
|
||||||
|
helperResource.getApplicationPath());
|
||||||
|
HelperResources.register(classLoader, helperResource.getApplicationPath(), resource);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,17 +82,17 @@ public class ClassLoaderMatcher {
|
||||||
try {
|
try {
|
||||||
// verify helper injector works
|
// verify helper injector works
|
||||||
List<String> allHelperClasses = instrumentationModule.getMuzzleHelperClassNames();
|
List<String> allHelperClasses = instrumentationModule.getMuzzleHelperClassNames();
|
||||||
HelperResourceBuilderImpl helperResources = new HelperResourceBuilderImpl();
|
HelperResourceBuilderImpl helperResourceBuilder = new HelperResourceBuilderImpl();
|
||||||
List<String> helperResourceNames = instrumentationModule.helperResourceNames();
|
List<String> helperResourceNames = instrumentationModule.helperResourceNames();
|
||||||
for (String helperResourceName : helperResourceNames) {
|
for (String helperResourceName : helperResourceNames) {
|
||||||
helperResources.register(helperResourceName);
|
helperResourceBuilder.register(helperResourceName);
|
||||||
}
|
}
|
||||||
instrumentationModule.registerHelperResources(helperResources);
|
instrumentationModule.registerHelperResources(helperResourceBuilder);
|
||||||
if (!allHelperClasses.isEmpty()) {
|
if (!allHelperClasses.isEmpty()) {
|
||||||
new HelperInjector(
|
new HelperInjector(
|
||||||
instrumentationModule.instrumentationName(),
|
instrumentationModule.instrumentationName(),
|
||||||
allHelperClasses,
|
allHelperClasses,
|
||||||
helperResources.getResourcePathMappings(),
|
helperResourceBuilder.getResources(),
|
||||||
Thread.currentThread().getContextClassLoader(),
|
Thread.currentThread().getContextClassLoader(),
|
||||||
null)
|
null)
|
||||||
.transform(null, null, classLoader, null);
|
.transform(null, null, classLoader, null);
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.javaagent.tooling.muzzle;
|
||||||
|
|
||||||
|
import com.google.auto.value.AutoValue;
|
||||||
|
|
||||||
|
@AutoValue
|
||||||
|
public abstract class HelperResource {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new helper resource object.
|
||||||
|
*
|
||||||
|
* @param applicationPath The path in the user's class loader at which to inject the resource.
|
||||||
|
* @param agentPath The path in the agent class loader from which to get the content for the
|
||||||
|
* resource.
|
||||||
|
*/
|
||||||
|
public static HelperResource create(String applicationPath, String agentPath) {
|
||||||
|
return new AutoValue_HelperResource(applicationPath, agentPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The path in the user's class loader at which to inject the resource. */
|
||||||
|
public abstract String getApplicationPath();
|
||||||
|
|
||||||
|
/** The path in the agent class loader from which to get the content for the resource. */
|
||||||
|
public abstract String getAgentPath();
|
||||||
|
}
|
|
@ -6,30 +6,24 @@
|
||||||
package io.opentelemetry.javaagent.tooling.muzzle;
|
package io.opentelemetry.javaagent.tooling.muzzle;
|
||||||
|
|
||||||
import io.opentelemetry.javaagent.extension.instrumentation.HelperResourceBuilder;
|
import io.opentelemetry.javaagent.extension.instrumentation.HelperResourceBuilder;
|
||||||
import java.util.HashMap;
|
import java.util.ArrayList;
|
||||||
import java.util.Map;
|
import java.util.List;
|
||||||
|
|
||||||
public class HelperResourceBuilderImpl implements HelperResourceBuilder {
|
public class HelperResourceBuilderImpl implements HelperResourceBuilder {
|
||||||
|
|
||||||
private final Map<String, String> resourcePathMappings = new HashMap<>();
|
private final List<HelperResource> resources = new ArrayList<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void register(String resourcePath) {
|
public void register(String resourcePath) {
|
||||||
resourcePathMappings.put(resourcePath, resourcePath);
|
resources.add(HelperResource.create(resourcePath, resourcePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void register(String applicationResourcePath, String agentResourcePath) {
|
public void register(String applicationResourcePath, String agentResourcePath) {
|
||||||
resourcePathMappings.put(applicationResourcePath, agentResourcePath);
|
resources.add(HelperResource.create(applicationResourcePath, agentResourcePath));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public List<HelperResource> getResources() {
|
||||||
* Returns the registered mappings, where the keys are the paths in the user's class loader at
|
return resources;
|
||||||
* which to inject the resource ({@code applicationResourcePath}) and the values are the paths in
|
|
||||||
* the agent class loader from which to get the content for the resource ({@code
|
|
||||||
* agentResourcePath}).
|
|
||||||
*/
|
|
||||||
public Map<String, String> getResourcePathMappings() {
|
|
||||||
return resourcePathMappings;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,14 +74,28 @@ public final class ReferenceCollector {
|
||||||
*
|
*
|
||||||
* @param resource path to the resource file, same as in {@link ClassLoader#getResource(String)}
|
* @param resource path to the resource file, same as in {@link ClassLoader#getResource(String)}
|
||||||
* @see InstrumentationClassPredicate
|
* @see InstrumentationClassPredicate
|
||||||
|
* @deprecated Use {@link #collectReferencesFromResource(HelperResource)} instead.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void collectReferencesFromResource(String resource) {
|
public void collectReferencesFromResource(String resource) {
|
||||||
if (!isSpiFile(resource)) {
|
collectReferencesFromResource(HelperResource.create(resource, resource));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If passed {@code resource} path points to an SPI file (either Java {@link
|
||||||
|
* java.util.ServiceLoader} or AWS SDK {@code ExecutionInterceptor}) reads the file and adds every
|
||||||
|
* implementation as a reference, traversing the graph of classes until a non-instrumentation
|
||||||
|
* (external) class is encountered.
|
||||||
|
*
|
||||||
|
* @see InstrumentationClassPredicate
|
||||||
|
*/
|
||||||
|
public void collectReferencesFromResource(HelperResource helperResource) {
|
||||||
|
if (!isSpiFile(helperResource.getApplicationPath())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> spiImplementations = new ArrayList<>();
|
List<String> spiImplementations = new ArrayList<>();
|
||||||
try (InputStream stream = getResourceStream(resource)) {
|
try (InputStream stream = getResourceStream(helperResource.getAgentPath())) {
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(stream, UTF_8));
|
BufferedReader reader = new BufferedReader(new InputStreamReader(stream, UTF_8));
|
||||||
while (reader.ready()) {
|
while (reader.ready()) {
|
||||||
String line = reader.readLine();
|
String line = reader.readLine();
|
||||||
|
@ -90,7 +104,7 @@ public final class ReferenceCollector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new IllegalStateException("Error reading resource " + resource, e);
|
throw new IllegalStateException("Error reading resource " + helperResource.getAgentPath(), e);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitClassesAndCollectReferences(spiImplementations, /* startsFromAdviceClass= */ false);
|
visitClassesAndCollectReferences(spiImplementations, /* startsFromAdviceClass= */ false);
|
||||||
|
|
Loading…
Reference in New Issue