io.opentelemetry.javaagent.tooling.BytecodeWithUrl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of opentelemetry-muzzle Show documentation
Show all versions of opentelemetry-muzzle Show documentation
Instrumentation of Java libraries using OpenTelemetry.
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.tooling;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.utility.StreamDrainer;
/**
* Provides the bytecode and the original resource URL for loaded and not-yet loaded classes. The
* implementation is based on {@link net.bytebuddy.dynamic.ClassFileLocator.ForClassLoader}, with
* the difference that it preserves the original classfile resource URL. In addition {@link
* BytecodeWithUrl} created can be generated for runtime generated types, this class will take care
* of runtime generating URLs for those.
*/
public abstract class BytecodeWithUrl {
private BytecodeWithUrl() {}
/**
* Provides a URL pointing to the specific classfile.
*
* @return the URL
*/
public abstract URL getUrl();
/**
* Provides the bytecode of the class. The result is the same as calling {@link URL#openStream()}
* on {@link #getUrl()} and draining that stream.
*
* @return the bytecode of the class.
*/
public abstract byte[] getBytecode();
/**
* Creates a cached copy of this {@link BytecodeWithUrl}. The cached copy eagerly loads the
* bytecode, so that {@link #getBytecode()} is guaranteed to not cause any IO. This comes at the
* cost of a higher heap consumption, as the bytecode is kept in memory.
*
* @return an ClassFileSource implementing the described caching behaviour.
*/
public abstract BytecodeWithUrl cached();
/**
* Creates a {@link BytecodeWithUrl} for the class with the provided fully qualified name. The
* .class file for the provided classname must be available as a resource in the provided
* classloader. The class is guaranteed to not be loaded during this process.
*
* @param className the fully qualified name of the class to copy
* @param classLoader the classloader
* @return the ClassCopySource which can be used to copy the provided class to other classloaders.
*/
public static BytecodeWithUrl create(String className, ClassLoader classLoader) {
if (classLoader == null) {
throw new IllegalArgumentException(
"Copying classes from the bootstrap classloader is not supported!");
}
String classFileName = className.replace('.', '/') + ".class";
return new Lazy(classLoader, classFileName);
}
/**
* Same as {@link #create(String, ClassLoader)}, but easier to use for already loaded classes.
*
* @param loadedClass the class to copy
* @return the ClassCopySource which can be used to copy the provided class to other classloaders.
*/
public static BytecodeWithUrl create(Class> loadedClass) {
return create(loadedClass.getName(), loadedClass.getClassLoader());
}
/**
* Creates a {@link BytecodeWithUrl} for a runtime-generated type. It will also provide an
* artificially generated {@link URL} pointing to the in-memory bytecode.
*
* @param className the name of the class represented by the provided bytecode
* @param bytecode the bytecode of the class
* @return the {@link BytecodeWithUrl} referring to this dynamically generated class
*/
public static BytecodeWithUrl create(String className, byte[] bytecode) {
return new ForDynamicType(className, bytecode);
}
/**
* Invokes {@link #create(String, byte[])} for the provided dynamic type.
*
* @param dynamicType the type to generate the {@link BytecodeWithUrl}
* @return the {@link BytecodeWithUrl} referring to this dynamically generated type
*/
public static BytecodeWithUrl create(DynamicType.Unloaded> dynamicType) {
String className = dynamicType.getTypeDescription().getName();
return new ForDynamicType(className, dynamicType.getBytes());
}
private static class Lazy extends BytecodeWithUrl {
private final ClassLoader classLoader;
private final String resourceName;
private Lazy(ClassLoader classLoader, String resourceName) {
this.classLoader = classLoader;
this.resourceName = resourceName;
}
@Override
public URL getUrl() {
URL url = classLoader.getResource(resourceName);
if (url == null) {
throw new IllegalStateException(
"Classfile " + resourceName + " does not exist in the provided classloader!");
}
return url;
}
@Override
public byte[] getBytecode() {
try (InputStream bytecodeStream = getUrl().openStream()) {
return StreamDrainer.DEFAULT.drain(bytecodeStream);
} catch (IOException e) {
throw new IllegalStateException("Failed to read classfile URL", e);
}
}
@Override
public BytecodeWithUrl cached() {
return new Cached(this);
}
}
private static class Cached extends BytecodeWithUrl {
private final URL classFileUrl;
private final byte[] cachedByteCode;
private Cached(BytecodeWithUrl.Lazy from) {
classFileUrl = from.getUrl();
cachedByteCode = from.getBytecode();
}
@Override
public URL getUrl() {
return classFileUrl;
}
@Override
public byte[] getBytecode() {
return cachedByteCode;
}
@Override
public BytecodeWithUrl cached() {
return this;
}
}
private static class ForDynamicType extends BytecodeWithUrl {
private final byte[] byteCode;
private final String className;
private volatile URL generatedUrl;
private ForDynamicType(String className, byte[] byteCode) {
this.byteCode = byteCode;
this.className = className;
}
@Override
public URL getUrl() {
if (generatedUrl == null) {
synchronized (this) {
if (generatedUrl == null) {
generatedUrl = ByteArrayUrl.create(className, byteCode);
}
}
}
return generatedUrl;
}
@Override
public byte[] getBytecode() {
return byteCode;
}
@Override
public BytecodeWithUrl cached() {
return this; // this type already holds the bytecode in-memory
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy