Support URLClassLoader addURL (#2772)
This commit is contained in:
parent
0f60b44b7c
commit
117d38a01b
|
@ -0,0 +1,8 @@
|
|||
ext.skipPublish = true
|
||||
|
||||
apply from: "$rootDir/gradle/instrumentation.gradle"
|
||||
|
||||
dependencies {
|
||||
testImplementation group: "org.apache.commons", name: "commons-lang3", version: "3.12.0"
|
||||
testImplementation group: "commons-io", name: "commons-io", version: "2.8.0"
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package instrumentation;
|
||||
|
||||
import static io.opentelemetry.javaagent.tooling.bytebuddy.matcher.ClassLoaderMatcher.hasClassesNamed;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import io.opentelemetry.javaagent.tooling.InstrumentationModule;
|
||||
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
@AutoService(InstrumentationModule.class)
|
||||
public class TestInstrumentationModule extends InstrumentationModule {
|
||||
public TestInstrumentationModule() {
|
||||
super("test-instrumentation");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TypeInstrumentation> typeInstrumentations() {
|
||||
return singletonList(new TestTypeInstrumentation());
|
||||
}
|
||||
|
||||
public static class TestTypeInstrumentation implements TypeInstrumentation {
|
||||
@Override
|
||||
public ElementMatcher<ClassLoader> classLoaderOptimization() {
|
||||
return hasClassesNamed("org.apache.commons.lang3.SystemUtils");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementMatcher<? super TypeDescription> typeMatcher() {
|
||||
return named("org.apache.commons.lang3.SystemUtils");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||
return Collections.singletonMap(named("getHostName"), MarkInstrumentedAdvice.class.getName());
|
||||
}
|
||||
}
|
||||
|
||||
public static class MarkInstrumentedAdvice {
|
||||
@Advice.OnMethodExit
|
||||
public static void methodExit(@Advice.Return(readOnly = false) String hostName) {
|
||||
hostName = "not-the-host-name";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification
|
||||
import org.apache.commons.io.IOUtils
|
||||
import org.apache.commons.lang3.SystemUtils
|
||||
|
||||
class AddUrlTest extends AgentInstrumentationSpecification {
|
||||
|
||||
def "should instrument class after it is loaded via addURL"() {
|
||||
given:
|
||||
TestURLClassLoader loader = new TestURLClassLoader()
|
||||
|
||||
when:
|
||||
// this is just to verify the assumption that TestURLClassLoader is not finding SystemUtils via
|
||||
// the test class path (in which case the verification below would not be very meaningful)
|
||||
loader.loadClass(SystemUtils.getName())
|
||||
|
||||
then:
|
||||
thrown ClassNotFoundException
|
||||
|
||||
when:
|
||||
// loading a class in the URLClassLoader in order to trigger
|
||||
// a negative cache hit on org.apache.commons.lang3.SystemUtils
|
||||
loader.addURL(IOUtils.getProtectionDomain().getCodeSource().getLocation())
|
||||
loader.loadClass(IOUtils.getName())
|
||||
|
||||
loader.addURL(SystemUtils.getProtectionDomain().getCodeSource().getLocation())
|
||||
def clazz = loader.loadClass(SystemUtils.getName())
|
||||
|
||||
then:
|
||||
clazz.getClassLoader() == loader
|
||||
clazz.getMethod("getHostName").invoke(null) == "not-the-host-name"
|
||||
}
|
||||
|
||||
static class TestURLClassLoader extends URLClassLoader {
|
||||
|
||||
TestURLClassLoader() {
|
||||
super(new URL[0], (ClassLoader) null)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@ public class ClassLoaderInstrumentationModule extends InstrumentationModule {
|
|||
public List<TypeInstrumentation> typeInstrumentations() {
|
||||
return asList(
|
||||
new ClassLoaderInstrumentation(),
|
||||
new UrlClassLoaderInstrumentation(),
|
||||
new ProxyInstrumentation(),
|
||||
new ResourceInjectionInstrumentation());
|
||||
}
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.javaclassloader;
|
||||
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isProtected;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||
|
||||
import io.opentelemetry.javaagent.bootstrap.ClassLoaderMatcherCacheHolder;
|
||||
import io.opentelemetry.javaagent.tooling.TypeInstrumentation;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Map;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
public class UrlClassLoaderInstrumentation implements TypeInstrumentation {
|
||||
|
||||
@Override
|
||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||
return named("java.net.URLClassLoader");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||
return singletonMap(
|
||||
isMethod()
|
||||
.and(named("addURL"))
|
||||
.and(takesArguments(1))
|
||||
.and(takesArgument(0, URL.class))
|
||||
.and(isProtected())
|
||||
.and(not(isStatic())),
|
||||
UrlClassLoaderInstrumentation.class.getName() + "$InvalidateClassLoaderMatcher");
|
||||
}
|
||||
|
||||
public static class InvalidateClassLoaderMatcher {
|
||||
@Advice.OnMethodExit(suppress = Throwable.class)
|
||||
public static void onExit(@Advice.This URLClassLoader loader) {
|
||||
ClassLoaderMatcherCacheHolder.invalidateAllCachesForClassLoader(loader);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.bootstrap;
|
||||
|
||||
import io.opentelemetry.instrumentation.api.caching.Cache;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.checkerframework.checker.lock.qual.GuardedBy;
|
||||
|
||||
/**
|
||||
* A holder of all ClassLoaderMatcher caches. We store them in the bootstrap classloader so that
|
||||
* instrumentation can invalidate the ClassLoaderMatcher for a particular ClassLoader, e.g. when
|
||||
* {@link java.net.URLClassLoader#addURL(URL)} is called.
|
||||
*/
|
||||
public class ClassLoaderMatcherCacheHolder {
|
||||
|
||||
@GuardedBy("allCaches")
|
||||
private static final List<Cache<ClassLoader, Boolean>> allCaches = new ArrayList<>();
|
||||
|
||||
private ClassLoaderMatcherCacheHolder() {}
|
||||
|
||||
public static void addCache(Cache<ClassLoader, Boolean> cache) {
|
||||
synchronized (allCaches) {
|
||||
allCaches.add(cache);
|
||||
}
|
||||
}
|
||||
|
||||
public static void invalidateAllCachesForClassLoader(ClassLoader loader) {
|
||||
synchronized (allCaches) {
|
||||
for (Cache<ClassLoader, Boolean> cache : allCaches) {
|
||||
cache.remove(loader);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@
|
|||
package io.opentelemetry.javaagent.tooling.bytebuddy.matcher;
|
||||
|
||||
import io.opentelemetry.instrumentation.api.caching.Cache;
|
||||
import io.opentelemetry.javaagent.bootstrap.ClassLoaderMatcherCacheHolder;
|
||||
import io.opentelemetry.javaagent.instrumentation.api.internal.InClassLoaderMatcher;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
|
@ -43,6 +44,7 @@ public final class ClassLoaderMatcher {
|
|||
for (int i = 0; i < resources.length; i++) {
|
||||
resources[i] = resources[i].replace(".", "/") + ".class";
|
||||
}
|
||||
ClassLoaderMatcherCacheHolder.addCache(cache);
|
||||
}
|
||||
|
||||
private boolean hasResources(ClassLoader cl) {
|
||||
|
|
|
@ -118,7 +118,9 @@ public class GlobalIgnoresMatcher<T extends TypeDescription>
|
|||
}
|
||||
|
||||
if (name.startsWith("java.")) {
|
||||
if (name.equals("java.net.URL") || name.equals("java.net.HttpURLConnection")) {
|
||||
if (name.equals("java.net.URL")
|
||||
|| name.equals("java.net.HttpURLConnection")
|
||||
|| name.equals("java.net.URLClassLoader")) {
|
||||
return false;
|
||||
}
|
||||
if (name.startsWith("java.rmi.") || name.startsWith("java.util.concurrent.")) {
|
||||
|
|
|
@ -86,6 +86,7 @@ include ':instrumentation:cassandra:cassandra-3.0:javaagent'
|
|||
include ':instrumentation:cassandra:cassandra-4.0:javaagent'
|
||||
include ':instrumentation:cdi-testing'
|
||||
include ':instrumentation:classloaders:javaagent'
|
||||
include ':instrumentation:classloaders:javaagent-integration-tests'
|
||||
include ':instrumentation:classloaders:javaagent-unittests'
|
||||
include ':instrumentation:couchbase:couchbase-2.0:javaagent'
|
||||
include ':instrumentation:couchbase:couchbase-2.0:javaagent-unittests'
|
||||
|
|
Loading…
Reference in New Issue