/*
* Copyright 2014 - 2020 Rafael Winterhalter
*
* 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 net.bytebuddy.dynamic;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
import net.bytebuddy.description.NamedElement;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
import net.bytebuddy.utility.JavaModule;
import net.bytebuddy.utility.JavaType;
import net.bytebuddy.utility.StreamDrainer;
import java.io.*;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.util.*;
import java.util.jar.JarFile;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import static net.bytebuddy.matcher.ElementMatchers.isChildOf;
Locates a class file or its byte array representation when it is given its type description.
/**
* Locates a class file or its byte array representation when it is given its type description.
*/
public interface ClassFileLocator extends Closeable {
The file extension for a Java class file.
/**
* The file extension for a Java class file.
*/
String CLASS_FILE_EXTENSION = ".class";
Locates the class file for a given type and returns the binary data of the class file.
Params: - name – The name of the type to locate a class file representation for.
Throws: - IOException – If reading a class file causes an error.
Returns: Any binary representation of the type which might be illegal.
/**
* Locates the class file for a given type and returns the binary data of the class file.
*
* @param name The name of the type to locate a class file representation for.
* @return Any binary representation of the type which might be illegal.
* @throws java.io.IOException If reading a class file causes an error.
*/
Resolution locate(String name) throws IOException;
Represents a class file as binary data.
/**
* Represents a class file as binary data.
*/
interface Resolution {
Checks if this binary representation is valid.
Returns: true
if this binary representation is valid.
/**
* Checks if this binary representation is valid.
*
* @return {@code true} if this binary representation is valid.
*/
boolean isResolved();
Finds the data of this binary representation. Calling this method is only legal for resolved instances.
For non-resolved instances, an exception is thrown.
Returns: The requested binary data. The returned array must not be altered.
/**
* Finds the data of this binary representation. Calling this method is only legal for resolved instances.
* For non-resolved instances, an exception is thrown.
*
* @return The requested binary data. The returned array must not be altered.
*/
byte[] resolve();
A canonical representation of an illegal binary representation.
/**
* A canonical representation of an illegal binary representation.
*/
@HashCodeAndEqualsPlugin.Enhance
class Illegal implements Resolution {
The name of the unresolved class file.
/**
* The name of the unresolved class file.
*/
private final String typeName;
Creates an illegal resolution for a class file.
Params: - typeName – The name of the unresolved class file.
/**
* Creates an illegal resolution for a class file.
*
* @param typeName The name of the unresolved class file.
*/
public Illegal(String typeName) {
this.typeName = typeName;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public boolean isResolved() {
return false;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public byte[] resolve() {
throw new IllegalStateException("Could not locate class file for " + typeName);
}
}
Represents a byte array as binary data.
/**
* Represents a byte array as binary data.
*/
@HashCodeAndEqualsPlugin.Enhance
class Explicit implements Resolution {
The represented data.
/**
* The represented data.
*/
private final byte[] binaryRepresentation;
Creates a new explicit resolution of a given array of binary data.
Params: - binaryRepresentation – The binary data to represent. The array must not be modified.
/**
* Creates a new explicit resolution of a given array of binary data.
*
* @param binaryRepresentation The binary data to represent. The array must not be modified.
*/
@SuppressFBWarnings(value = "EI_EXPOSE_REP2", justification = "The array is not to be modified by contract")
public Explicit(byte[] binaryRepresentation) {
this.binaryRepresentation = binaryRepresentation;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public boolean isResolved() {
return true;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
@SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "The array is not to be modified by contract")
public byte[] resolve() {
return binaryRepresentation;
}
}
}
A class file locator that cannot locate any class files.
/**
* A class file locator that cannot locate any class files.
*/
enum NoOp implements ClassFileLocator {
The singleton instance.
/**
* The singleton instance.
*/
INSTANCE;
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Resolution locate(String name) {
return new Resolution.Illegal(name);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public void close() {
/* do nothing */
}
}
A simple class file locator that returns class files from a selection of given types.
/**
* A simple class file locator that returns class files from a selection of given types.
*/
@HashCodeAndEqualsPlugin.Enhance
class Simple implements ClassFileLocator {
The class files that are known to this class file locator mapped by their type name.
/**
* The class files that are known to this class file locator mapped by their type name.
*/
private final Map<String, byte[]> classFiles;
Creates a new simple class file locator.
Params: - classFiles – The class files that are known to this class file locator mapped by their type name.
/**
* Creates a new simple class file locator.
*
* @param classFiles The class files that are known to this class file locator mapped by their type name.
*/
public Simple(Map<String, byte[]> classFiles) {
this.classFiles = classFiles;
}
Creates a class file locator for a single known type.
Params: - typeName – The name of the type.
- binaryRepresentation – The binary representation of the type.
Returns: An appropriate class file locator.
/**
* Creates a class file locator for a single known type.
*
* @param typeName The name of the type.
* @param binaryRepresentation The binary representation of the type.
* @return An appropriate class file locator.
*/
public static ClassFileLocator of(String typeName, byte[] binaryRepresentation) {
return new Simple(Collections.singletonMap(typeName, binaryRepresentation));
}
Creates a class file locator that represents all types of a dynamic type.
Params: - dynamicType – The dynamic type to represent.
Returns: A class file locator representing the dynamic type's types.
/**
* Creates a class file locator that represents all types of a dynamic type.
*
* @param dynamicType The dynamic type to represent.
* @return A class file locator representing the dynamic type's types.
*/
public static ClassFileLocator of(DynamicType dynamicType) {
return of(dynamicType.getAllTypes());
}
Creates a class file locator that represents all types of a dynamic type.
Params: - binaryRepresentations – The binary representation of all types.
Returns: A class file locator representing the dynamic type's types.
/**
* Creates a class file locator that represents all types of a dynamic type.
*
* @param binaryRepresentations The binary representation of all types.
* @return A class file locator representing the dynamic type's types.
*/
public static ClassFileLocator of(Map<TypeDescription, byte[]> binaryRepresentations) {
Map<String, byte[]> classFiles = new HashMap<String, byte[]>();
for (Map.Entry<TypeDescription, byte[]> entry : binaryRepresentations.entrySet()) {
classFiles.put(entry.getKey().getName(), entry.getValue());
}
return new Simple(classFiles);
}
Creates a class file locator of a map of resources where class files are mapped by their path and file extension.
Params: - binaryRepresentations – A map of resource names to their binary representation.
Returns: A class file locator that finds class files within the map.
/**
* Creates a class file locator of a map of resources where class files are mapped by their path and file extension.
*
* @param binaryRepresentations A map of resource names to their binary representation.
* @return A class file locator that finds class files within the map.
*/
public static ClassFileLocator ofResources(Map<String, byte[]> binaryRepresentations) {
Map<String, byte[]> classFiles = new HashMap<String, byte[]>();
for (Map.Entry<String, byte[]> entry : binaryRepresentations.entrySet()) {
if (entry.getKey().endsWith(CLASS_FILE_EXTENSION)) {
classFiles.put(entry.getKey().substring(0, entry.getKey().length() - CLASS_FILE_EXTENSION.length()).replace('/', '.'), entry.getValue());
}
}
return new Simple(classFiles);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Resolution locate(String name) {
byte[] binaryRepresentation = classFiles.get(name);
return binaryRepresentation == null
? new Resolution.Illegal(name)
: new Resolution.Explicit(binaryRepresentation);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public void close() {
/* do nothing */
}
}
A class file locator that queries a class loader for binary representations of class files.
Important: Even when calling Closeable.close()
on this class file locator, no underlying class loader is closed if it implements the Closeable
interface as this is typically not intended.
/**
* <p>
* A class file locator that queries a class loader for binary representations of class files.
* </p>
* <p>
* <b>Important</b>: Even when calling {@link Closeable#close()} on this class file locator, no underlying
* class loader is closed if it implements the {@link Closeable} interface as this is typically not intended.
* </p>
*/
@HashCodeAndEqualsPlugin.Enhance
class ForClassLoader implements ClassFileLocator {
A class loader that does not define resources of its own but allows querying for resources supplied by the boot loader.
/**
* A class loader that does not define resources of its own but allows querying for resources supplied by the boot loader.
*/
private static final ClassLoader BOOT_LOADER_PROXY = AccessController.doPrivileged(BootLoaderProxyCreationAction.INSTANCE);
The class loader to query.
/**
* The class loader to query.
*/
private final ClassLoader classLoader;
Creates a new class file locator for the given class loader.
Params: - classLoader – The class loader to query which must not be the bootstrap class loader, i.e.
null
.
/**
* Creates a new class file locator for the given class loader.
*
* @param classLoader The class loader to query which must not be the bootstrap class loader, i.e. {@code null}.
*/
protected ForClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
Creates a class file locator that queries the system class loader.
Returns: A class file locator that queries the system class loader.
/**
* Creates a class file locator that queries the system class loader.
*
* @return A class file locator that queries the system class loader.
*/
public static ClassFileLocator ofSystemLoader() {
return new ForClassLoader(ClassLoader.getSystemClassLoader());
}
Creates a class file locator that queries the plaform class loader or the extension class loader if the
current VM is not at least of version 9.
Returns: A class file locator that queries the plaform class loader or the extension class loader.
/**
* Creates a class file locator that queries the plaform class loader or the extension class loader if the
* current VM is not at least of version 9.
*
* @return A class file locator that queries the plaform class loader or the extension class loader.
*/
public static ClassFileLocator ofPlatformLoader() {
return of(ClassLoader.getSystemClassLoader().getParent());
}
Creates a class file locator that queries the boot loader.
Returns: A class file locator that queries the boot loader.
/**
* Creates a class file locator that queries the boot loader.
*
* @return A class file locator that queries the boot loader.
*/
public static ClassFileLocator ofBootLoader() {
return new ForClassLoader(BOOT_LOADER_PROXY);
}
Creates a class file locator for a given class loader.
Params: - classLoader – The class loader to be used which might be
null
to represent the bootstrap loader.
Returns: A corresponding source locator.
/**
* Creates a class file locator for a given class loader.
*
* @param classLoader The class loader to be used which might be {@code null} to represent the bootstrap loader.
* @return A corresponding source locator.
*/
public static ClassFileLocator of(ClassLoader classLoader) {
return new ForClassLoader(classLoader == null
? BOOT_LOADER_PROXY
: classLoader);
}
Attempts to create a binary representation of a loaded type by requesting data from its ClassLoader
. Params: - type – The type of interest.
Returns: The binary representation of the supplied type.
/**
* Attempts to create a binary representation of a loaded type by requesting data from its
* {@link java.lang.ClassLoader}.
*
* @param type The type of interest.
* @return The binary representation of the supplied type.
*/
public static byte[] read(Class<?> type) {
try {
ClassLoader classLoader = type.getClassLoader();
return locate(classLoader == null
? BOOT_LOADER_PROXY
: classLoader, TypeDescription.ForLoadedType.getName(type)).resolve();
} catch (IOException exception) {
throw new IllegalStateException("Cannot read class file for " + type, exception);
}
}
Attempts to create a binary representation of several loaded types by requesting data from their respective ClassLoader
s. Params: - type – The types of interest.
Returns: A mapping of the supplied types to their binary representation.
/**
* Attempts to create a binary representation of several loaded types by requesting
* data from their respective {@link java.lang.ClassLoader}s.
*
* @param type The types of interest.
* @return A mapping of the supplied types to their binary representation.
*/
public static Map<Class<?>, byte[]> read(Class<?>... type) {
return read(Arrays.asList(type));
}
Attempts to create a binary representation of several loaded types by requesting data from their respective ClassLoader
s. Params: - types – The types of interest.
Returns: A mapping of the supplied types to their binary representation.
/**
* Attempts to create a binary representation of several loaded types by requesting
* data from their respective {@link java.lang.ClassLoader}s.
*
* @param types The types of interest.
* @return A mapping of the supplied types to their binary representation.
*/
public static Map<Class<?>, byte[]> read(Collection<? extends Class<?>> types) {
Map<Class<?>, byte[]> result = new HashMap<Class<?>, byte[]>();
for (Class<?> type : types) {
result.put(type, read(type));
}
return result;
}
Attempts to create a binary representation of several loaded types by requesting data from their respective ClassLoader
s. Params: - type – The types of interest.
Returns: A mapping of the supplied types' names to their binary representation.
/**
* Attempts to create a binary representation of several loaded types by requesting
* data from their respective {@link java.lang.ClassLoader}s.
*
* @param type The types of interest.
* @return A mapping of the supplied types' names to their binary representation.
*/
public static Map<String, byte[]> readToNames(Class<?>... type) {
return readToNames(Arrays.asList(type));
}
Attempts to create a binary representation of several loaded types by requesting data from their respective ClassLoader
s. Params: - types – The types of interest.
Returns: A mapping of the supplied types' names to their binary representation.
/**
* Attempts to create a binary representation of several loaded types by requesting
* data from their respective {@link java.lang.ClassLoader}s.
*
* @param types The types of interest.
* @return A mapping of the supplied types' names to their binary representation.
*/
public static Map<String, byte[]> readToNames(Collection<? extends Class<?>> types) {
Map<String, byte[]> result = new HashMap<String, byte[]>();
for (Class<?> type : types) {
result.put(type.getName(), read(type));
}
return result;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Resolution locate(String name) throws IOException {
return locate(classLoader, name);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public void close() {
/* do nothing */
}
Locates the class file for the supplied type by requesting a resource from the class loader.
Params: - classLoader – The class loader to query for the resource.
- name – The name of the type for which to locate a class file.
Throws: - IOException – If reading the class file causes an exception.
Returns: A resolution for the class file.
/**
* Locates the class file for the supplied type by requesting a resource from the class loader.
*
* @param classLoader The class loader to query for the resource.
* @param name The name of the type for which to locate a class file.
* @return A resolution for the class file.
* @throws IOException If reading the class file causes an exception.
*/
protected static Resolution locate(ClassLoader classLoader, String name) throws IOException {
InputStream inputStream = classLoader.getResourceAsStream(name.replace('.', '/') + CLASS_FILE_EXTENSION);
if (inputStream != null) {
try {
return new Resolution.Explicit(StreamDrainer.DEFAULT.drain(inputStream));
} finally {
inputStream.close();
}
} else {
return new Resolution.Illegal(name);
}
}
A privileged action for creating a proxy class loader for the boot class loader.
/**
* A privileged action for creating a proxy class loader for the boot class loader.
*/
protected enum BootLoaderProxyCreationAction implements PrivilegedAction<ClassLoader> {
The singleton instance.
/**
* The singleton instance.
*/
INSTANCE;
{@inheritDoc}
/**
* {@inheritDoc}
*/
public ClassLoader run() {
return new URLClassLoader(new URL[0], ClassLoadingStrategy.BOOTSTRAP_LOADER);
}
}
A class file locator that queries a class loader for binary representations of class files.
The class loader is only weakly referenced.
Important: Even when calling Closeable.close()
on this class file locator, no underlying class loader is closed if it implements the Closeable
interface as this is typically not intended.
/**
* <p>
* A class file locator that queries a class loader for binary representations of class files.
* The class loader is only weakly referenced.
* </p>
* <p>
* <b>Important</b>: Even when calling {@link Closeable#close()} on this class file locator, no underlying
* class loader is closed if it implements the {@link Closeable} interface as this is typically not intended.
* </p>
*/
public static class WeaklyReferenced extends WeakReference<ClassLoader> implements ClassFileLocator {
The represented class loader's hash code.
/**
* The represented class loader's hash code.
*/
private final int hashCode;
Creates a class file locator for a class loader that is weakly referenced.
Params: - classLoader – The class loader to represent.
/**
* Creates a class file locator for a class loader that is weakly referenced.
*
* @param classLoader The class loader to represent.
*/
protected WeaklyReferenced(ClassLoader classLoader) {
super(classLoader);
hashCode = System.identityHashCode(classLoader);
}
Creates a class file locator for a given class loader. If the class loader is not the bootstrap
class loader or the system class loader which cannot be collected, the class loader is only weakly
referenced.
Params: - classLoader – The class loader to be used. If this class loader represents the bootstrap class loader which is represented by the
null
value, this system class loader is used instead.
Returns: A corresponding source locator.
/**
* Creates a class file locator for a given class loader. If the class loader is not the bootstrap
* class loader or the system class loader which cannot be collected, the class loader is only weakly
* referenced.
*
* @param classLoader The class loader to be used. If this class loader represents the bootstrap class
* loader which is represented by the {@code null} value, this system class loader
* is used instead.
* @return A corresponding source locator.
*/
public static ClassFileLocator of(ClassLoader classLoader) {
return classLoader == null || classLoader == ClassLoader.getSystemClassLoader() || classLoader == ClassLoader.getSystemClassLoader().getParent()
? ForClassLoader.of(classLoader)
: new WeaklyReferenced(classLoader);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Resolution locate(String name) throws IOException {
ClassLoader classLoader = get();
return classLoader == null
? new Resolution.Illegal(name)
: ForClassLoader.locate(classLoader, name);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public void close() {
/* do nothing */
}
@Override
public int hashCode() {
return hashCode;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
} else if (other == null || getClass() != other.getClass()) {
return false;
}
WeaklyReferenced weaklyReferenced = (WeaklyReferenced) other;
ClassLoader classLoader = weaklyReferenced.get();
return classLoader != null && get() == classLoader;
}
}
}
A class file locator that locates class files by querying a Java module's getResourceAsStream
method.
Important: Even when calling Closeable.close()
on this class file locator, no underlying class loader is closed if it implements the Closeable
interface as this is typically not intended.
/**
* <p>
* A class file locator that locates class files by querying a Java module's {@code getResourceAsStream} method.
* </p>
* <p>
* <b>Important</b>: Even when calling {@link Closeable#close()} on this class file locator, no underlying
* class loader is closed if it implements the {@link Closeable} interface as this is typically not intended.
* </p>
*/
@HashCodeAndEqualsPlugin.Enhance
class ForModule implements ClassFileLocator {
An empty array that can be used to indicate no arguments to avoid an allocation on a reflective call.
/**
* An empty array that can be used to indicate no arguments to avoid an allocation on a reflective call.
*/
private static final Object[] NO_ARGUMENTS = new Object[0];
The represented Java module.
/**
* The represented Java module.
*/
private final JavaModule module;
Creates a new class file locator for a Java module.
Params: - module – The represented Java module.
/**
* Creates a new class file locator for a Java module.
*
* @param module The represented Java module.
*/
protected ForModule(JavaModule module) {
this.module = module;
}
Returns a class file locator that exposes all class files of the boot module layer. This class file locator is only available
on virtual machines of version 9 or later. On earlier versions, the returned class file locator does not locate any resources.
Returns: A class file locator that locates classes of the boot layer.
/**
* Returns a class file locator that exposes all class files of the boot module layer. This class file locator is only available
* on virtual machines of version 9 or later. On earlier versions, the returned class file locator does not locate any resources.
*
* @return A class file locator that locates classes of the boot layer.
*/
@SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should always be wrapped for clarity")
public static ClassFileLocator ofBootLayer() {
try {
Map<String, ClassFileLocator> bootModules = new HashMap<String, ClassFileLocator>();
Class<?> layerType = Class.forName("java.lang.ModuleLayer");
Method getPackages = JavaType.MODULE.load().getMethod("getPackages");
for (Object rawModule : (Set<?>) layerType.getMethod("modules").invoke(layerType.getMethod("boot").invoke(null))) {
ClassFileLocator classFileLocator = ForModule.of(JavaModule.of(rawModule));
for (Object packageName : (Set<?>) getPackages.invoke(rawModule, NO_ARGUMENTS)) {
bootModules.put((String) packageName, classFileLocator);
}
}
return new PackageDiscriminating(bootModules);
} catch (Exception exception) {
throw new IllegalStateException("Cannot process boot layer", exception);
}
}
Returns a class file locator for the provided module. If the provided module is not named, class files are located via this
unnamed module's class loader.
Params: - module – The module to create a class file locator for.
Returns: An appropriate class file locator.
/**
* Returns a class file locator for the provided module. If the provided module is not named, class files are located via this
* unnamed module's class loader.
*
* @param module The module to create a class file locator for.
* @return An appropriate class file locator.
*/
public static ClassFileLocator of(JavaModule module) {
return module.isNamed()
? new ForModule(module)
: ForClassLoader.of(module.getClassLoader());
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Resolution locate(String name) throws IOException {
return locate(module, name);
}
Creates a resolution for a Java module's class files.
Params: - module – The Java module to query.
- typeName – The name of the type being queried.
Throws: - IOException – If an I/O exception was thrown.
Returns: A resolution for the query.
/**
* Creates a resolution for a Java module's class files.
*
* @param module The Java module to query.
* @param typeName The name of the type being queried.
* @return A resolution for the query.
* @throws IOException If an I/O exception was thrown.
*/
protected static Resolution locate(JavaModule module, String typeName) throws IOException {
InputStream inputStream = module.getResourceAsStream(typeName.replace('.', '/') + CLASS_FILE_EXTENSION);
if (inputStream != null) {
try {
return new Resolution.Explicit(StreamDrainer.DEFAULT.drain(inputStream));
} finally {
inputStream.close();
}
} else {
return new Resolution.Illegal(typeName);
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public void close() {
/* do nothing */
}
A class file locator for a Java module that only references this module weakly. If a module was garbage collected,
this class file locator only returns unresolved resolutions.
Important: Even when calling Closeable.close()
on this class file locator, no underlying class loader is closed if it implements the Closeable
interface as this is typically not intended.
/**
* <p>
* A class file locator for a Java module that only references this module weakly. If a module was garbage collected,
* this class file locator only returns unresolved resolutions.
* </p>
* <p>
* <b>Important</b>: Even when calling {@link Closeable#close()} on this class file locator, no underlying
* class loader is closed if it implements the {@link Closeable} interface as this is typically not intended.
* </p>
*/
public static class WeaklyReferenced extends WeakReference<Object> implements ClassFileLocator {
The represented module's hash code.
/**
* The represented module's hash code.
*/
private final int hashCode;
Creates a class file locator for a Java module that is weakly referenced.
Params: - module – The raw Java module to represent.
/**
* Creates a class file locator for a Java module that is weakly referenced.
*
* @param module The raw Java module to represent.
*/
protected WeaklyReferenced(Object module) {
super(module);
hashCode = System.identityHashCode(module);
}
Creates a class file locator for a Java module where the module is referenced weakly. If the module is not named, the module's class loader
is represented instead. Module's of the boot layer are not referenced weakly.
Params: - module – The Java module to represent.
Returns: A suitable class file locator.
/**
* Creates a class file locator for a Java module where the module is referenced weakly. If the module is not named, the module's class loader
* is represented instead. Module's of the boot layer are not referenced weakly.
*
* @param module The Java module to represent.
* @return A suitable class file locator.
*/
public static ClassFileLocator of(JavaModule module) {
if (module.isNamed()) {
return module.getClassLoader() == null || module.getClassLoader() == ClassLoader.getSystemClassLoader() || module.getClassLoader() == ClassLoader.getSystemClassLoader().getParent()
? new ForModule(module)
: new WeaklyReferenced(module.unwrap());
} else {
return ForClassLoader.WeaklyReferenced.of(module.getClassLoader());
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Resolution locate(String name) throws IOException {
Object module = get();
return module == null
? new Resolution.Illegal(name)
: ForModule.locate(JavaModule.of(module), name);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public void close() {
/* do nothing */
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public int hashCode() {
return hashCode;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public boolean equals(Object other) {
if (this == other) {
return true;
} else if (other == null || getClass() != other.getClass()) {
return false;
}
WeaklyReferenced weaklyReferenced = (WeaklyReferenced) other;
Object module = weaklyReferenced.get();
return module != null && get() == module;
}
}
}
A class file locator that locates classes within a Java jar file.
/**
* A class file locator that locates classes within a Java <i>jar</i> file.
*/
@HashCodeAndEqualsPlugin.Enhance
class ForJarFile implements ClassFileLocator {
A list of potential locations of the runtime jar for different platforms.
/**
* A list of potential locations of the runtime jar for different platforms.
*/
private static final List<String> RUNTIME_LOCATIONS = Arrays.asList("lib/rt.jar", "../lib/rt.jar", "../Classes/classes.jar");
The jar file to read from.
/**
* The jar file to read from.
*/
private final JarFile jarFile;
Creates a new class file locator for the given jar file.
Params: - jarFile – The jar file to read from.
/**
* Creates a new class file locator for the given jar file.
*
* @param jarFile The jar file to read from.
*/
public ForJarFile(JarFile jarFile) {
this.jarFile = jarFile;
}
Creates a new class file locator for the given jar file.
Params: - file – The jar file to read from.
Throws: - IOException – If an I/O exception is thrown.
Returns: A class file locator for the jar file.
/**
* Creates a new class file locator for the given jar file.
*
* @param file The jar file to read from.
* @return A class file locator for the jar file.
* @throws IOException If an I/O exception is thrown.
*/
public static ClassFileLocator of(File file) throws IOException {
return new ForJarFile(new JarFile(file));
}
Resolves a class file locator for the class path that reads class files directly from the file system. The resulting
class file locator does not imply classes on the boot path.
Throws: - IOException – If an I/O exception occurs.
Returns: A class file locator for the class path.
/**
* Resolves a class file locator for the class path that reads class files directly from the file system. The resulting
* class file locator does not imply classes on the boot path.
*
* @return A class file locator for the class path.
* @throws IOException If an I/O exception occurs.
*/
public static ClassFileLocator ofClassPath() throws IOException {
return ofClassPath(System.getProperty("java.class.path"));
}
Resolves a class file locator for the class path that reads class files directly from the file system.
Note: The resulting class file locator does not include classes of the bootstrap class loader.
Params: - classPath – The class path to scan with the elements separated by
path.separator
.
Throws: - IOException – If an I/O exception occurs.
Returns: A class file locator for the class path.
/**
* <p>
* Resolves a class file locator for the class path that reads class files directly from the file system.
* </p>
* <p>
* <b>Note</b>: The resulting class file locator does not include classes of the bootstrap class loader.
* </p>
*
* @param classPath The class path to scan with the elements separated by {@code path.separator}.
* @return A class file locator for the class path.
* @throws IOException If an I/O exception occurs.
*/
public static ClassFileLocator ofClassPath(String classPath) throws IOException {
List<ClassFileLocator> classFileLocators = new ArrayList<ClassFileLocator>();
for (String element : Pattern.compile(System.getProperty("path.separator"), Pattern.LITERAL).split(classPath)) {
File file = new File(element);
if (file.isDirectory()) {
classFileLocators.add(new ForFolder(file));
} else if (file.isFile()) {
classFileLocators.add(of(file));
}
}
return new Compound(classFileLocators);
}
Resolves a class file locator for the runtime jar. If such a file does not exist or cannot be located, a runtime exception is thrown.
Throws: - IOException – If an I/O exception occurs.
Returns: A class file locator for the runtime jar, if available.
/**
* Resolves a class file locator for the runtime jar. If such a file does not exist or cannot be located, a runtime exception is thrown.
*
* @return A class file locator for the runtime jar, if available.
* @throws IOException If an I/O exception occurs.
*/
public static ClassFileLocator ofRuntimeJar() throws IOException {
String javaHome = System.getProperty("java.home").replace('\\', '/');
File runtimeJar = null;
for (String location : RUNTIME_LOCATIONS) {
File candidate = new File(javaHome, location);
if (candidate.isFile()) {
runtimeJar = candidate;
break;
}
}
if (runtimeJar == null) {
throw new IllegalStateException("Runtime jar does not exist in " + javaHome + " for any of " + RUNTIME_LOCATIONS);
}
return of(runtimeJar);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Resolution locate(String name) throws IOException {
ZipEntry zipEntry = jarFile.getEntry(name.replace('.', '/') + CLASS_FILE_EXTENSION);
if (zipEntry == null) {
return new Resolution.Illegal(name);
} else {
InputStream inputStream = jarFile.getInputStream(zipEntry);
try {
return new Resolution.Explicit(StreamDrainer.DEFAULT.drain(inputStream));
} finally {
inputStream.close();
}
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public void close() throws IOException {
jarFile.close();
}
}
A class file locator that locates classes within a Java jmod file. This class file locator should not be used for reading modular jar files for which ForJarFile
is appropriate. /**
* A class file locator that locates classes within a Java <i>jmod</i> file. This class file locator should not be used
* for reading modular jar files for which {@link ForJarFile} is appropriate.
*/
@HashCodeAndEqualsPlugin.Enhance
class ForModuleFile implements ClassFileLocator {
The file extension of a modular Java package.
/**
* The file extension of a modular Java package.
*/
private static final String JMOD_FILE_EXTENSION = ".jmod";
A list of potential locations of the boot path for different platforms.
/**
* A list of potential locations of the boot path for different platforms.
*/
private static final List<String> BOOT_LOCATIONS = Arrays.asList("jmods", "../jmods", "modules");
The represented jmod file.
/**
* The represented jmod file.
*/
private final ZipFile zipFile;
Creates a new class file locator for a jmod file.
Params: - zipFile – The represented jmod file.
/**
* Creates a new class file locator for a jmod file.
*
* @param zipFile The represented jmod file.
*/
public ForModuleFile(ZipFile zipFile) {
this.zipFile = zipFile;
}
Creates a new class file locator for this VM's boot module path.
Throws: - IOException – If an I/O error occurs.
Returns: A class file locator for this VM's boot module path.
/**
* Creates a new class file locator for this VM's boot module path.
*
* @return A class file locator for this VM's boot module path.
* @throws IOException If an I/O error occurs.
*/
public static ClassFileLocator ofBootPath() throws IOException {
String javaHome = System.getProperty("java.home").replace('\\', '/');
File bootPath = null;
for (String location : BOOT_LOCATIONS) {
File candidate = new File(javaHome, location);
if (candidate.isDirectory()) {
bootPath = candidate;
break;
}
}
if (bootPath == null) {
throw new IllegalStateException("Boot modules do not exist in " + javaHome + " for any of " + BOOT_LOCATIONS);
}
return ofBootPath(bootPath);
}
Creates a new class file locator for a Java boot module path.
Params: - bootPath – The boot path folder.
Throws: - IOException – If an I/O error occurs.
Returns: A class file locator for this VMs boot module path.
/**
* Creates a new class file locator for a Java boot module path.
*
* @param bootPath The boot path folder.
* @return A class file locator for this VMs boot module path.
* @throws IOException If an I/O error occurs.
*/
public static ClassFileLocator ofBootPath(File bootPath) throws IOException {
File[] module = bootPath.listFiles();
if (module == null) {
return NoOp.INSTANCE;
}
List<ClassFileLocator> classFileLocators = new ArrayList<ClassFileLocator>(module.length);
for (File aModule : module) {
if (aModule.isFile()) {
classFileLocators.add(of(aModule));
} else if (aModule.isDirectory()) { // Relevant for locally built OpenJDK.
classFileLocators.add(new ForFolder(aModule));
}
}
return new Compound(classFileLocators);
}
Resolves a class file locator for this VM's Java module path that reads class files directly from the file system.
Note: The resulting class file locator does not include classes of the bootstrap class loader.
Throws: - IOException – If an I/O exception occurs.
Returns: A class file locator for the class path.
/**
* <p>
* Resolves a class file locator for this VM's Java module path that reads class files directly from the file system.
* </p>
* <p>
* <b>Note</b>: The resulting class file locator does not include classes of the bootstrap class loader.
* </p>
*
* @return A class file locator for the class path.
* @throws IOException If an I/O exception occurs.
*/
public static ClassFileLocator ofModulePath() throws IOException {
String modulePath = System.getProperty("jdk.module.path");
return modulePath == null
? NoOp.INSTANCE
: ofModulePath(modulePath);
}
Resolves a class file locator for a Java module path that reads class files directly from the file system. All elements of the module path are resolved relative to this VM's user.dir
.
Note: The resulting class file locator does not include classes of the bootstrap class loader.
Params: - modulePath – The module path to scan with the elements separated by
path.separator
.
Throws: - IOException – If an I/O exception occurs.
Returns: A class file locator for the class path.
/**
* <p>
* Resolves a class file locator for a Java module path that reads class files directly from the file system. All
* elements of the module path are resolved relative to this VM's {@code user.dir}.
* </p>
* <p>
* <b>Note</b>: The resulting class file locator does not include classes of the bootstrap class loader.
* </p>
*
* @param modulePath The module path to scan with the elements separated by {@code path.separator}.
* @return A class file locator for the class path.
* @throws IOException If an I/O exception occurs.
*/
public static ClassFileLocator ofModulePath(String modulePath) throws IOException {
return ofModulePath(modulePath, System.getProperty("user.dir"));
}
Resolves a class file locator for a Java module path that reads class files directly from the file system.
Note: The resulting class file locator does not include classes of the bootstrap class loader.
Params: - modulePath – The module path to scan with the elements separated by
path.separator
. - baseFolder – The relative location of the elements on the module path.
Throws: - IOException – If an I/O exception occurs.
Returns: A class file locator for the class path.
/**
* <p>
* Resolves a class file locator for a Java module path that reads class files directly from the file system.
* </p>
* <p>
* <b>Note</b>: The resulting class file locator does not include classes of the bootstrap class loader.
* </p>
*
* @param modulePath The module path to scan with the elements separated by {@code path.separator}.
* @param baseFolder The relative location of the elements on the module path.
* @return A class file locator for the class path.
* @throws IOException If an I/O exception occurs.
*/
public static ClassFileLocator ofModulePath(String modulePath, String baseFolder) throws IOException {
List<ClassFileLocator> classFileLocators = new ArrayList<ClassFileLocator>();
for (String element : Pattern.compile(System.getProperty("path.separator"), Pattern.LITERAL).split(modulePath)) {
File file = new File(baseFolder, element);
if (file.isDirectory()) {
File[] module = file.listFiles();
if (module != null) {
for (File aModule : module) {
if (aModule.isDirectory()) {
classFileLocators.add(new ForFolder(aModule));
} else if (aModule.isFile()) {
classFileLocators.add(aModule.getName().endsWith(JMOD_FILE_EXTENSION)
? of(aModule)
: ForJarFile.of(aModule));
}
}
}
} else if (file.isFile()) {
classFileLocators.add(file.getName().endsWith(JMOD_FILE_EXTENSION)
? of(file)
: ForJarFile.of(file));
}
}
return new Compound(classFileLocators);
}
Returns a class file locator for the given module file.
Params: - file – The module file.
Throws: - IOException – If an I/O error occurs.
Returns: A class file locator for the given module
/**
* Returns a class file locator for the given module file.
*
* @param file The module file.
* @return A class file locator for the given module
* @throws IOException If an I/O error occurs.
*/
public static ClassFileLocator of(File file) throws IOException {
return new ForModuleFile(new ZipFile(file));
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Resolution locate(String name) throws IOException {
ZipEntry zipEntry = zipFile.getEntry("classes/" + name.replace('.', '/') + CLASS_FILE_EXTENSION);
if (zipEntry == null) {
return new Resolution.Illegal(name);
} else {
InputStream inputStream = zipFile.getInputStream(zipEntry);
try {
return new Resolution.Explicit(StreamDrainer.DEFAULT.drain(inputStream));
} finally {
inputStream.close();
}
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public void close() throws IOException {
zipFile.close();
}
}
A class file locator that finds files from a standardized Java folder structure with folders donating packages and class files being saved as <classname>.class
files within their package folder. /**
* A class file locator that finds files from a standardized Java folder structure with
* folders donating packages and class files being saved as {@code <classname>.class} files
* within their package folder.
*/
@HashCodeAndEqualsPlugin.Enhance
class ForFolder implements ClassFileLocator {
The base folder of the package structure.
/**
* The base folder of the package structure.
*/
private final File folder;
Creates a new class file locator for a folder structure of class files.
Params: - folder – The base folder of the package structure.
/**
* Creates a new class file locator for a folder structure of class files.
*
* @param folder The base folder of the package structure.
*/
public ForFolder(File folder) {
this.folder = folder;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Resolution locate(String name) throws IOException {
File file = new File(folder, name.replace('.', File.separatorChar) + CLASS_FILE_EXTENSION);
if (file.exists()) {
InputStream inputStream = new FileInputStream(file);
try {
return new Resolution.Explicit(StreamDrainer.DEFAULT.drain(inputStream));
} finally {
inputStream.close();
}
} else {
return new Resolution.Illegal(name);
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public void close() {
/* do nothing */
}
}
A class file locator that reads class files from one or several URLs. The reading is accomplished via using an URLClassLoader
. Doing so, boot loader resources might be located additionally to those found via the specified URLs. /**
* A class file locator that reads class files from one or several URLs. The reading is accomplished via using an {@link URLClassLoader}.
* Doing so, boot loader resources might be located additionally to those found via the specified URLs.
*/
@HashCodeAndEqualsPlugin.Enhance
class ForUrl implements ClassFileLocator {
The class loader that delegates to the URLs.
/**
* The class loader that delegates to the URLs.
*/
private final ClassLoader classLoader;
Creates a new class file locator for the given URLs.
Params: - url – The URLs to search for class files.
/**
* Creates a new class file locator for the given URLs.
*
* @param url The URLs to search for class files.
*/
public ForUrl(URL... url) {
classLoader = AccessController.doPrivileged(new ClassLoaderCreationAction(url));
}
Creates a new class file locator for the given URLs.
Params: - urls – The URLs to search for class files.
/**
* Creates a new class file locator for the given URLs.
*
* @param urls The URLs to search for class files.
*/
public ForUrl(Collection<? extends URL> urls) {
this(urls.toArray(new URL[0]));
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Resolution locate(String name) throws IOException {
return ForClassLoader.locate(classLoader, name);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public void close() throws IOException {
if (classLoader instanceof Closeable) {
((Closeable) classLoader).close();
}
}
An action to create a class loader with the purpose of locating classes from an URL location.
/**
* An action to create a class loader with the purpose of locating classes from an URL location.
*/
@HashCodeAndEqualsPlugin.Enhance
protected static class ClassLoaderCreationAction implements PrivilegedAction<ClassLoader> {
The URLs to locate classes from.
/**
* The URLs to locate classes from.
*/
private final URL[] url;
Creates a new class loader creation action.
Params: - url – The URLs to locate classes from.
/**
* Creates a new class loader creation action.
*
* @param url The URLs to locate classes from.
*/
protected ClassLoaderCreationAction(URL[] url) {
this.url = url;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public ClassLoader run() {
return new URLClassLoader(url, ClassLoadingStrategy.BOOTSTRAP_LOADER);
}
}
}
A Java agent that allows the location of class files by emulating a retransformation. Note that this class file
locator causes a class to be loaded in order to look up its class file. Also, this locator does deliberately not
support the look-up of classes that represent lambda expressions.
/**
* A Java agent that allows the location of class files by emulating a retransformation. Note that this class file
* locator causes a class to be loaded in order to look up its class file. Also, this locator does deliberately not
* support the look-up of classes that represent lambda expressions.
*/
@HashCodeAndEqualsPlugin.Enhance
class AgentBased implements ClassFileLocator {
The name of the Byte Buddy net.bytebuddy.agent.Installer
class. /**
* The name of the Byte Buddy {@code net.bytebuddy.agent.Installer} class.
*/
private static final String INSTALLER_TYPE = "net.bytebuddy.agent.Installer";
The name of the net.bytebuddy.agent.Installer
getter for reading an installed Instrumentation
. /**
* The name of the {@code net.bytebuddy.agent.Installer} getter for reading an installed {@link Instrumentation}.
*/
private static final String INSTRUMENTATION_GETTER = "getInstrumentation";
Indicator for access to a static member via reflection to make the code more readable.
/**
* Indicator for access to a static member via reflection to make the code more readable.
*/
private static final Object STATIC_MEMBER = null;
A dispatcher for interacting with the instrumentation API.
/**
* A dispatcher for interacting with the instrumentation API.
*/
private static final Dispatcher DISPATCHER = AccessController.doPrivileged(Dispatcher.CreationAction.INSTANCE);
The instrumentation instance to use for looking up the binary format of a type.
/**
* The instrumentation instance to use for looking up the binary format of a type.
*/
private final Instrumentation instrumentation;
The delegate to load a class by its name.
/**
* The delegate to load a class by its name.
*/
private final ClassLoadingDelegate classLoadingDelegate;
Creates an agent-based class file locator.
Params: - instrumentation – The instrumentation to be used.
- classLoader – The class loader to read a class from.
/**
* Creates an agent-based class file locator.
*
* @param instrumentation The instrumentation to be used.
* @param classLoader The class loader to read a class from.
*/
public AgentBased(Instrumentation instrumentation, ClassLoader classLoader) {
this(instrumentation, ClassLoadingDelegate.Default.of(classLoader));
}
Creates an agent-based class file locator.
Params: - instrumentation – The instrumentation to be used.
- classLoadingDelegate – The delegate responsible for class loading.
/**
* Creates an agent-based class file locator.
*
* @param instrumentation The instrumentation to be used.
* @param classLoadingDelegate The delegate responsible for class loading.
*/
public AgentBased(Instrumentation instrumentation, ClassLoadingDelegate classLoadingDelegate) {
if (!DISPATCHER.isRetransformClassesSupported(instrumentation)) {
throw new IllegalArgumentException(instrumentation + " does not support retransformation");
}
this.instrumentation = instrumentation;
this.classLoadingDelegate = classLoadingDelegate;
}
Returns an agent-based class file locator for the given class loader and an already installed
Byte Buddy-agent.
Params: - classLoader – The class loader that is expected to load the looked-up a class.
Returns: A class file locator for the given class loader based on a Byte Buddy agent.
/**
* Returns an agent-based class file locator for the given class loader and an already installed
* Byte Buddy-agent.
*
* @param classLoader The class loader that is expected to load the looked-up a class.
* @return A class file locator for the given class loader based on a Byte Buddy agent.
*/
public static ClassFileLocator fromInstalledAgent(ClassLoader classLoader) {
try {
return new AgentBased((Instrumentation) ClassLoader.getSystemClassLoader()
.loadClass(INSTALLER_TYPE)
.getMethod(INSTRUMENTATION_GETTER)
.invoke(STATIC_MEMBER), classLoader);
} catch (RuntimeException exception) {
throw exception;
} catch (Exception exception) {
throw new IllegalStateException("The Byte Buddy agent is not installed or not accessible", exception);
}
}
Returns a class file locator that is capable of locating a class file for the given type using the given instrumentation instance.
Params: - instrumentation – The instrumentation instance to query for a retransformation.
- type – The locatable type which class loader is used as a fallback.
Returns: A class file locator for locating the class file of the given type.
/**
* Returns a class file locator that is capable of locating a class file for the given type using the given instrumentation instance.
*
* @param instrumentation The instrumentation instance to query for a retransformation.
* @param type The locatable type which class loader is used as a fallback.
* @return A class file locator for locating the class file of the given type.
*/
public static ClassFileLocator of(Instrumentation instrumentation, Class<?> type) {
return new AgentBased(instrumentation, ClassLoadingDelegate.Explicit.of(type));
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Resolution locate(String name) {
try {
ExtractionClassFileTransformer classFileTransformer = new ExtractionClassFileTransformer(classLoadingDelegate.getClassLoader(), name);
DISPATCHER.addTransformer(instrumentation, classFileTransformer, true);
try {
DISPATCHER.retransformClasses(instrumentation, new Class<?>[]{classLoadingDelegate.locate(name)});
byte[] binaryRepresentation = classFileTransformer.getBinaryRepresentation();
return binaryRepresentation == null
? new Resolution.Illegal(name)
: new Resolution.Explicit(binaryRepresentation);
} finally {
instrumentation.removeTransformer(classFileTransformer);
}
} catch (RuntimeException exception) {
throw exception;
} catch (Exception ignored) {
return new Resolution.Illegal(name);
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public void close() {
/* do nothing */
}
A dispatcher to interact with the Instrumentation
API. /**
* A dispatcher to interact with the {@link Instrumentation} API.
*/
protected interface Dispatcher {
Invokes the Instrumentation#isRetransformClassesSupported
method. Params: - instrumentation – The instrumentation instance to invoke the method on.
Returns: true
if the supplied instrumentation instance supports retransformation.
/**
* Invokes the {@code Instrumentation#isRetransformClassesSupported} method.
*
* @param instrumentation The instrumentation instance to invoke the method on.
* @return {@code true} if the supplied instrumentation instance supports retransformation.
*/
boolean isRetransformClassesSupported(Instrumentation instrumentation);
Registers a transformer.
Params: - instrumentation – The instrumentation instance to invoke the method on.
- classFileTransformer – The class file transformer to register.
- canRetransform –
true
if the class file transformer should be invoked upon a retransformation.
/**
* Registers a transformer.
*
* @param instrumentation The instrumentation instance to invoke the method on.
* @param classFileTransformer The class file transformer to register.
* @param canRetransform {@code true} if the class file transformer should be invoked upon a retransformation.
*/
void addTransformer(Instrumentation instrumentation, ClassFileTransformer classFileTransformer, boolean canRetransform);
Retransforms the supplied classes.
Params: - instrumentation – The instrumentation instance to invoke the method on.
- type – The types to retransform.
Throws: - UnmodifiableClassException – If any of the supplied types are unmodifiable.
/**
* Retransforms the supplied classes.
*
* @param instrumentation The instrumentation instance to invoke the method on.
* @param type The types to retransform.
* @throws UnmodifiableClassException If any of the supplied types are unmodifiable.
*/
void retransformClasses(Instrumentation instrumentation, Class<?>[] type) throws UnmodifiableClassException;
An action to create a Dispatcher
. /**
* An action to create a {@link Dispatcher}.
*/
enum CreationAction implements PrivilegedAction<Dispatcher> {
The singleton instance.
/**
* The singleton instance.
*/
INSTANCE;
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Dispatcher run() {
try {
Class<?> instrumentation = Class.forName("java.lang.instrument.Instrumentation");
return new ForJava6CapableVm(instrumentation.getMethod("isRetransformClassesSupported"),
instrumentation.getMethod("addTransformer", ClassFileTransformer.class, boolean.class),
instrumentation.getMethod("retransformClasses", Class[].class));
} catch (ClassNotFoundException ignored) {
return ForLegacyVm.INSTANCE;
} catch (NoSuchMethodException ignored) {
return ForLegacyVm.INSTANCE;
}
}
}
A dispatcher for a VM that does not support retransformation.
/**
* A dispatcher for a VM that does not support retransformation.
*/
enum ForLegacyVm implements Dispatcher {
The singleton instance.
/**
* The singleton instance.
*/
INSTANCE;
{@inheritDoc}
/**
* {@inheritDoc}
*/
public boolean isRetransformClassesSupported(Instrumentation instrumentation) {
return false;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public void addTransformer(Instrumentation instrumentation, ClassFileTransformer classFileTransformer, boolean canRetransform) {
throw new IllegalStateException("The current VM does not support class retransformation");
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public void retransformClasses(Instrumentation instrumentation, Class<?>[] type) {
throw new IllegalStateException("The current VM does not support class retransformation");
}
}
A dispatcher for a Java 6 capable VM.
/**
* A dispatcher for a Java 6 capable VM.
*/
@HashCodeAndEqualsPlugin.Enhance
class ForJava6CapableVm implements Dispatcher {
The Instrumentation#isRetransformClassesSupported
method. /**
* The {@code Instrumentation#isRetransformClassesSupported} method.
*/
private final Method isRetransformClassesSupported;
The Instrumentation#addTransformer
method. /**
* The {@code Instrumentation#addTransformer} method.
*/
private final Method addTransformer;
The Instrumentation#retransformClasses
method. /**
* The {@code Instrumentation#retransformClasses} method.
*/
private final Method retransformClasses;
Creates a dispatcher for a Java 6 capable VM.
Params: - isRetransformClassesSupported – The
Instrumentation#isRetransformClassesSupported
method. - addTransformer – The
Instrumentation#addTransformer
method. - retransformClasses – The
Instrumentation#retransformClasses
method.
/**
* Creates a dispatcher for a Java 6 capable VM.
*
* @param isRetransformClassesSupported The {@code Instrumentation#isRetransformClassesSupported} method.
* @param addTransformer The {@code Instrumentation#addTransformer} method.
* @param retransformClasses The {@code Instrumentation#retransformClasses} method.
*/
protected ForJava6CapableVm(Method isRetransformClassesSupported, Method addTransformer, Method retransformClasses) {
this.isRetransformClassesSupported = isRetransformClassesSupported;
this.addTransformer = addTransformer;
this.retransformClasses = retransformClasses;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public boolean isRetransformClassesSupported(Instrumentation instrumentation) {
try {
return (Boolean) isRetransformClassesSupported.invoke(instrumentation);
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Cannot access java.lang.instrument.Instrumentation#isRetransformClassesSupported", exception);
} catch (InvocationTargetException exception) {
throw new IllegalStateException("Error invoking java.lang.instrument.Instrumentation#isRetransformClassesSupported", exception.getCause());
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public void addTransformer(Instrumentation instrumentation, ClassFileTransformer classFileTransformer, boolean canRetransform) {
try {
addTransformer.invoke(instrumentation, classFileTransformer, canRetransform);
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Cannot access java.lang.instrument.Instrumentation#addTransformer", exception);
} catch (InvocationTargetException exception) {
throw new IllegalStateException("Error invoking java.lang.instrument.Instrumentation#addTransformer", exception.getCause());
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public void retransformClasses(Instrumentation instrumentation, Class<?>[] type) throws UnmodifiableClassException {
try {
retransformClasses.invoke(instrumentation, (Object) type);
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Cannot access java.lang.instrument.Instrumentation#retransformClasses", exception);
} catch (InvocationTargetException exception) {
Throwable cause = exception.getCause();
if (cause instanceof UnmodifiableClassException) {
throw (UnmodifiableClassException) cause;
} else {
throw new IllegalStateException("Error invoking java.lang.instrument.Instrumentation#retransformClasses", cause);
}
}
}
}
}
A delegate that is queried for loading a class.
/**
* A delegate that is queried for loading a class.
*/
public interface ClassLoadingDelegate {
Loads a class by its name.
Params: - name – The name of the type.
Throws: - ClassNotFoundException – If a class cannot be found.
Returns: The class with the given name.
/**
* Loads a class by its name.
*
* @param name The name of the type.
* @return The class with the given name.
* @throws ClassNotFoundException If a class cannot be found.
*/
Class<?> locate(String name) throws ClassNotFoundException;
Returns the underlying class loader.
Returns: The underlying class loader.
/**
* Returns the underlying class loader.
*
* @return The underlying class loader.
*/
ClassLoader getClassLoader();
A default implementation of a class loading delegate.
/**
* A default implementation of a class loading delegate.
*/
@HashCodeAndEqualsPlugin.Enhance
class Default implements ClassLoadingDelegate {
The underlying class loader.
/**
* The underlying class loader.
*/
protected final ClassLoader classLoader;
Creates a default class loading delegate.
Params: - classLoader – The class loader to be queried.
/**
* Creates a default class loading delegate.
*
* @param classLoader The class loader to be queried.
*/
protected Default(ClassLoader classLoader) {
this.classLoader = classLoader;
}
Creates a class loading delegate for the given class loader.
Params: - classLoader – The class loader for which to create a delegate.
Returns: The class loading delegate for the provided class loader.
/**
* Creates a class loading delegate for the given class loader.
*
* @param classLoader The class loader for which to create a delegate.
* @return The class loading delegate for the provided class loader.
*/
public static ClassLoadingDelegate of(ClassLoader classLoader) {
return ForDelegatingClassLoader.isDelegating(classLoader)
? new ForDelegatingClassLoader(classLoader)
: new Default(classLoader == null ? ClassLoader.getSystemClassLoader() : classLoader);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Class<?> locate(String name) throws ClassNotFoundException {
return classLoader.loadClass(name);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public ClassLoader getClassLoader() {
return classLoader;
}
}
A class loading delegate that accounts for a sun.reflect.DelegatingClassLoader
which cannot load its own classes by name. /**
* A class loading delegate that accounts for a {@code sun.reflect.DelegatingClassLoader} which
* cannot load its own classes by name.
*/
class ForDelegatingClassLoader extends Default {
The name of the delegating class loader.
/**
* The name of the delegating class loader.
*/
private static final String DELEGATING_CLASS_LOADER_NAME = "sun.reflect.DelegatingClassLoader";
An index indicating the first element of a collection.
/**
* An index indicating the first element of a collection.
*/
private static final int ONLY = 0;
A dispatcher for extracting a class loader's loaded classes.
/**
* A dispatcher for extracting a class loader's loaded classes.
*/
private static final Dispatcher.Initializable DISPATCHER = AccessController.doPrivileged(Dispatcher.CreationAction.INSTANCE);
Creates a class loading delegate for a delegating class loader.
Params: - classLoader – The delegating class loader.
/**
* Creates a class loading delegate for a delegating class loader.
*
* @param classLoader The delegating class loader.
*/
protected ForDelegatingClassLoader(ClassLoader classLoader) {
super(classLoader);
}
Checks if a class loader is a delegating class loader.
Params: - classLoader – The class loader to inspect.
Returns: true
if the class loader is a delegating class loader.
/**
* Checks if a class loader is a delegating class loader.
*
* @param classLoader The class loader to inspect.
* @return {@code true} if the class loader is a delegating class loader.
*/
protected static boolean isDelegating(ClassLoader classLoader) {
return classLoader != null && classLoader.getClass().getName().equals(DELEGATING_CLASS_LOADER_NAME);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public Class<?> locate(String name) throws ClassNotFoundException {
Vector<Class<?>> classes;
try {
classes = DISPATCHER.initialize().extract(classLoader);
} catch (RuntimeException ignored) {
return super.locate(name);
}
if (classes.size() != 1) {
return super.locate(name);
}
Class<?> type = classes.get(ONLY);
return TypeDescription.ForLoadedType.getName(type).equals(name)
? type
: super.locate(name);
}
Representation of a Java Field
. /**
* Representation of a Java {@link java.lang.reflect.Field}.
*/
protected interface Dispatcher {
Reads the classes of the represented collection.
Params: - classLoader – The class loader to read from.
Returns: The class loader's loaded classes.
/**
* Reads the classes of the represented collection.
*
* @param classLoader The class loader to read from.
* @return The class loader's loaded classes.
*/
Vector<Class<?>> extract(ClassLoader classLoader);
An uninitialized version of a dispatcher for extracting a class loader's loaded classes.
/**
* An uninitialized version of a dispatcher for extracting a class loader's loaded classes.
*/
interface Initializable {
Initializes the dispatcher.
Returns: An initialized dispatcher.
/**
* Initializes the dispatcher.
*
* @return An initialized dispatcher.
*/
Dispatcher initialize();
}
An action for creating a dispatcher.
/**
* An action for creating a dispatcher.
*/
enum CreationAction implements PrivilegedAction<Initializable> {
The singleton instance.
/**
* The singleton instance.
*/
INSTANCE;
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Initializable run() {
try {
return new Dispatcher.Resolved(ClassLoader.class.getDeclaredField("classes"));
} catch (Exception exception) {
return new Dispatcher.Unresolved(exception.getMessage());
}
}
}
Represents a field that could be located.
/**
* Represents a field that could be located.
*/
@HashCodeAndEqualsPlugin.Enhance
class Resolved implements Dispatcher, Initializable, PrivilegedAction<Dispatcher> {
The represented field.
/**
* The represented field.
*/
private final Field field;
Creates a new resolved field.
Params: - field – the represented field.l
/**
* Creates a new resolved field.
*
* @param field the represented field.l
*/
public Resolved(Field field) {
this.field = field;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Dispatcher initialize() {
return AccessController.doPrivileged(this);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public Vector<Class<?>> extract(ClassLoader classLoader) {
try {
return (Vector<Class<?>>) field.get(classLoader);
} catch (IllegalAccessException exception) {
throw new IllegalStateException("Cannot access field", exception);
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Dispatcher run() {
field.setAccessible(true);
return this;
}
}
Represents a field that could not be located.
/**
* Represents a field that could not be located.
*/
@HashCodeAndEqualsPlugin.Enhance
class Unresolved implements Initializable {
The reason why this dispatcher is unavailable.
/**
* The reason why this dispatcher is unavailable.
*/
private final String message;
Creates a representation of a non-resolved field.
Params: - message – The reason why this dispatcher is unavailable.
/**
* Creates a representation of a non-resolved field.
*
* @param message The reason why this dispatcher is unavailable.
*/
public Unresolved(String message) {
this.message = message;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Dispatcher initialize() {
throw new UnsupportedOperationException("Could not locate classes vector: " + message);
}
}
}
}
A class loading delegate that allows the location of explicitly registered classes that cannot
be located by a class loader directly. This allows for locating classes that are loaded by
an anonymous class loader which does not register its classes in a system dictionary.
/**
* A class loading delegate that allows the location of explicitly registered classes that cannot
* be located by a class loader directly. This allows for locating classes that are loaded by
* an anonymous class loader which does not register its classes in a system dictionary.
*/
@HashCodeAndEqualsPlugin.Enhance
class Explicit implements ClassLoadingDelegate {
A class loading delegate that is queried for classes that are not registered explicitly.
/**
* A class loading delegate that is queried for classes that are not registered explicitly.
*/
private final ClassLoadingDelegate fallbackDelegate;
The map of registered classes mapped by their name.
/**
* The map of registered classes mapped by their name.
*/
private final Map<String, Class<?>> types;
Creates a new class loading delegate with a possibility of looking up explicitly
registered classes.
Params: - classLoader – The class loader to be used for looking up classes.
- types – A collection of classes that cannot be looked up explicitly.
/**
* Creates a new class loading delegate with a possibility of looking up explicitly
* registered classes.
*
* @param classLoader The class loader to be used for looking up classes.
* @param types A collection of classes that cannot be looked up explicitly.
*/
public Explicit(ClassLoader classLoader, Collection<? extends Class<?>> types) {
this(Default.of(classLoader), types);
}
Creates a new class loading delegate with a possibility of looking up explicitly
registered classes.
Params: - fallbackDelegate – The class loading delegate to query for any class that is not
registered explicitly.
- types – A collection of classes that cannot be looked up explicitly.
/**
* Creates a new class loading delegate with a possibility of looking up explicitly
* registered classes.
*
* @param fallbackDelegate The class loading delegate to query for any class that is not
* registered explicitly.
* @param types A collection of classes that cannot be looked up explicitly.
*/
public Explicit(ClassLoadingDelegate fallbackDelegate, Collection<? extends Class<?>> types) {
this.fallbackDelegate = fallbackDelegate;
this.types = new HashMap<String, Class<?>>();
for (Class<?> type : types) {
this.types.put(TypeDescription.ForLoadedType.getName(type), type);
}
}
Creates an explicit class loading delegate for the given type.
Params: - type – The type that is explicitly locatable.
Returns: A suitable class loading delegate.
/**
* Creates an explicit class loading delegate for the given type.
*
* @param type The type that is explicitly locatable.
* @return A suitable class loading delegate.
*/
public static ClassLoadingDelegate of(Class<?> type) {
return new Explicit(type.getClassLoader(), Collections.singleton(type));
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Class<?> locate(String name) throws ClassNotFoundException {
Class<?> type = types.get(name);
return type == null
? fallbackDelegate.locate(name)
: type;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public ClassLoader getClassLoader() {
return fallbackDelegate.getClassLoader();
}
}
}
A non-operational class file transformer that remembers the binary format of a given class.
/**
* A non-operational class file transformer that remembers the binary format of a given class.
*/
protected static class ExtractionClassFileTransformer implements ClassFileTransformer {
An indicator that an attempted class file transformation did not alter the handed class file.
/**
* An indicator that an attempted class file transformation did not alter the handed class file.
*/
private static final byte[] DO_NOT_TRANSFORM = null;
The class loader that is expected to have loaded the looked-up a class.
/**
* The class loader that is expected to have loaded the looked-up a class.
*/
private final ClassLoader classLoader;
The name of the type to look up.
/**
* The name of the type to look up.
*/
private final String typeName;
The binary representation of the looked-up class.
/**
* The binary representation of the looked-up class.
*/
@SuppressFBWarnings(value = "VO_VOLATILE_REFERENCE_TO_ARRAY", justification = "The array is not to be modified by contract")
private volatile byte[] binaryRepresentation;
Creates a class file transformer for the purpose of extraction.
Params: - classLoader – The class loader that is expected to have loaded the looked-up a class.
- typeName – The name of the type to look up.
/**
* Creates a class file transformer for the purpose of extraction.
*
* @param classLoader The class loader that is expected to have loaded the looked-up a class.
* @param typeName The name of the type to look up.
*/
protected ExtractionClassFileTransformer(ClassLoader classLoader, String typeName) {
this.classLoader = classLoader;
this.typeName = typeName;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
@SuppressFBWarnings(value = {"EI_EXPOSE_REP", "EI_EXPOSE_REP2"}, justification = "The array is not to be modified by contract")
public byte[] transform(ClassLoader classLoader,
String internalName,
Class<?> redefinedType,
ProtectionDomain protectionDomain,
byte[] binaryRepresentation) {
if (internalName != null && isChildOf(this.classLoader).matches(classLoader) && typeName.equals(internalName.replace('/', '.'))) {
this.binaryRepresentation = binaryRepresentation.clone();
}
return DO_NOT_TRANSFORM;
}
Returns the binary representation of the class file that was looked up. The returned array must never be modified.
Returns: The binary representation of the class file or null
if no such class file could be located.
/**
* Returns the binary representation of the class file that was looked up. The returned array must never be modified.
*
* @return The binary representation of the class file or {@code null} if no such class file could
* be located.
*/
@SuppressFBWarnings(value = "EI_EXPOSE_REP", justification = "The array is not to be modified by contract")
protected byte[] getBinaryRepresentation() {
return binaryRepresentation;
}
}
}
A class file locator that discriminates by a type's package.
/**
* A class file locator that discriminates by a type's package.
*/
@HashCodeAndEqualsPlugin.Enhance
class PackageDiscriminating implements ClassFileLocator {
A mapping of package names to class file locators.
/**
* A mapping of package names to class file locators.
*/
private final Map<String, ClassFileLocator> classFileLocators;
Creates a new package-discriminating class file locator.
Params: - classFileLocators – A mapping of package names to class file locators where an empty string donates the default package.
/**
* Creates a new package-discriminating class file locator.
*
* @param classFileLocators A mapping of package names to class file locators where an empty string donates the default package.
*/
public PackageDiscriminating(Map<String, ClassFileLocator> classFileLocators) {
this.classFileLocators = classFileLocators;
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Resolution locate(String name) throws IOException {
int packageIndex = name.lastIndexOf('.');
ClassFileLocator classFileLocator = classFileLocators.get(packageIndex == -1
? NamedElement.EMPTY_NAME
: name.substring(0, packageIndex));
return classFileLocator == null
? new Resolution.Illegal(name)
: classFileLocator.locate(name);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public void close() throws IOException {
for (ClassFileLocator classFileLocator : classFileLocators.values()) {
classFileLocator.close();
}
}
}
A compound ClassFileLocator
that chains several locators. Any class file locator is queried in the supplied order until one locator is able to provide an input stream of the class file. /**
* A compound {@link ClassFileLocator} that chains several locators.
* Any class file locator is queried in the supplied order until one locator is able to provide an input
* stream of the class file.
*/
@HashCodeAndEqualsPlugin.Enhance
class Compound implements ClassFileLocator, Closeable {
The ClassFileLocator
s which are represented by this compound class file locator in the order of their application. /**
* The {@link ClassFileLocator}s which are represented by this compound
* class file locator in the order of their application.
*/
private final List<ClassFileLocator> classFileLocators;
Creates a new compound class file locator.
Params: - classFileLocator – The
ClassFileLocator
s to be represented by this compound class file locator in the order of their application.
/**
* Creates a new compound class file locator.
*
* @param classFileLocator The {@link ClassFileLocator}s to be
* represented by this compound class file locator in the order of their application.
*/
public Compound(ClassFileLocator... classFileLocator) {
this(Arrays.asList(classFileLocator));
}
Creates a new compound class file locator.
Params: - classFileLocators – The
ClassFileLocator
s to be represented by this compound class file locator in the order of their application.
/**
* Creates a new compound class file locator.
*
* @param classFileLocators The {@link ClassFileLocator}s to be represented by this compound class file locator in
* the order of their application.
*/
public Compound(List<? extends ClassFileLocator> classFileLocators) {
this.classFileLocators = new ArrayList<ClassFileLocator>();
for (ClassFileLocator classFileLocator : classFileLocators) {
if (classFileLocator instanceof Compound) {
this.classFileLocators.addAll(((Compound) classFileLocator).classFileLocators);
} else if (!(classFileLocator instanceof NoOp)) {
this.classFileLocators.add(classFileLocator);
}
}
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public Resolution locate(String name) throws IOException {
for (ClassFileLocator classFileLocator : classFileLocators) {
Resolution resolution = classFileLocator.locate(name);
if (resolution.isResolved()) {
return resolution;
}
}
return new Resolution.Illegal(name);
}
{@inheritDoc}
/**
* {@inheritDoc}
*/
public void close() throws IOException {
for (ClassFileLocator classFileLocator : classFileLocators) {
classFileLocator.close();
}
}
}
}