opentelemetry-java-instrume.../agent-bootstrap/src/main/java/io/opentelemetry/auto/bootstrap/AgentClassLoader.java

136 lines
4.3 KiB
Java

/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.auto.bootstrap;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import lombok.extern.slf4j.Slf4j;
/**
* Classloader used to run the core agent.
*
* <p>It is built around the concept of a jar inside another jar. This classloader loads the files
* of the internal jar to load classes and resources.
*/
@Slf4j
public class AgentClassLoader extends URLClassLoader {
static {
ClassLoader.registerAsParallelCapable();
}
private static final String AGENT_INITIALIZER_JAR = System.getProperty("ota.initializer.jar", "");
// Calling java.lang.instrument.Instrumentation#appendToBootstrapClassLoaderSearch
// adds a jar to the bootstrap class lookup, but not to the resource lookup.
// As a workaround, we keep a reference to the bootstrap jar
// to use only for resource lookups.
private final BootstrapClassLoaderProxy bootstrapProxy;
/**
* Construct a new AgentClassLoader
*
* @param bootstrapJarLocation Used for resource lookups.
* @param internalJarFileName File name of the internal jar
* @param parent Classloader parent. Should null (bootstrap), or the platform classloader for java
* 9+.
*/
public AgentClassLoader(
final URL bootstrapJarLocation, final String internalJarFileName, final ClassLoader parent) {
super(new URL[] {}, parent);
// some tests pass null
bootstrapProxy =
bootstrapJarLocation == null
? new BootstrapClassLoaderProxy(new URL[0])
: new BootstrapClassLoaderProxy(new URL[] {bootstrapJarLocation});
try {
// The fields of the URL are mostly dummy. InternalJarURLHandler is the only important
// field. If extending this class from Classloader instead of URLClassloader required less
// boilerplate it could be used and the need for dummy fields would be reduced
final URL internalJarURL =
new URL(
"x-internal-jar",
null,
0,
"/",
new InternalJarURLHandler(internalJarFileName, bootstrapJarLocation));
addURL(internalJarURL);
} catch (final MalformedURLException e) {
// This can't happen with current URL constructor
log.error("URL malformed. Unsupported JDK?", e);
}
if (!AGENT_INITIALIZER_JAR.isEmpty()) {
final URL url;
try {
url = new File(AGENT_INITIALIZER_JAR).toURI().toURL();
} catch (MalformedURLException e) {
log.warn(
"Filename could not be parsed: "
+ AGENT_INITIALIZER_JAR
+ ". Initializer is not installed");
return;
}
addURL(url);
}
}
@Override
public URL getResource(final String resourceName) {
final URL bootstrapResource = bootstrapProxy.getResource(resourceName);
if (null == bootstrapResource) {
return super.getResource(resourceName);
} else {
return bootstrapResource;
}
}
public BootstrapClassLoaderProxy getBootstrapProxy() {
return bootstrapProxy;
}
/**
* A stand-in for the bootstrap classloader. Used to look up bootstrap resources and resources
* appended by instrumentation.
*
* <p>This class is thread safe.
*/
public static final class BootstrapClassLoaderProxy extends URLClassLoader {
static {
ClassLoader.registerAsParallelCapable();
}
public BootstrapClassLoaderProxy(final URL[] urls) {
super(urls, null);
}
@Override
public void addURL(final URL url) {
super.addURL(url);
}
@Override
protected Class<?> findClass(final String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
}
}