All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.bytebuddy.dynamic.loading.ClassLoaderByteArrayInjector Maven / Gradle / Ivy

Go to download

Byte Buddy is a Java library for creating Java classes at run time. This artifact is a build of Byte Buddy with all ASM dependencies repackaged into its own name space.

There is a newer version: 1.17.2
Show newest version
package net.bytebuddy.dynamic.loading;

import net.bytebuddy.instrumentation.type.TypeDescription;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.ProtectionDomain;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * An injector that loads classes by reflectively invoking non-public methods on a given {@link java.lang.ClassLoader}.
 * 

 

* Note that the injector is only able to load classes in a linear manner. Thus, classes that refer to other classes * which are not yet loaded cannot be injected but will result in a {@link java.lang.NoClassDefFoundError}. This becomes * a problem when classes refer to each other using cyclic references. This injector can further not be applied to the * bootstrap class loader which is usually represented by a {@code null} value and can therefore not be accessed by * reflection. */ public class ClassLoaderByteArrayInjector { /** * A convenience reference to the default protection domain which is {@code null}. */ private static final ProtectionDomain DEFAULT_PROTECTION_DOMAIN = null; /** * A convenience variable representing the first index of an array, to make the code more readable. */ private static final int FROM_BEGINNING = 0; /** * A storage for the reflection method representations that are obtained on loading this classes. */ private static final ReflectionStore REFLECTION_STORE; /** * Obtains the reflective instances used by this injector or a no-op instance that throws the exception * that occurred when attempting to obtain the reflective member instances. */ static { ReflectionStore reflectionStore; try { Method findLoadedClassMethod = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class); findLoadedClassMethod.setAccessible(true); Method loadByteArrayMethod = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class, ProtectionDomain.class); loadByteArrayMethod.setAccessible(true); reflectionStore = new ReflectionStore.Resolved(findLoadedClassMethod, loadByteArrayMethod); } catch (Exception e) { reflectionStore = new ReflectionStore.Faulty(e); } REFLECTION_STORE = reflectionStore; } /** * The class loader into which the classes are to be injected. */ private final ClassLoader classLoader; /** * The protection domain that is used when loading classes. */ private final ProtectionDomain protectionDomain; /** * Creates a new injector for the given {@link java.lang.ClassLoader} and a default * {@link java.security.ProtectionDomain}. * * @param classLoader The {@link java.lang.ClassLoader} into which new class definitions are to be injected. */ public ClassLoaderByteArrayInjector(ClassLoader classLoader) { this(classLoader, DEFAULT_PROTECTION_DOMAIN); } /** * Creates a new injector for the given {@link java.lang.ClassLoader} and {@link java.security.ProtectionDomain}. * * @param classLoader The {@link java.lang.ClassLoader} into which new class definitions are to be injected. * @param protectionDomain The protection domain to apply during class definition. */ public ClassLoaderByteArrayInjector(ClassLoader classLoader, ProtectionDomain protectionDomain) { this.classLoader = classLoader; this.protectionDomain = protectionDomain; } /** * Injects a given type mapping into a class loader byte array injector. * * @param classLoaderByteArrayInjector The target of the injection. * @param types A mapping of types to their binary representation. * @return A map of loaded classes which were injected into the class loader byte array injector. */ public static Map> inject(ClassLoaderByteArrayInjector classLoaderByteArrayInjector, Map types) { Map> loadedTypes = new LinkedHashMap>(types.size()); for (Map.Entry entry : types.entrySet()) { loadedTypes.put(entry.getKey(), classLoaderByteArrayInjector.inject(entry.getKey().getName(), entry.getValue())); } return loadedTypes; } /** * Explicitly loads a {@link java.lang.Class} by reflective access into the represented class loader. * * @param name The fully qualified name of the {@link java.lang.Class} to be loaded. * @param binaryRepresentation The type's binary representation. * @return The loaded class that is a result of the class loading attempt. */ public Class inject(String name, byte[] binaryRepresentation) { try { synchronized (classLoader) { Class type = (Class) REFLECTION_STORE.getFindLoadedClassMethod().invoke(classLoader, name); if (type != null) { return type; } else { return (Class) REFLECTION_STORE.getLoadByteArrayMethod().invoke(classLoader, name, binaryRepresentation, FROM_BEGINNING, binaryRepresentation.length, protectionDomain); } } } catch (IllegalAccessException e) { throw new IllegalStateException("Could not access injection method", e); } catch (InvocationTargetException e) { throw new IllegalStateException("Exception on invoking loader method", e.getCause()); } } @Override public String toString() { return "ClassLoaderByteArrayInjector{classLoader=" + classLoader + '}'; } /** * A storage for method representations in order to access a class loader reflectively. */ private static interface ReflectionStore { /** * Returns the method for finding a class on a class loader. * * @return The method for finding a class on a class loader. */ Method getFindLoadedClassMethod(); /** * Returns the method for loading a class into a class loader. * * @return The method for loading a class into a class loader. */ Method getLoadByteArrayMethod(); /** * Represents a successfully loaded method lookup. */ static class Resolved implements ReflectionStore { /** * The method for finding a class on a class loader. */ private final Method findLoadedClassMethod; /** * The method for loading a class into a class loader. */ private final Method loadByteArrayMethod; /** * Creates a new resolved reflection store. * * @param findLoadedClassMethod The method for finding a class on a class loader. * @param loadByteArrayMethod The method for loading a class into a class loader. */ private Resolved(Method findLoadedClassMethod, Method loadByteArrayMethod) { this.findLoadedClassMethod = findLoadedClassMethod; this.loadByteArrayMethod = loadByteArrayMethod; } @Override public Method getFindLoadedClassMethod() { return findLoadedClassMethod; } @Override public Method getLoadByteArrayMethod() { return loadByteArrayMethod; } @Override public String toString() { return "ClassLoaderByteArrayInjector.ReflectionStore.Resolved{" + "findLoadedClassMethod=" + findLoadedClassMethod + ", loadByteArrayMethod=" + loadByteArrayMethod + '}'; } } /** * Represents an unsuccessfully loaded method lookup. */ static class Faulty implements ReflectionStore { /** * An exception to throw when attempting to lookup a method using this reflection store. */ private final RuntimeException exception; /** * Creates a new faulty reflection store. * * @param exception The exception that was thrown when attempting to lookup the method. */ private Faulty(Exception exception) { this.exception = new RuntimeException("Could not execute reflective lookup", exception); } @Override public Method getFindLoadedClassMethod() { throw exception; } @Override public Method getLoadByteArrayMethod() { throw exception; } @Override public String toString() { return "ClassLoaderByteArrayInjector.ReflectionStore.Faulty{exception=" + exception + '}'; } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy