/*
 * Copyright 2014 - 2019 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.loading;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.asm.MemberRemoval;
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
import net.bytebuddy.description.modifier.Visibility;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.scaffold.TypeValidation;
import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.implementation.MethodCall;
import net.bytebuddy.utility.JavaModule;
import net.bytebuddy.utility.JavaType;
import net.bytebuddy.utility.RandomString;
import org.objectweb.asm.Opcodes;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.*;
import java.net.URL;
import java.security.AccessController;
import java.security.Permission;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;

import static net.bytebuddy.matcher.ElementMatchers.any;

A class injector is capable of injecting classes into a ClassLoader without requiring the class loader to being able to explicitly look up these classes.

Important: Byte Buddy does not supply privileges when injecting code. When using a SecurityManager, the user of this injector is responsible for providing access to non-public properties.

/** * <p> * A class injector is capable of injecting classes into a {@link java.lang.ClassLoader} without * requiring the class loader to being able to explicitly look up these classes. * </p> * <p> * <b>Important</b>: Byte Buddy does not supply privileges when injecting code. When using a {@link SecurityManager}, * the user of this injector is responsible for providing access to non-public properties. * </p> */
public interface ClassInjector {
A permission for the suppressAccessChecks permission.
/** * A permission for the {@code suppressAccessChecks} permission. */
Permission SUPPRESS_ACCESS_CHECKS = new ReflectPermission("suppressAccessChecks");
Determines the default behavior for type injections when a type is already loaded.
/** * Determines the default behavior for type injections when a type is already loaded. */
boolean ALLOW_EXISTING_TYPES = false;
Indicates if this class injector is available on the current VM.
Returns:true if this injector is available on the current VM.
/** * Indicates if this class injector is available on the current VM. * * @return {@code true} if this injector is available on the current VM. */
boolean isAlive();
Injects the given types into the represented class loader.
Params:
  • types – The types to load via injection.
Returns:The loaded types that were passed as arguments.
/** * Injects the given types into the represented class loader. * * @param types The types to load via injection. * @return The loaded types that were passed as arguments. */
Map<TypeDescription, Class<?>> inject(Map<? extends TypeDescription, byte[]> types);
Injects the given types into the represented class loader using a mapping from name to binary representation.
Params:
  • types – The types to load via injection.
Returns:The loaded types that were passed as arguments.
/** * Injects the given types into the represented class loader using a mapping from name to binary representation. * * @param types The types to load via injection. * @return The loaded types that were passed as arguments. */
Map<String, Class<?>> injectRaw(Map<? extends String, byte[]> types);
An abstract base implementation of a class injector.
/** * An abstract base implementation of a class injector. */
abstract class AbstractBase implements ClassInjector {
{@inheritDoc}
/** * {@inheritDoc} */
public Map<TypeDescription, Class<?>> inject(Map<? extends TypeDescription, byte[]> types) { Map<String, byte[]> binaryRepresentations = new LinkedHashMap<String, byte[]>(); for (Map.Entry<? extends TypeDescription, byte[]> entry : types.entrySet()) { binaryRepresentations.put(entry.getKey().getName(), entry.getValue()); } Map<String, Class<?>> loadedTypes = injectRaw(binaryRepresentations); Map<TypeDescription, Class<?>> result = new LinkedHashMap<TypeDescription, Class<?>>(); for (TypeDescription typeDescription : types.keySet()) { result.put(typeDescription, loadedTypes.get(typeDescription.getName())); } return result; } }
A class injector that uses reflective method calls.
/** * A class injector that uses reflective method calls. */
@HashCodeAndEqualsPlugin.Enhance class UsingReflection extends AbstractBase {
The dispatcher to use for accessing a class loader via reflection.
/** * The dispatcher to use for accessing a class loader via reflection. */
private static final Dispatcher.Initializable DISPATCHER = AccessController.doPrivileged(Dispatcher.CreationAction.INSTANCE);
The class loader into which the classes are to be injected.
/** * The class loader into which the classes are to be injected. */
private final ClassLoader classLoader;
The protection domain that is used when loading classes.
/** * The protection domain that is used when loading classes. */
@HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY) private final ProtectionDomain protectionDomain;
The package definer to be queried for package definitions.
/** * The package definer to be queried for package definitions. */
private final PackageDefinitionStrategy packageDefinitionStrategy;
Determines if an exception should be thrown when attempting to load a type that already exists.
/** * Determines if an exception should be thrown when attempting to load a type that already exists. */
private final boolean forbidExisting;
Creates a new injector for the given ClassLoader and a default ProtectionDomain and a trivial PackageDefinitionStrategy which does not trigger an error when discovering existent classes.
Params:
  • classLoader – The ClassLoader into which new class definitions are to be injected. Must not be the bootstrap loader.
/** * Creates a new injector for the given {@link java.lang.ClassLoader} and a default {@link java.security.ProtectionDomain} and a * trivial {@link PackageDefinitionStrategy} which does not trigger an error when discovering existent classes. * * @param classLoader The {@link java.lang.ClassLoader} into which new class definitions are to be injected. Must not be the bootstrap loader. */
public UsingReflection(ClassLoader classLoader) { this(classLoader, ClassLoadingStrategy.NO_PROTECTION_DOMAIN); }
Creates a new injector for the given ClassLoader and a default PackageDefinitionStrategy where the injection of existent classes does not trigger an error.
Params:
  • classLoader – The ClassLoader into which new class definitions are to be injected. Must not be the bootstrap loader.
  • protectionDomain – The protection domain to apply during class definition.
/** * Creates a new injector for the given {@link java.lang.ClassLoader} and a default {@link PackageDefinitionStrategy} where the * injection of existent classes does not trigger an error. * * @param classLoader The {@link java.lang.ClassLoader} into which new class definitions are to be injected. Must not be the bootstrap loader. * @param protectionDomain The protection domain to apply during class definition. */
public UsingReflection(ClassLoader classLoader, ProtectionDomain protectionDomain) { this(classLoader, protectionDomain, PackageDefinitionStrategy.Trivial.INSTANCE, ALLOW_EXISTING_TYPES); }
Creates a new injector for the given ClassLoader and ProtectionDomain.
Params:
  • classLoader – The ClassLoader into which new class definitions are to be injected.Must not be the bootstrap loader.
  • protectionDomain – The protection domain to apply during class definition.
  • packageDefinitionStrategy – The package definer to be queried for package definitions.
  • forbidExisting – Determines if an exception should be thrown when attempting to load a type that already exists.
/** * 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.Must not be the bootstrap loader. * @param protectionDomain The protection domain to apply during class definition. * @param packageDefinitionStrategy The package definer to be queried for package definitions. * @param forbidExisting Determines if an exception should be thrown when attempting to load a type that already exists. */
public UsingReflection(ClassLoader classLoader, ProtectionDomain protectionDomain, PackageDefinitionStrategy packageDefinitionStrategy, boolean forbidExisting) { if (classLoader == null) { throw new IllegalArgumentException("Cannot inject classes into the bootstrap class loader"); } this.classLoader = classLoader; this.protectionDomain = protectionDomain; this.packageDefinitionStrategy = packageDefinitionStrategy; this.forbidExisting = forbidExisting; }
{@inheritDoc}
/** * {@inheritDoc} */
public boolean isAlive() { return isAvailable(); }
{@inheritDoc}
/** * {@inheritDoc} */
public Map<String, Class<?>> injectRaw(Map<? extends String, byte[]> types) { Dispatcher dispatcher = DISPATCHER.initialize(); Map<String, Class<?>> result = new HashMap<String, Class<?>>(); for (Map.Entry<? extends String, byte[]> entry : types.entrySet()) { synchronized (dispatcher.getClassLoadingLock(classLoader, entry.getKey())) { Class<?> type = dispatcher.findClass(classLoader, entry.getKey()); if (type == null) { int packageIndex = entry.getKey().lastIndexOf('.'); if (packageIndex != -1) { String packageName = entry.getKey().substring(0, packageIndex); PackageDefinitionStrategy.Definition definition = packageDefinitionStrategy.define(classLoader, packageName, entry.getKey()); if (definition.isDefined()) { Package definedPackage = dispatcher.getPackage(classLoader, packageName); if (definedPackage == null) { dispatcher.definePackage(classLoader, packageName, definition.getSpecificationTitle(), definition.getSpecificationVersion(), definition.getSpecificationVendor(), definition.getImplementationTitle(), definition.getImplementationVersion(), definition.getImplementationVendor(), definition.getSealBase()); } else if (!definition.isCompatibleTo(definedPackage)) { throw new SecurityException("Sealing violation for package " + packageName); } } } type = dispatcher.defineClass(classLoader, entry.getKey(), entry.getValue(), protectionDomain); } else if (forbidExisting) { throw new IllegalStateException("Cannot inject already loaded type: " + type); } result.put(entry.getKey(), type); } } return result; }
Indicates if this class injection is available on the current VM.
Returns:true if this class injection is available.
/** * Indicates if this class injection is available on the current VM. * * @return {@code true} if this class injection is available. */
public static boolean isAvailable() { return DISPATCHER.isAvailable(); }
Creates a class injector for the system class loader.
Returns:A class injector for the system class loader.
/** * Creates a class injector for the system class loader. * * @return A class injector for the system class loader. */
public static ClassInjector ofSystemClassLoader() { return new UsingReflection(ClassLoader.getSystemClassLoader()); }
A dispatcher for accessing a ClassLoader reflectively.
/** * A dispatcher for accessing a {@link ClassLoader} reflectively. */
protected interface Dispatcher {
Indicates a class that is currently not defined.
/** * Indicates a class that is currently not defined. */
Class<?> UNDEFINED = null;
Returns the lock for loading the specified class.
Params:
  • classLoader – the class loader to inject the class into.
  • name – The name of the class.
Returns:The lock for loading this class.
/** * Returns the lock for loading the specified class. * * @param classLoader the class loader to inject the class into. * @param name The name of the class. * @return The lock for loading this class. */
Object getClassLoadingLock(ClassLoader classLoader, String name);
Looks up a class from the given class loader.
Params:
  • classLoader – The class loader for which a class should be located.
  • name – The binary name of the class that should be located.
Returns:The class for the binary name or null if no such class is defined for the provided class loader.
/** * Looks up a class from the given class loader. * * @param classLoader The class loader for which a class should be located. * @param name The binary name of the class that should be located. * @return The class for the binary name or {@code null} if no such class is defined for the provided class loader. */
Class<?> findClass(ClassLoader classLoader, String name);
Defines a class for the given class loader.
Params:
  • classLoader – The class loader for which a new class should be defined.
  • name – The binary name of the class that should be defined.
  • binaryRepresentation – The binary representation of the class.
  • protectionDomain – The protection domain for the defined class.
Returns:The defined, loaded class.
/** * Defines a class for the given class loader. * * @param classLoader The class loader for which a new class should be defined. * @param name The binary name of the class that should be defined. * @param binaryRepresentation The binary representation of the class. * @param protectionDomain The protection domain for the defined class. * @return The defined, loaded class. */
Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, ProtectionDomain protectionDomain);
Looks up a package from a class loader.
Params:
  • classLoader – The class loader to query.
  • name – The binary name of the package.
Returns:The package for the given name as defined by the provided class loader or null if no such package exists.
/** * Looks up a package from a class loader. * * @param classLoader The class loader to query. * @param name The binary name of the package. * @return The package for the given name as defined by the provided class loader or {@code null} if no such package exists. */
Package getPackage(ClassLoader classLoader, String name);
Defines a package for the given class loader.
Params:
  • classLoader – The class loader for which a package is to be defined.
  • name – The binary name of the package.
  • specificationTitle – The specification title of the package or null if no specification title exists.
  • specificationVersion – The specification version of the package or null if no specification version exists.
  • specificationVendor – The specification vendor of the package or null if no specification vendor exists.
  • implementationTitle – The implementation title of the package or null if no implementation title exists.
  • implementationVersion – The implementation version of the package or null if no implementation version exists.
  • implementationVendor – The implementation vendor of the package or null if no implementation vendor exists.
  • sealBase – The seal base URL or null if the package should not be sealed.
Returns:The defined package.
/** * Defines a package for the given class loader. * * @param classLoader The class loader for which a package is to be defined. * @param name The binary name of the package. * @param specificationTitle The specification title of the package or {@code null} if no specification title exists. * @param specificationVersion The specification version of the package or {@code null} if no specification version exists. * @param specificationVendor The specification vendor of the package or {@code null} if no specification vendor exists. * @param implementationTitle The implementation title of the package or {@code null} if no implementation title exists. * @param implementationVersion The implementation version of the package or {@code null} if no implementation version exists. * @param implementationVendor The implementation vendor of the package or {@code null} if no implementation vendor exists. * @param sealBase The seal base URL or {@code null} if the package should not be sealed. * @return The defined package. */
Package definePackage(ClassLoader classLoader, String name, String specificationTitle, String specificationVersion, String specificationVendor, String implementationTitle, String implementationVersion, String implementationVendor, URL sealBase);
Initializes a dispatcher to make non-accessible APIs accessible.
/** * Initializes a dispatcher to make non-accessible APIs accessible. */
interface Initializable {
Indicates if this dispatcher is available.
Returns:true if this dispatcher is available.
/** * Indicates if this dispatcher is available. * * @return {@code true} if this dispatcher is available. */
boolean isAvailable();
Initializes this dispatcher.
Returns:The initialized dispatcher.
/** * Initializes this dispatcher. * * @return The initialized dispatcher. */
Dispatcher initialize();
Represents an unsuccessfully loaded method lookup.
/** * Represents an unsuccessfully loaded method lookup. */
@HashCodeAndEqualsPlugin.Enhance class Unavailable implements Dispatcher, Initializable {
The reason why this dispatcher is not available.
/** * The reason why this dispatcher is not available. */
private final String message;
Creates a new faulty reflection store.
Params:
  • message – The reason why this dispatcher is not available.
/** * Creates a new faulty reflection store. * * @param message The reason why this dispatcher is not available. */
protected Unavailable(String message) { this.message = message; }
{@inheritDoc}
/** * {@inheritDoc} */
public boolean isAvailable() { return false; }
{@inheritDoc}
/** * {@inheritDoc} */
public Dispatcher initialize() { return this; }
{@inheritDoc}
/** * {@inheritDoc} */
public Object getClassLoadingLock(ClassLoader classLoader, String name) { return classLoader; }
{@inheritDoc}
/** * {@inheritDoc} */
public Class<?> findClass(ClassLoader classLoader, String name) { try { return classLoader.loadClass(name); } catch (ClassNotFoundException ignored) { return UNDEFINED; } }
{@inheritDoc}
/** * {@inheritDoc} */
public Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, ProtectionDomain protectionDomain) { throw new UnsupportedOperationException("Cannot define class using reflection: " + message); }
{@inheritDoc}
/** * {@inheritDoc} */
public Package getPackage(ClassLoader classLoader, String name) { throw new UnsupportedOperationException("Cannot get package using reflection: " + message); }
{@inheritDoc}
/** * {@inheritDoc} */
public Package definePackage(ClassLoader classLoader, String name, String specificationTitle, String specificationVersion, String specificationVendor, String implementationTitle, String implementationVersion, String implementationVendor, URL sealBase) { throw new UnsupportedOperationException("Cannot define package using injection: " + message); } } }
A creation action for a dispatcher.
/** * A creation action for a dispatcher. */
enum CreationAction implements PrivilegedAction<Initializable> {
The singleton instance.
/** * The singleton instance. */
INSTANCE;
{@inheritDoc}
/** * {@inheritDoc} */
@SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should not be rethrown but trigger a fallback") public Initializable run() { try { if (JavaModule.isSupported()) { return UsingUnsafe.isAvailable() ? UsingUnsafeInjection.make() : UsingUnsafeOverride.make(); } else { return Direct.make(); } } catch (InvocationTargetException exception) { return new Initializable.Unavailable(exception.getCause().getMessage()); } catch (Exception exception) { return new Initializable.Unavailable(exception.getMessage()); } } }
A class injection dispatcher that is using reflection on the ClassLoader methods.
/** * A class injection dispatcher that is using reflection on the {@link ClassLoader} methods. */
@HashCodeAndEqualsPlugin.Enhance abstract class Direct implements Dispatcher, Initializable { /** * An instance of {@link ClassLoader#findLoadedClass(String)}. */ protected final Method findLoadedClass; /** * An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}. */ protected final Method defineClass;
An instance of ClassLoader.getPackage(String) or ClassLoader#getDefinedPackage(String).
/** * An instance of {@link ClassLoader#getPackage(String)} or {@code ClassLoader#getDefinedPackage(String)}. */
protected final Method getPackage; /** * An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}. */ protected final Method definePackage;
Creates a new direct injection dispatcher.
Params:
/** * Creates a new direct injection dispatcher. * * @param findLoadedClass An instance of {@link ClassLoader#findLoadedClass(String)}. * @param defineClass An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}. * @param getPackage An instance of {@link ClassLoader#getPackage(String)} or {@code ClassLoader#getDefinedPackage(String)}. * @param definePackage An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}. */
protected Direct(Method findLoadedClass, Method defineClass, Method getPackage, Method definePackage) { this.findLoadedClass = findLoadedClass; this.defineClass = defineClass; this.getPackage = getPackage; this.definePackage = definePackage; }
Creates a direct dispatcher.
Throws:
Returns:A direct dispatcher for class injection.
/** * Creates a direct dispatcher. * * @return A direct dispatcher for class injection. * @throws Exception If the creation is impossible. */
@SuppressFBWarnings(value = "DP_DO_INSIDE_DO_PRIVILEGED", justification = "Privilege is explicit caller responsibility") protected static Initializable make() throws Exception { Method getPackage; if (JavaModule.isSupported()) { // Avoid accidental lookup of method with same name in Java 8 J9 VM. try { getPackage = ClassLoader.class.getMethod("getDefinedPackage", String.class); } catch (NoSuchMethodException ignored) { getPackage = ClassLoader.class.getDeclaredMethod("getPackage", String.class); getPackage.setAccessible(true); } } else { getPackage = ClassLoader.class.getDeclaredMethod("getPackage", String.class); getPackage.setAccessible(true); } Method findLoadedClass = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class); findLoadedClass.setAccessible(true); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class, ProtectionDomain.class); defineClass.setAccessible(true); Method definePackage = ClassLoader.class.getDeclaredMethod("definePackage", String.class, String.class, String.class, String.class, String.class, String.class, String.class, URL.class); definePackage.setAccessible(true); try { Method getClassLoadingLock = ClassLoader.class.getDeclaredMethod("getClassLoadingLock", String.class); getClassLoadingLock.setAccessible(true); return new ForJava7CapableVm(findLoadedClass, defineClass, getPackage, definePackage, getClassLoadingLock); } catch (NoSuchMethodException ignored) { return new ForLegacyVm(findLoadedClass, defineClass, getPackage, definePackage); } }
{@inheritDoc}
/** * {@inheritDoc} */
public boolean isAvailable() { return true; }
{@inheritDoc}
/** * {@inheritDoc} */
public Dispatcher initialize() { SecurityManager securityManager = System.getSecurityManager(); if (securityManager != null) { try { securityManager.checkPermission(SUPPRESS_ACCESS_CHECKS); } catch (Exception exception) { return new Dispatcher.Unavailable(exception.getMessage()); } } return this; }
{@inheritDoc}
/** * {@inheritDoc} */
public Class<?> findClass(ClassLoader classLoader, String name) { try { return (Class<?>) findLoadedClass.invoke(classLoader, name); } catch (IllegalAccessException exception) { throw new IllegalStateException("Could not access java.lang.ClassLoader#findClass", exception); } catch (InvocationTargetException exception) { throw new IllegalStateException("Error invoking java.lang.ClassLoader#findClass", exception.getCause()); } }
{@inheritDoc}
/** * {@inheritDoc} */
public Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, ProtectionDomain protectionDomain) { try { return (Class<?>) defineClass.invoke(classLoader, name, binaryRepresentation, 0, binaryRepresentation.length, protectionDomain); } catch (IllegalAccessException exception) { throw new IllegalStateException("Could not access java.lang.ClassLoader#defineClass", exception); } catch (InvocationTargetException exception) { throw new IllegalStateException("Error invoking java.lang.ClassLoader#defineClass", exception.getCause()); } }
{@inheritDoc}
/** * {@inheritDoc} */
public Package getPackage(ClassLoader classLoader, String name) { try { return (Package) getPackage.invoke(classLoader, name); } catch (IllegalAccessException exception) { throw new IllegalStateException("Could not access java.lang.ClassLoader#getPackage", exception); } catch (InvocationTargetException exception) { throw new IllegalStateException("Error invoking java.lang.ClassLoader#getPackage", exception.getCause()); } }
{@inheritDoc}
/** * {@inheritDoc} */
public Package definePackage(ClassLoader classLoader, String name, String specificationTitle, String specificationVersion, String specificationVendor, String implementationTitle, String implementationVersion, String implementationVendor, URL sealBase) { try { return (Package) definePackage.invoke(classLoader, name, specificationTitle, specificationVersion, specificationVendor, implementationTitle, implementationVersion, implementationVendor, sealBase); } catch (IllegalAccessException exception) { throw new IllegalStateException("Could not access java.lang.ClassLoader#definePackage", exception); } catch (InvocationTargetException exception) { throw new IllegalStateException("Error invoking java.lang.ClassLoader#definePackage", exception.getCause()); } }
A resolved class dispatcher for a class injector on a VM running at least Java 7.
/** * A resolved class dispatcher for a class injector on a VM running at least Java 7. */
@HashCodeAndEqualsPlugin.Enhance protected static class ForJava7CapableVm extends Direct {
An instance of ClassLoader#getClassLoadingLock(String).
/** * An instance of {@code ClassLoader#getClassLoadingLock(String)}. */
private final Method getClassLoadingLock;
Creates a new resolved reflection store for a VM running at least Java 7.
Params:
/** * Creates a new resolved reflection store for a VM running at least Java 7. * * @param getClassLoadingLock An instance of {@code ClassLoader#getClassLoadingLock(String)}. * @param findLoadedClass An instance of {@link ClassLoader#findLoadedClass(String)}. * @param defineClass An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}. * @param getPackage An instance of {@link ClassLoader#getPackage(String)} or {@code ClassLoader#getDefinedPackage(String)}. * @param definePackage An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}. */
protected ForJava7CapableVm(Method findLoadedClass, Method defineClass, Method getPackage, Method definePackage, Method getClassLoadingLock) { super(findLoadedClass, defineClass, getPackage, definePackage); this.getClassLoadingLock = getClassLoadingLock; }
{@inheritDoc}
/** * {@inheritDoc} */
public Object getClassLoadingLock(ClassLoader classLoader, String name) { try { return getClassLoadingLock.invoke(classLoader, name); } catch (IllegalAccessException exception) { throw new IllegalStateException("Could not access java.lang.ClassLoader#getClassLoadingLock", exception); } catch (InvocationTargetException exception) { throw new IllegalStateException("Error invoking java.lang.ClassLoader#getClassLoadingLock", exception.getCause()); } } }
A resolved class dispatcher for a class injector prior to Java 7.
/** * A resolved class dispatcher for a class injector prior to Java 7. */
protected static class ForLegacyVm extends Direct {
Creates a new resolved reflection store for a VM prior to Java 8.
Params:
/** * Creates a new resolved reflection store for a VM prior to Java 8. * * @param findLoadedClass An instance of {@link ClassLoader#findLoadedClass(String)}. * @param defineClass An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}. * @param getPackage An instance of {@link ClassLoader#getPackage(String)} or {@code ClassLoader#getDefinedPackage(String)}. * @param definePackage An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}. */
protected ForLegacyVm(Method findLoadedClass, Method defineClass, Method getPackage, Method definePackage) { super(findLoadedClass, defineClass, getPackage, definePackage); }
{@inheritDoc}
/** * {@inheritDoc} */
public Object getClassLoadingLock(ClassLoader classLoader, String name) { return classLoader; } } }
An indirect dispatcher that uses a redirection accessor class that was injected into the bootstrap class loader.
/** * An indirect dispatcher that uses a redirection accessor class that was injected into the bootstrap class loader. */
@HashCodeAndEqualsPlugin.Enhance class UsingUnsafeInjection implements Dispatcher, Initializable {
An instance of the accessor class that is required for using it's intentionally non-static methods.
/** * An instance of the accessor class that is required for using it's intentionally non-static methods. */
private final Object accessor;
The accessor method for using ClassLoader.findLoadedClass(String).
/** * The accessor method for using {@link ClassLoader#findLoadedClass(String)}. */
private final Method findLoadedClass; /** * The accessor method for using {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}. */ private final Method defineClass;
The accessor method for using ClassLoader.getPackage(String) or ClassLoader#getDefinedPackage(String).
/** * The accessor method for using {@link ClassLoader#getPackage(String)} or {@code ClassLoader#getDefinedPackage(String)}. */
private final Method getPackage; /** * The accessor method for using {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}. */ private final Method definePackage;
The accessor method for using ClassLoader#getClassLoadingLock(String) or returning the supplied ClassLoader if this method does not exist on the current VM.
/** * The accessor method for using {@code ClassLoader#getClassLoadingLock(String)} or returning the supplied {@link ClassLoader} * if this method does not exist on the current VM. */
private final Method getClassLoadingLock;
Creates a new class loading injection dispatcher using an unsafe injected dispatcher.
Params:
/** * Creates a new class loading injection dispatcher using an unsafe injected dispatcher. * * @param accessor An instance of the accessor class that is required for using it's intentionally non-static methods. * @param findLoadedClass An instance of {@link ClassLoader#findLoadedClass(String)}. * @param defineClass An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}. * @param getPackage An instance of {@link ClassLoader#getPackage(String)} or {@code ClassLoader#getDefinedPackage(String)}. * @param definePackage An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}. * @param getClassLoadingLock The accessor method for using {@code ClassLoader#getClassLoadingLock(String)} or returning the * supplied {@link ClassLoader} if this method does not exist on the current VM. */
protected UsingUnsafeInjection(Object accessor, Method findLoadedClass, Method defineClass, Method getPackage, Method definePackage, Method getClassLoadingLock) { this.accessor = accessor; this.findLoadedClass = findLoadedClass; this.defineClass = defineClass; this.getPackage = getPackage; this.definePackage = definePackage; this.getClassLoadingLock = getClassLoadingLock; }
Creates an indirect dispatcher.
Throws:
  • Exception – If the dispatcher cannot be created.
Returns:An indirect dispatcher for class creation.
/** * Creates an indirect dispatcher. * * @return An indirect dispatcher for class creation. * @throws Exception If the dispatcher cannot be created. */
@SuppressFBWarnings(value = "DP_DO_INSIDE_DO_PRIVILEGED", justification = "Privilege is explicit caller responsibility") protected static Initializable make() throws Exception { if (Boolean.getBoolean(UsingUnsafe.SAFE_PROPERTY)) { return new Initializable.Unavailable("Use of Unsafe was disabled by system property"); } Class<?> unsafe = Class.forName("sun.misc.Unsafe"); Field theUnsafe = unsafe.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); Object unsafeInstance = theUnsafe.get(null); Method getPackage; if (JavaModule.isSupported()) { // Avoid accidental lookup of method with same name in Java 8 J9 VM. try { getPackage = ClassLoader.class.getDeclaredMethod("getDefinedPackage", String.class); } catch (NoSuchMethodException ignored) { getPackage = ClassLoader.class.getDeclaredMethod("getPackage", String.class); } } else { getPackage = ClassLoader.class.getDeclaredMethod("getPackage", String.class); } getPackage.setAccessible(true); DynamicType.Builder<?> builder = new ByteBuddy() .with(TypeValidation.DISABLED) .subclass(Object.class, ConstructorStrategy.Default.NO_CONSTRUCTORS) .name(ClassLoader.class.getName() + "$ByteBuddyAccessor$" + RandomString.make()) .defineMethod("findLoadedClass", Class.class, Visibility.PUBLIC) .withParameters(ClassLoader.class, String.class) .intercept(MethodCall.invoke(ClassLoader.class .getDeclaredMethod("findLoadedClass", String.class)) .onArgument(0) .withArgument(1)) .defineMethod("defineClass", Class.class, Visibility.PUBLIC) .withParameters(ClassLoader.class, String.class, byte[].class, int.class, int.class, ProtectionDomain.class) .intercept(MethodCall.invoke(ClassLoader.class .getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class, ProtectionDomain.class)) .onArgument(0) .withArgument(1, 2, 3, 4, 5)) .defineMethod("getPackage", Package.class, Visibility.PUBLIC) .withParameters(ClassLoader.class, String.class) .intercept(MethodCall.invoke(getPackage) .onArgument(0) .withArgument(1)) .defineMethod("definePackage", Package.class, Visibility.PUBLIC) .withParameters(ClassLoader.class, String.class, String.class, String.class, String.class, String.class, String.class, String.class, URL.class) .intercept(MethodCall.invoke(ClassLoader.class .getDeclaredMethod("definePackage", String.class, String.class, String.class, String.class, String.class, String.class, String.class, URL.class)) .onArgument(0) .withArgument(1, 2, 3, 4, 5, 6, 7, 8)); try { builder = builder.defineMethod("getClassLoadingLock", Object.class, Visibility.PUBLIC) .withParameters(ClassLoader.class, String.class) .intercept(MethodCall.invoke(ClassLoader.class.getDeclaredMethod("getClassLoadingLock", String.class)) .onArgument(0) .withArgument(1)); } catch (NoSuchMethodException ignored) { builder = builder.defineMethod("getClassLoadingLock", Object.class, Visibility.PUBLIC) .withParameters(ClassLoader.class, String.class) .intercept(FixedValue.argument(0)); } Class<?> type = builder.make() .load(ClassLoadingStrategy.BOOTSTRAP_LOADER, new ClassLoadingStrategy.ForUnsafeInjection()) .getLoaded(); return new UsingUnsafeInjection( unsafe.getMethod("allocateInstance", Class.class).invoke(unsafeInstance, type), type.getMethod("findLoadedClass", ClassLoader.class, String.class), type.getMethod("defineClass", ClassLoader.class, String.class, byte[].class, int.class, int.class, ProtectionDomain.class), type.getMethod("getPackage", ClassLoader.class, String.class), type.getMethod("definePackage", ClassLoader.class, String.class, String.class, String.class, String.class, String.class, String.class, String.class, URL.class), type.getMethod("getClassLoadingLock", ClassLoader.class, String.class)); }
{@inheritDoc}
/** * {@inheritDoc} */
public boolean isAvailable() { return true; }
{@inheritDoc}
/** * {@inheritDoc} */
public Dispatcher initialize() { SecurityManager securityManager = System.getSecurityManager(); if (securityManager != null) { try { securityManager.checkPermission(SUPPRESS_ACCESS_CHECKS); } catch (Exception exception) { return new Dispatcher.Unavailable(exception.getMessage()); } } return this; }
{@inheritDoc}
/** * {@inheritDoc} */
public Object getClassLoadingLock(ClassLoader classLoader, String name) { try { return getClassLoadingLock.invoke(accessor, classLoader, name); } catch (IllegalAccessException exception) { throw new IllegalStateException("Could not access (accessor)::getClassLoadingLock", exception); } catch (InvocationTargetException exception) { throw new IllegalStateException("Error invoking (accessor)::getClassLoadingLock", exception.getCause()); } }
{@inheritDoc}
/** * {@inheritDoc} */
public Class<?> findClass(ClassLoader classLoader, String name) { try { return (Class<?>) findLoadedClass.invoke(accessor, classLoader, name); } catch (IllegalAccessException exception) { throw new IllegalStateException("Could not access (accessor)::findLoadedClass", exception); } catch (InvocationTargetException exception) { throw new IllegalStateException("Error invoking (accessor)::findLoadedClass", exception.getCause()); } }
{@inheritDoc}
/** * {@inheritDoc} */
public Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, ProtectionDomain protectionDomain) { try { return (Class<?>) defineClass.invoke(accessor, classLoader, name, binaryRepresentation, 0, binaryRepresentation.length, protectionDomain); } catch (IllegalAccessException exception) { throw new IllegalStateException("Could not access (accessor)::defineClass", exception); } catch (InvocationTargetException exception) { throw new IllegalStateException("Error invoking (accessor)::defineClass", exception.getCause()); } }
{@inheritDoc}
/** * {@inheritDoc} */
public Package getPackage(ClassLoader classLoader, String name) { try { return (Package) getPackage.invoke(accessor, classLoader, name); } catch (IllegalAccessException exception) { throw new IllegalStateException("Could not access (accessor)::getPackage", exception); } catch (InvocationTargetException exception) { throw new IllegalStateException("Error invoking (accessor)::getPackage", exception.getCause()); } }
{@inheritDoc}
/** * {@inheritDoc} */
public Package definePackage(ClassLoader classLoader, String name, String specificationTitle, String specificationVersion, String specificationVendor, String implementationTitle, String implementationVersion, String implementationVendor, URL sealBase) { try { return (Package) definePackage.invoke(accessor, classLoader, name, specificationTitle, specificationVersion, specificationVendor, implementationTitle, implementationVersion, implementationVendor, sealBase); } catch (IllegalAccessException exception) { throw new IllegalStateException("Could not access (accessor)::definePackage", exception); } catch (InvocationTargetException exception) { throw new IllegalStateException("Error invoking (accessor)::definePackage", exception.getCause()); } } }
A dispatcher implementation that uses sun.misc.Unsafe#putBoolean to set the AccessibleObject field for making methods accessible.
/** * A dispatcher implementation that uses {@code sun.misc.Unsafe#putBoolean} to set the {@link AccessibleObject} field * for making methods accessible. */
abstract class UsingUnsafeOverride implements Dispatcher, Initializable { /** * An instance of {@link ClassLoader#findLoadedClass(String)}. */ protected final Method findLoadedClass; /** * An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}. */ protected final Method defineClass;
An instance of ClassLoader.getPackage(String) or ClassLoader#getDefinedPackage(String).
/** * An instance of {@link ClassLoader#getPackage(String)} or {@code ClassLoader#getDefinedPackage(String)}. */
protected final Method getPackage; /** * An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}. */ protected final Method definePackage;
Creates a new unsafe field injecting injection dispatcher.
Params:
/** * Creates a new unsafe field injecting injection dispatcher. * * @param findLoadedClass An instance of {@link ClassLoader#findLoadedClass(String)}. * @param defineClass An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}. * @param getPackage An instance of {@link ClassLoader#getPackage(String)} or {@code ClassLoader#getDefinedPackage(String)}. * @param definePackage An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}. */
protected UsingUnsafeOverride(Method findLoadedClass, Method defineClass, Method getPackage, Method definePackage) { this.findLoadedClass = findLoadedClass; this.defineClass = defineClass; this.getPackage = getPackage; this.definePackage = definePackage; }
Creates a new initializable class injector using an unsafe field injection.
Throws:
  • Exception – If the injector cannot be created.
Returns:An appropriate initializable.
/** * Creates a new initializable class injector using an unsafe field injection. * * @return An appropriate initializable. * @throws Exception If the injector cannot be created. */
@SuppressFBWarnings(value = "DP_DO_INSIDE_DO_PRIVILEGED", justification = "Privilege is explicit caller responsibility") protected static Initializable make() throws Exception { if (Boolean.getBoolean(UsingUnsafe.SAFE_PROPERTY)) { return new Initializable.Unavailable("Use of Unsafe was disabled by system property"); } Class<?> unsafeType = Class.forName("sun.misc.Unsafe"); Field theUnsafe = unsafeType.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); Object unsafe = theUnsafe.get(null); Field override; try { override = AccessibleObject.class.getDeclaredField("override"); } catch (NoSuchFieldException ignored) { // Since Java 12, the override field is hidden from the reflection API. To circumvent this, we // create a mirror class of AccessibleObject that defines the same fields and has the same field // layout such that the override field will receive the same class offset. Doing so, we can write to // the offset location and still set a value to it, despite it being hidden from the reflection API. override = new ByteBuddy() .redefine(AccessibleObject.class) .name("net.bytebuddy.mirror." + AccessibleObject.class.getSimpleName()) .noNestMate() .visit(new MemberRemoval().stripInvokables(any())) .make() .load(AccessibleObject.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER) .getLoaded() .getDeclaredField("override"); } long offset = (Long) unsafeType .getMethod("objectFieldOffset", Field.class) .invoke(unsafe, override); Method putBoolean = unsafeType.getMethod("putBoolean", Object.class, long.class, boolean.class); Method getPackage; if (JavaModule.isSupported()) { // Avoid accidental lookup of method with same name in Java 8 J9 VM. try { getPackage = ClassLoader.class.getMethod("getDefinedPackage", String.class); } catch (NoSuchMethodException ignored) { getPackage = ClassLoader.class.getDeclaredMethod("getPackage", String.class); putBoolean.invoke(unsafe, getPackage, offset, true); } } else { getPackage = ClassLoader.class.getDeclaredMethod("getPackage", String.class); putBoolean.invoke(unsafe, getPackage, offset, true); } Method findLoadedClass = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class); Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class, ProtectionDomain.class); Method definePackage = ClassLoader.class.getDeclaredMethod("definePackage", String.class, String.class, String.class, String.class, String.class, String.class, String.class, URL.class); putBoolean.invoke(unsafe, defineClass, offset, true); putBoolean.invoke(unsafe, findLoadedClass, offset, true); putBoolean.invoke(unsafe, definePackage, offset, true); try { Method getClassLoadingLock = ClassLoader.class.getDeclaredMethod("getClassLoadingLock", String.class); putBoolean.invoke(unsafe, getClassLoadingLock, offset, true); return new ForJava7CapableVm(findLoadedClass, defineClass, getPackage, definePackage, getClassLoadingLock); } catch (NoSuchMethodException ignored) { return new ForLegacyVm(findLoadedClass, defineClass, getPackage, definePackage); } }
{@inheritDoc}
/** * {@inheritDoc} */
public boolean isAvailable() { return true; }
{@inheritDoc}
/** * {@inheritDoc} */
public Dispatcher initialize() { SecurityManager securityManager = System.getSecurityManager(); if (securityManager != null) { try { securityManager.checkPermission(SUPPRESS_ACCESS_CHECKS); } catch (Exception exception) { return new Dispatcher.Unavailable(exception.getMessage()); } } return this; }
{@inheritDoc}
/** * {@inheritDoc} */
public Class<?> findClass(ClassLoader classLoader, String name) { try { return (Class<?>) findLoadedClass.invoke(classLoader, name); } catch (IllegalAccessException exception) { throw new IllegalStateException("Could not access java.lang.ClassLoader#findClass", exception); } catch (InvocationTargetException exception) { throw new IllegalStateException("Error invoking java.lang.ClassLoader#findClass", exception.getCause()); } }
{@inheritDoc}
/** * {@inheritDoc} */
public Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, ProtectionDomain protectionDomain) { try { return (Class<?>) defineClass.invoke(classLoader, name, binaryRepresentation, 0, binaryRepresentation.length, protectionDomain); } catch (IllegalAccessException exception) { throw new IllegalStateException("Could not access java.lang.ClassLoader#defineClass", exception); } catch (InvocationTargetException exception) { throw new IllegalStateException("Error invoking java.lang.ClassLoader#defineClass", exception.getCause()); } }
{@inheritDoc}
/** * {@inheritDoc} */
public Package getPackage(ClassLoader classLoader, String name) { try { return (Package) getPackage.invoke(classLoader, name); } catch (IllegalAccessException exception) { throw new IllegalStateException("Could not access java.lang.ClassLoader#getPackage", exception); } catch (InvocationTargetException exception) { throw new IllegalStateException("Error invoking java.lang.ClassLoader#getPackage", exception.getCause()); } }
{@inheritDoc}
/** * {@inheritDoc} */
public Package definePackage(ClassLoader classLoader, String name, String specificationTitle, String specificationVersion, String specificationVendor, String implementationTitle, String implementationVersion, String implementationVendor, URL sealBase) { try { return (Package) definePackage.invoke(classLoader, name, specificationTitle, specificationVersion, specificationVendor, implementationTitle, implementationVersion, implementationVendor, sealBase); } catch (IllegalAccessException exception) { throw new IllegalStateException("Could not access java.lang.ClassLoader#definePackage", exception); } catch (InvocationTargetException exception) { throw new IllegalStateException("Error invoking java.lang.ClassLoader#definePackage", exception.getCause()); } }
A resolved class dispatcher using unsafe field injection for a class injector on a VM running at least Java 7.
/** * A resolved class dispatcher using unsafe field injection for a class injector on a VM running at least Java 7. */
@HashCodeAndEqualsPlugin.Enhance protected static class ForJava7CapableVm extends UsingUnsafeOverride {
An instance of ClassLoader#getClassLoadingLock(String).
/** * An instance of {@code ClassLoader#getClassLoadingLock(String)}. */
private final Method getClassLoadingLock;
Creates a new resolved class injector using unsafe field injection for a VM running at least Java 7.
Params:
/** * Creates a new resolved class injector using unsafe field injection for a VM running at least Java 7. * * @param getClassLoadingLock An instance of {@code ClassLoader#getClassLoadingLock(String)}. * @param findLoadedClass An instance of {@link ClassLoader#findLoadedClass(String)}. * @param defineClass An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}. * @param getPackage An instance of {@link ClassLoader#getPackage(String)} or {@code ClassLoader#getDefinedPackage(String)}. * @param definePackage An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}. */
protected ForJava7CapableVm(Method findLoadedClass, Method defineClass, Method getPackage, Method definePackage, Method getClassLoadingLock) { super(findLoadedClass, defineClass, getPackage, definePackage); this.getClassLoadingLock = getClassLoadingLock; }
{@inheritDoc}
/** * {@inheritDoc} */
public Object getClassLoadingLock(ClassLoader classLoader, String name) { try { return getClassLoadingLock.invoke(classLoader, name); } catch (IllegalAccessException exception) { throw new IllegalStateException("Could not access java.lang.ClassLoader#getClassLoadingLock", exception); } catch (InvocationTargetException exception) { throw new IllegalStateException("Error invoking java.lang.ClassLoader#getClassLoadingLock", exception.getCause()); } } }
A resolved class dispatcher using unsafe field injection for a class injector prior to Java 7.
/** * A resolved class dispatcher using unsafe field injection for a class injector prior to Java 7. */
protected static class ForLegacyVm extends UsingUnsafeOverride {
Creates a new resolved class injector using unsafe field injection for a VM prior to Java 7.
Params:
/** * Creates a new resolved class injector using unsafe field injection for a VM prior to Java 7. * * @param findLoadedClass An instance of {@link ClassLoader#findLoadedClass(String)}. * @param defineClass An instance of {@link ClassLoader#defineClass(String, byte[], int, int, ProtectionDomain)}. * @param getPackage An instance of {@link ClassLoader#getPackage(String)} or {@code ClassLoader#getDefinedPackage(String)}. * @param definePackage An instance of {@link ClassLoader#definePackage(String, String, String, String, String, String, String, URL)}. */
protected ForLegacyVm(Method findLoadedClass, Method defineClass, Method getPackage, Method definePackage) { super(findLoadedClass, defineClass, getPackage, definePackage); }
{@inheritDoc}
/** * {@inheritDoc} */
public Object getClassLoadingLock(ClassLoader classLoader, String name) { return classLoader; } } }
Represents an unsuccessfully loaded method lookup.
/** * Represents an unsuccessfully loaded method lookup. */
@HashCodeAndEqualsPlugin.Enhance class Unavailable implements Dispatcher {
The error message being displayed.
/** * The error message being displayed. */
private final String message;
Creates a dispatcher for a VM that does not support reflective injection.
Params:
  • message – The error message being displayed.
/** * Creates a dispatcher for a VM that does not support reflective injection. * * @param message The error message being displayed. */
protected Unavailable(String message) { this.message = message; }
{@inheritDoc}
/** * {@inheritDoc} */
public Object getClassLoadingLock(ClassLoader classLoader, String name) { return classLoader; }
{@inheritDoc}
/** * {@inheritDoc} */
public Class<?> findClass(ClassLoader classLoader, String name) { try { return classLoader.loadClass(name); } catch (ClassNotFoundException ignored) { return UNDEFINED; } }
{@inheritDoc}
/** * {@inheritDoc} */
public Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, ProtectionDomain protectionDomain) { throw new UnsupportedOperationException("Cannot define class using reflection: " + message); }
{@inheritDoc}
/** * {@inheritDoc} */
public Package getPackage(ClassLoader classLoader, String name) { throw new UnsupportedOperationException("Cannot get package using reflection: " + message); }
{@inheritDoc}
/** * {@inheritDoc} */
public Package definePackage(ClassLoader classLoader, String name, String specificationTitle, String specificationVersion, String specificationVendor, String implementationTitle, String implementationVersion, String implementationVendor, URL sealBase) { throw new UnsupportedOperationException("Cannot define package using injection: " + message); } } } }

A class injector that uses a java.lang.invoke.MethodHandles$Lookup object for defining a class.

Important: This functionality is only available starting from Java 9.

/** * <p> * A class injector that uses a {@code java.lang.invoke.MethodHandles$Lookup} object for defining a class. * </p> * <p> * <b>Important</b>: This functionality is only available starting from Java 9. * </p> */
@HashCodeAndEqualsPlugin.Enhance class UsingLookup extends AbstractBase {
The dispatcher to interacting with method handles.
/** * The dispatcher to interacting with method handles. */
private static final Dispatcher DISPATCHER = AccessController.doPrivileged(Dispatcher.Creator.INSTANCE);
Indicates a lookup instance's package lookup mode.
/** * Indicates a lookup instance's package lookup mode. */
private static final int PACKAGE_LOOKUP = 0x8;
The java.lang.invoke.MethodHandles$Lookup to use.
/** * The {@code java.lang.invoke.MethodHandles$Lookup} to use. */
private final Object lookup;
Creates a new class injector using a lookup instance.
Params:
  • lookup – The java.lang.invoke.MethodHandles$Lookup instance to use.
/** * Creates a new class injector using a lookup instance. * * @param lookup The {@code java.lang.invoke.MethodHandles$Lookup} instance to use. */
protected UsingLookup(Object lookup) { this.lookup = lookup; }
Creates class injector that defines a class using a method handle lookup.
Params:
  • lookup – The java.lang.invoke.MethodHandles$Lookup instance to use.
Returns:An appropriate class injector.
/** * Creates class injector that defines a class using a method handle lookup. * * @param lookup The {@code java.lang.invoke.MethodHandles$Lookup} instance to use. * @return An appropriate class injector. */
public static UsingLookup of(Object lookup) { if (!DISPATCHER.isAlive()) { throw new IllegalStateException("The current VM does not support class definition via method handle lookups"); } else if (!JavaType.METHOD_HANDLES_LOOKUP.isInstance(lookup)) { throw new IllegalArgumentException("Not a method handle lookup: " + lookup); } else if ((DISPATCHER.lookupModes(lookup) & PACKAGE_LOOKUP) == 0) { throw new IllegalArgumentException("Lookup does not imply package-access: " + lookup); } return new UsingLookup(DISPATCHER.dropLookupMode(lookup, Opcodes.ACC_PRIVATE)); }
Returns the lookup type this injector is based upon.
Returns:The lookup type.
/** * Returns the lookup type this injector is based upon. * * @return The lookup type. */
public Class<?> lookupType() { return DISPATCHER.lookupType(lookup); }
Resolves this injector to use the supplied type's scope.
Params:
  • type – The type to resolve the access scope for.
Returns:An new injector with the specified scope.
/** * Resolves this injector to use the supplied type's scope. * * @param type The type to resolve the access scope for. * @return An new injector with the specified scope. */
public UsingLookup in(Class<?> type) { return new UsingLookup(DISPATCHER.resolve(lookup, type)); }
{@inheritDoc}
/** * {@inheritDoc} */
public boolean isAlive() { return isAvailable(); }
{@inheritDoc}
/** * {@inheritDoc} */
public Map<String, Class<?>> injectRaw(Map<? extends String, byte[]> types) { String expectedPackage = TypeDescription.ForLoadedType.of(lookupType()).getPackage().getName(); Map<String, Class<?>> result = new HashMap<String, Class<?>>(); for (Map.Entry<? extends String, byte[]> entry : types.entrySet()) { int index = entry.getKey().lastIndexOf('.'); if (!expectedPackage.equals(index == -1 ? "" : entry.getKey().substring(0, index))) { throw new IllegalArgumentException(entry.getKey() + " must be defined in the same package as " + lookup); } result.put(entry.getKey(), DISPATCHER.defineClass(lookup, entry.getValue())); } return result; }
Checks if the current VM is capable of defining classes using a method handle lookup.
Returns:true if the current VM is capable of defining classes using a lookup.
/** * Checks if the current VM is capable of defining classes using a method handle lookup. * * @return {@code true} if the current VM is capable of defining classes using a lookup. */
public static boolean isAvailable() { return DISPATCHER.isAlive(); }
A dispatcher for interacting with a method handle lookup.
/** * A dispatcher for interacting with a method handle lookup. */
protected interface Dispatcher {
Indicates if this dispatcher is available on the current VM.
Returns:true if this dispatcher is alive.
/** * Indicates if this dispatcher is available on the current VM. * * @return {@code true} if this dispatcher is alive. */
boolean isAlive();
Returns the lookup type for a given method handle lookup.
Params:
  • lookup – The lookup instance.
Returns:The lookup type.
/** * Returns the lookup type for a given method handle lookup. * * @param lookup The lookup instance. * @return The lookup type. */
Class<?> lookupType(Object lookup);
Returns a lookup objects lookup types.
Params:
  • lookup – The lookup instance.
Returns:The modifiers indicating the instance's lookup modes.
/** * Returns a lookup objects lookup types. * * @param lookup The lookup instance. * @return The modifiers indicating the instance's lookup modes. */
int lookupModes(Object lookup);
Drops a given lookup mode from a lookup instance.
Params:
  • lookup – The lookup instance.
  • mode – The modes to drop.
Returns:A new lookup instance where the modes were dropped.
/** * Drops a given lookup mode from a lookup instance. * * @param lookup The lookup instance. * @param mode The modes to drop. * @return A new lookup instance where the modes were dropped. */
Object dropLookupMode(Object lookup, int mode);
Resolves the supplied lookup instance's access scope for the supplied type.
Params:
  • lookup – The lookup to use.
  • type – The type to resolve the scope for.
Returns:An appropriate lookup instance.
/** * Resolves the supplied lookup instance's access scope for the supplied type. * * @param lookup The lookup to use. * @param type The type to resolve the scope for. * @return An appropriate lookup instance. */
Object resolve(Object lookup, Class<?> type);
Defines a class.
Params:
  • lookup – The java.lang.invoke.MethodHandles$Lookup instance to use.
  • binaryRepresentation – The defined class's binary representation.
Returns:The defined class.
/** * Defines a class. * * @param lookup The {@code java.lang.invoke.MethodHandles$Lookup} instance to use. * @param binaryRepresentation The defined class's binary representation. * @return The defined class. */
Class<?> defineClass(Object lookup, byte[] binaryRepresentation);
An action for defining a dispatcher.
/** * An action for defining a dispatcher. */
enum Creator implements PrivilegedAction<Dispatcher> {
The singleton instance.
/** * The singleton instance. */
INSTANCE;
{@inheritDoc}
/** * {@inheritDoc} */
@SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should not be rethrown but trigger a fallback") public Dispatcher run() { try { Class<?> lookup = JavaType.METHOD_HANDLES_LOOKUP.load(); return new Dispatcher.ForJava9CapableVm(JavaType.METHOD_HANDLES.load().getMethod("privateLookupIn", Class.class, lookup), lookup.getMethod("lookupClass"), lookup.getMethod("lookupModes"), lookup.getMethod("dropLookupMode", int.class), lookup.getMethod("defineClass", byte[].class)); } catch (Exception ignored) { return Dispatcher.ForLegacyVm.INSTANCE; } } }
A dispatcher for a legacy VM that does not support class definition via method handles.
/** * A dispatcher for a legacy VM that does not support class definition via method handles. */
enum ForLegacyVm implements Dispatcher {
The singleton instance.
/** * The singleton instance. */
INSTANCE;
{@inheritDoc}
/** * {@inheritDoc} */
public boolean isAlive() { return false; }
{@inheritDoc}
/** * {@inheritDoc} */
public Class<?> lookupType(Object lookup) { throw new IllegalStateException("Cannot dispatch method for java.lang.invoke.MethodHandles$Lookup"); }
{@inheritDoc}
/** * {@inheritDoc} */
public int lookupModes(Object lookup) { throw new IllegalStateException("Cannot dispatch method for java.lang.invoke.MethodHandles$Lookup"); }
{@inheritDoc}
/** * {@inheritDoc} */
public Object dropLookupMode(Object lookup, int mode) { throw new IllegalStateException("Cannot dispatch method for java.lang.invoke.MethodHandles$Lookup"); }
{@inheritDoc}
/** * {@inheritDoc} */
public Object resolve(Object lookup, Class<?> type) { throw new IllegalStateException("Cannot dispatch method for java.lang.invoke.MethodHandles"); }
{@inheritDoc}
/** * {@inheritDoc} */
public Class<?> defineClass(Object lookup, byte[] binaryRepresentation) { throw new IllegalStateException("Cannot dispatch method for java.lang.invoke.MethodHandles$Lookup"); } }
A dispatcher for a Java 9 capable VM that supports class definition via method handles.
/** * A dispatcher for a Java 9 capable VM that supports class definition via method handles. */
@HashCodeAndEqualsPlugin.Enhance class ForJava9CapableVm implements Dispatcher {
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 java.lang.invoke.MethodHandles$#privateLookupIn method.
/** * The {@code java.lang.invoke.MethodHandles$#privateLookupIn} method. */
private final Method privateLookupIn;
The java.lang.invoke.MethodHandles$Lookup#lookupClass method.
/** * The {@code java.lang.invoke.MethodHandles$Lookup#lookupClass} method. */
private final Method lookupClass;
The java.lang.invoke.MethodHandles$Lookup#lookupModes method.
/** * The {@code java.lang.invoke.MethodHandles$Lookup#lookupModes} method. */
private final Method lookupModes;
The java.lang.invoke.MethodHandles$Lookup#dropLookupMode method.
/** * The {@code java.lang.invoke.MethodHandles$Lookup#dropLookupMode} method. */
private final Method dropLookupMode;
The java.lang.invoke.MethodHandles$Lookup#defineClass method.
/** * The {@code java.lang.invoke.MethodHandles$Lookup#defineClass} method. */
private final Method defineClass;
Creates a new dispatcher for a Java 9 capable VM.
Params:
  • privateLookupIn – The java.lang.invoke.MethodHandles$#privateLookupIn method.
  • lookupClass – The java.lang.invoke.MethodHandles$Lookup#lookupClass method.
  • lookupModes – The java.lang.invoke.MethodHandles$Lookup#lookupModes method.
  • dropLookupMode – The java.lang.invoke.MethodHandles$Lookup#dropLookupMode method.
  • defineClass – The java.lang.invoke.MethodHandles$Lookup#defineClass method.
/** * Creates a new dispatcher for a Java 9 capable VM. * * @param privateLookupIn The {@code java.lang.invoke.MethodHandles$#privateLookupIn} method. * @param lookupClass The {@code java.lang.invoke.MethodHandles$Lookup#lookupClass} method. * @param lookupModes The {@code java.lang.invoke.MethodHandles$Lookup#lookupModes} method. * @param dropLookupMode The {@code java.lang.invoke.MethodHandles$Lookup#dropLookupMode} method. * @param defineClass The {@code java.lang.invoke.MethodHandles$Lookup#defineClass} method. */
protected ForJava9CapableVm(Method privateLookupIn, Method lookupClass, Method lookupModes, Method dropLookupMode, Method defineClass) { this.privateLookupIn = privateLookupIn; this.lookupClass = lookupClass; this.lookupModes = lookupModes; this.defineClass = defineClass; this.dropLookupMode = dropLookupMode; }
{@inheritDoc}
/** * {@inheritDoc} */
public boolean isAlive() { return true; }
{@inheritDoc}
/** * {@inheritDoc} */
public Class<?> lookupType(Object lookup) { try { return (Class<?>) lookupClass.invoke(lookup, NO_ARGUMENTS); } catch (IllegalAccessException exception) { throw new IllegalStateException("Cannot access java.lang.invoke.MethodHandles$Lookup#lookupClass", exception); } catch (InvocationTargetException exception) { throw new IllegalStateException("Error invoking java.lang.invoke.MethodHandles$Lookup#lookupClass", exception.getCause()); } }
{@inheritDoc}
/** * {@inheritDoc} */
public int lookupModes(Object lookup) { try { return (Integer) lookupModes.invoke(lookup, NO_ARGUMENTS); } catch (IllegalAccessException exception) { throw new IllegalStateException("Cannot access java.lang.invoke.MethodHandles$Lookup#lookupModes", exception); } catch (InvocationTargetException exception) { throw new IllegalStateException("Error invoking java.lang.invoke.MethodHandles$Lookup#lookupModes", exception.getCause()); } }
{@inheritDoc}
/** * {@inheritDoc} */
public Object dropLookupMode(Object lookup, int mode) { try { return dropLookupMode.invoke(lookup, mode); } catch (IllegalAccessException exception) { throw new IllegalStateException("Cannot access java.lang.invoke.MethodHandles$Lookup#lookupModes", exception); } catch (InvocationTargetException exception) { throw new IllegalStateException("Error invoking java.lang.invoke.MethodHandles$Lookup#lookupModes", exception.getCause()); } }
{@inheritDoc}
/** * {@inheritDoc} */
public Object resolve(Object lookup, Class<?> type) { try { return privateLookupIn.invoke(null, type, lookup); } catch (IllegalAccessException exception) { throw new IllegalStateException("Cannot access java.lang.invoke.MethodHandles#privateLookupIn", exception); } catch (InvocationTargetException exception) { throw new IllegalStateException("Error invoking java.lang.invoke.MethodHandles#privateLookupIn", exception.getCause()); } }
{@inheritDoc}
/** * {@inheritDoc} */
public Class<?> defineClass(Object lookup, byte[] binaryRepresentation) { try { return (Class<?>) defineClass.invoke(lookup, (Object) binaryRepresentation); } catch (IllegalAccessException exception) { throw new IllegalStateException("Cannot access java.lang.invoke.MethodHandles$Lookup#defineClass", exception); } catch (InvocationTargetException exception) { throw new IllegalStateException("Error invoking java.lang.invoke.MethodHandles$Lookup#defineClass", exception.getCause()); } } } } }

A class injector that uses sun.misc.Unsafe to inject classes.

Important: This strategy is no longer available after Java 11.

/** * <p> * A class injector that uses {@code sun.misc.Unsafe} to inject classes. * </p> * <p> * <b>Important</b>: This strategy is no longer available after Java 11. * </p> */
@HashCodeAndEqualsPlugin.Enhance class UsingUnsafe extends AbstractBase {
If this property is set, Byte Buddy does not make use of any Unsafe class.
/** * If this property is set, Byte Buddy does not make use of any {@code Unsafe} class. */
public static final String SAFE_PROPERTY = "net.bytebuddy.safe";
The dispatcher to use.
/** * The dispatcher to use. */
private static final Dispatcher.Initializable DISPATCHER = AccessController.doPrivileged(Dispatcher.CreationAction.INSTANCE);
A lock for the bootstrap loader when injecting code.
/** * A lock for the bootstrap loader when injecting code. */
private static final Object BOOTSTRAP_LOADER_LOCK = new Object();
The class loader to inject classes into or null for the bootstrap loader.
/** * The class loader to inject classes into or {@code null} for the bootstrap loader. */
@HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY) private final ClassLoader classLoader;
The protection domain to use or null for no protection domain.
/** * The protection domain to use or {@code null} for no protection domain. */
@HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY) private final ProtectionDomain protectionDomain;
Creates a new unsafe injector for the given class loader with a default protection domain.
Params:
  • classLoader – The class loader to inject classes into or null for the bootstrap loader.
/** * Creates a new unsafe injector for the given class loader with a default protection domain. * * @param classLoader The class loader to inject classes into or {@code null} for the bootstrap loader. */
public UsingUnsafe(ClassLoader classLoader) { this(classLoader, ClassLoadingStrategy.NO_PROTECTION_DOMAIN); }
Creates a new unsafe injector for the given class loader with a default protection domain.
Params:
  • classLoader – The class loader to inject classes into or null for the bootstrap loader.
  • protectionDomain – The protection domain to use or null for no protection domain.
/** * Creates a new unsafe injector for the given class loader with a default protection domain. * * @param classLoader The class loader to inject classes into or {@code null} for the bootstrap loader. * @param protectionDomain The protection domain to use or {@code null} for no protection domain. */
public UsingUnsafe(ClassLoader classLoader, ProtectionDomain protectionDomain) { this.classLoader = classLoader; this.protectionDomain = protectionDomain; }
{@inheritDoc}
/** * {@inheritDoc} */
public boolean isAlive() { return isAvailable(); }
{@inheritDoc}
/** * {@inheritDoc} */
public Map<String, Class<?>> injectRaw(Map<? extends String, byte[]> types) { Dispatcher dispatcher = DISPATCHER.initialize(); Map<String, Class<?>> result = new HashMap<String, Class<?>>(); synchronized (classLoader == null ? BOOTSTRAP_LOADER_LOCK : classLoader) { for (Map.Entry<? extends String, byte[]> entry : types.entrySet()) { try { result.put(entry.getKey(), Class.forName(entry.getKey(), false, classLoader)); } catch (ClassNotFoundException ignored) { result.put(entry.getKey(), dispatcher.defineClass(classLoader, entry.getKey(), entry.getValue(), protectionDomain)); } } } return result; }
Checks if unsafe class injection is available on the current VM.
Returns:true if unsafe class injection is available on the current VM.
/** * Checks if unsafe class injection is available on the current VM. * * @return {@code true} if unsafe class injection is available on the current VM. */
public static boolean isAvailable() { return DISPATCHER.isAvailable(); }
Returns an unsafe class injector for the system class loader.
Returns:A class injector for the system class loader.
/** * Returns an unsafe class injector for the system class loader. * * @return A class injector for the system class loader. */
public static ClassInjector ofSystemLoader() { return new UsingUnsafe(ClassLoader.getSystemClassLoader()); }
Returns an unsafe class injector for the platform class loader. For VMs of version 8 or older, the extension class loader is represented instead.
Returns:A class injector for the platform class loader.
/** * Returns an unsafe class injector for the platform class loader. For VMs of version 8 or older, * the extension class loader is represented instead. * * @return A class injector for the platform class loader. */
public static ClassInjector ofPlatformLoader() { return new UsingUnsafe(ClassLoader.getSystemClassLoader().getParent()); }
Returns an unsafe class injector for the boot class loader.
Returns:A class injector for the boot loader.
/** * Returns an unsafe class injector for the boot class loader. * * @return A class injector for the boot loader. */
public static ClassInjector ofBootLoader() { return new UsingUnsafe(ClassLoadingStrategy.BOOTSTRAP_LOADER); }
A dispatcher for using sun.misc.Unsafe.
/** * A dispatcher for using {@code sun.misc.Unsafe}. */
protected interface Dispatcher {
Defines a class.
Params:
  • classLoader – The class loader to inject the class into.
  • name – The type's name.
  • binaryRepresentation – The type's binary representation.
  • protectionDomain – The type's protection domain.
Returns:The defined class.
/** * Defines a class. * * @param classLoader The class loader to inject the class into. * @param name The type's name. * @param binaryRepresentation The type's binary representation. * @param protectionDomain The type's protection domain. * @return The defined class. */
Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, ProtectionDomain protectionDomain);
A class injection dispatcher that is not yet initialized.
/** * A class injection dispatcher that is not yet initialized. */
interface Initializable {
Checks if unsafe class injection is available on the current VM.
Returns:true if unsafe class injection is available.
/** * Checks if unsafe class injection is available on the current VM. * * @return {@code true} if unsafe class injection is available. */
boolean isAvailable();
Initializes the dispatcher.
Returns:The initialized dispatcher.
/** * Initializes the dispatcher. * * @return The initialized dispatcher. */
Dispatcher initialize(); }
A privileged action for creating a dispatcher.
/** * A privileged action for creating a dispatcher. */
enum CreationAction implements PrivilegedAction<Initializable> {
The singleton instance.
/** * The singleton instance. */
INSTANCE;
{@inheritDoc}
/** * {@inheritDoc} */
@SuppressFBWarnings(value = "REC_CATCH_EXCEPTION", justification = "Exception should not be rethrown but trigger a fallback") public Initializable run() { if (Boolean.getBoolean(SAFE_PROPERTY)) { return new Unavailable("Use of Unsafe was disabled by system property"); } try { Class<?> unsafeType = Class.forName("sun.misc.Unsafe"); Field theUnsafe = unsafeType.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); Object unsafe = theUnsafe.get(null); try { Method defineClass = unsafeType.getMethod("defineClass", String.class, byte[].class, int.class, int.class, ClassLoader.class, ProtectionDomain.class); defineClass.setAccessible(true); return new Enabled(unsafe, defineClass); } catch (Exception exception) { try { Field override; try { override = AccessibleObject.class.getDeclaredField("override"); } catch (NoSuchFieldException ignored) { // Since Java 12, the override field is hidden from the reflection API. To circumvent this, we // create a mirror class of AccessibleObject that defines the same fields and has the same field // layout such that the override field will receive the same class offset. Doing so, we can write to // the offset location and still set a value to it, despite it being hidden from the reflection API. override = new ByteBuddy() .redefine(AccessibleObject.class) .name("net.bytebuddy.mirror." + AccessibleObject.class.getSimpleName()) .noNestMate() .visit(new MemberRemoval().stripInvokables(any())) .make() .load(AccessibleObject.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER) .getLoaded() .getDeclaredField("override"); } long offset = (Long) unsafeType .getMethod("objectFieldOffset", Field.class) .invoke(unsafe, override); Method putBoolean = unsafeType.getMethod("putBoolean", Object.class, long.class, boolean.class); Class<?> internalUnsafe = Class.forName("jdk.internal.misc.Unsafe"); Field theUnsafeInternal = internalUnsafe.getDeclaredField("theUnsafe"); putBoolean.invoke(unsafe, theUnsafeInternal, offset, true); Method defineClassInternal = internalUnsafe.getMethod("defineClass", String.class, byte[].class, int.class, int.class, ClassLoader.class, ProtectionDomain.class); putBoolean.invoke(unsafe, defineClassInternal, offset, true); return new Enabled(theUnsafeInternal.get(null), defineClassInternal); } catch (Exception ignored) { throw exception; } } } catch (Exception exception) { return new Unavailable(exception.getMessage()); } } }
An enabled dispatcher.
/** * An enabled dispatcher. */
@HashCodeAndEqualsPlugin.Enhance class Enabled implements Dispatcher, Initializable {
An instance of sun.misc.Unsafe.
/** * An instance of {@code sun.misc.Unsafe}. */
private final Object unsafe;
The sun.misc.Unsafe#defineClass method.
/** * The {@code sun.misc.Unsafe#defineClass} method. */
private final Method defineClass;
Creates an enabled dispatcher.
Params:
  • unsafe – An instance of sun.misc.Unsafe.
  • defineClass – The sun.misc.Unsafe#defineClass method.
/** * Creates an enabled dispatcher. * * @param unsafe An instance of {@code sun.misc.Unsafe}. * @param defineClass The {@code sun.misc.Unsafe#defineClass} method. */
protected Enabled(Object unsafe, Method defineClass) { this.unsafe = unsafe; this.defineClass = defineClass; }
{@inheritDoc}
/** * {@inheritDoc} */
public boolean isAvailable() { return true; }
{@inheritDoc}
/** * {@inheritDoc} */
public Dispatcher initialize() { SecurityManager securityManager = System.getSecurityManager(); if (securityManager != null) { try { securityManager.checkPermission(SUPPRESS_ACCESS_CHECKS); } catch (Exception exception) { return new Unavailable(exception.getMessage()); } } return this; }
{@inheritDoc}
/** * {@inheritDoc} */
public Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, ProtectionDomain protectionDomain) { try { return (Class<?>) defineClass.invoke(unsafe, name, binaryRepresentation, 0, binaryRepresentation.length, classLoader, protectionDomain); } catch (IllegalAccessException exception) { throw new IllegalStateException("Could not access Unsafe::defineClass", exception); } catch (InvocationTargetException exception) { throw new IllegalStateException("Error invoking Unsafe::defineClass", exception.getCause()); } } }
A disabled dispatcher.
/** * A disabled dispatcher. */
@HashCodeAndEqualsPlugin.Enhance class Unavailable implements Dispatcher, Initializable {
The reason why this dispatcher is not available.
/** * The reason why this dispatcher is not available. */
private final String message;
Creates a disabled dispatcher.
Params:
  • message – The reason why this dispatcher is not available.
/** * Creates a disabled dispatcher. * * @param message The reason why this dispatcher is not available. */
protected Unavailable(String message) { this.message = message; }
{@inheritDoc}
/** * {@inheritDoc} */
public boolean isAvailable() { return false; }
{@inheritDoc}
/** * {@inheritDoc} */
public Dispatcher initialize() { throw new UnsupportedOperationException("Could not access Unsafe class: " + message); }
{@inheritDoc}
/** * {@inheritDoc} */
public Class<?> defineClass(ClassLoader classLoader, String name, byte[] binaryRepresentation, ProtectionDomain protectionDomain) { throw new UnsupportedOperationException("Could not access Unsafe class: " + message); } } } }
A class injector using a Instrumentation to append to either the boot classpath or the system class path.
/** * A class injector using a {@link java.lang.instrument.Instrumentation} to append to either the boot classpath * or the system class path. */
@HashCodeAndEqualsPlugin.Enhance class UsingInstrumentation extends AbstractBase {
The jar file name extension.
/** * The jar file name extension. */
private static final String JAR = "jar";
The class file extension.
/** * The class file extension. */
private static final String CLASS_FILE_EXTENSION = ".class";
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 to use for appending to the class path or the boot path.
/** * The instrumentation to use for appending to the class path or the boot path. */
private final Instrumentation instrumentation;
A representation of the target path to which classes are to be appended.
/** * A representation of the target path to which classes are to be appended. */
private final Target target;
The folder to be used for storing jar files.
/** * The folder to be used for storing jar files. */
private final File folder;
A random string generator for creating file names.
/** * A random string generator for creating file names. */
private final RandomString randomString;
Creates an instrumentation-based class injector.
Params:
  • folder – The folder to be used for storing jar files.
  • target – A representation of the target path to which classes are to be appended.
  • instrumentation – The instrumentation to use for appending to the class path or the boot path.
  • randomString – The random string generator to use.
/** * Creates an instrumentation-based class injector. * * @param folder The folder to be used for storing jar files. * @param target A representation of the target path to which classes are to be appended. * @param instrumentation The instrumentation to use for appending to the class path or the boot path. * @param randomString The random string generator to use. */
protected UsingInstrumentation(File folder, Target target, Instrumentation instrumentation, RandomString randomString) { this.folder = folder; this.target = target; this.instrumentation = instrumentation; this.randomString = randomString; }
Creates an instrumentation-based class injector.
Params:
  • folder – The folder to be used for storing jar files.
  • target – A representation of the target path to which classes are to be appended.
  • instrumentation – The instrumentation to use for appending to the class path or the boot path.
Returns:An appropriate class injector that applies instrumentation.
/** * Creates an instrumentation-based class injector. * * @param folder The folder to be used for storing jar files. * @param target A representation of the target path to which classes are to be appended. * @param instrumentation The instrumentation to use for appending to the class path or the boot path. * @return An appropriate class injector that applies instrumentation. */
public static ClassInjector of(File folder, Target target, Instrumentation instrumentation) { return new UsingInstrumentation(folder, target, instrumentation, new RandomString()); }
{@inheritDoc}
/** * {@inheritDoc} */
public boolean isAlive() { return isAvailable(); }
{@inheritDoc}
/** * {@inheritDoc} */
public Map<String, Class<?>> injectRaw(Map<? extends String, byte[]> types) { File jarFile = new File(folder, JAR + randomString.nextString() + "." + JAR); try { if (!jarFile.createNewFile()) { throw new IllegalStateException("Cannot create file " + jarFile); } JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(jarFile)); try { for (Map.Entry<? extends String, byte[]> entry : types.entrySet()) { jarOutputStream.putNextEntry(new JarEntry(entry.getKey().replace('.', '/') + CLASS_FILE_EXTENSION)); jarOutputStream.write(entry.getValue()); } } finally { jarOutputStream.close(); } target.inject(instrumentation, new JarFile(jarFile)); Map<String, Class<?>> result = new HashMap<String, Class<?>>(); for (String name : types.keySet()) { result.put(name, Class.forName(name, false, ClassLoader.getSystemClassLoader())); } return result; } catch (IOException exception) { throw new IllegalStateException("Cannot write jar file to disk", exception); } catch (ClassNotFoundException exception) { throw new IllegalStateException("Cannot load injected class", exception); } }
Returns true if this class injector is available on this VM.
Returns:true if this class injector is available on this VM.
/** * Returns {@code true} if this class injector is available on this VM. * * @return {@code true} if this class injector is available on this VM. */
public static boolean isAvailable() { return DISPATCHER.isAlive(); }
A dispatcher to interact with the instrumentation API.
/** * A dispatcher to interact with the instrumentation API. */
protected interface Dispatcher {
Returns true if this dispatcher is alive.
Returns:true if this dispatcher is alive.
/** * Returns {@code true} if this dispatcher is alive. * * @return {@code true} if this dispatcher is alive. */
boolean isAlive();
Appends a jar file to the bootstrap class loader.
Params:
  • instrumentation – The instrumentation instance to interact with.
  • jarFile – The jar file to append.
/** * Appends a jar file to the bootstrap class loader. * * @param instrumentation The instrumentation instance to interact with. * @param jarFile The jar file to append. */
void appendToBootstrapClassLoaderSearch(Instrumentation instrumentation, JarFile jarFile);
Appends a jar file to the system class loader.
Params:
  • instrumentation – The instrumentation instance to interact with.
  • jarFile – The jar file to append.
/** * Appends a jar file to the system class loader. * * @param instrumentation The instrumentation instance to interact with. * @param jarFile The jar file to append. */
void appendToSystemClassLoaderSearch(Instrumentation instrumentation, JarFile jarFile);
An action to create a dispatcher for interacting with the instrumentation API.
/** * An action to create a dispatcher for interacting with the instrumentation API. */
enum CreationAction implements PrivilegedAction<Dispatcher> {
The singleton instance.
/** * The singleton instance. */
INSTANCE;
{@inheritDoc}
/** * {@inheritDoc} */
public Dispatcher run() { try { return new ForJava6CapableVm(Instrumentation.class.getMethod("appendToBootstrapClassLoaderSearch", JarFile.class), Instrumentation.class.getMethod("appendToSystemClassLoaderSearch", JarFile.class)); } catch (NoSuchMethodException ignored) { return ForLegacyVm.INSTANCE; } } }
A dispatcher for a legacy VM that is not capable of appending jar files.
/** * A dispatcher for a legacy VM that is not capable of appending jar files. */
enum ForLegacyVm implements Dispatcher {
The singleton instance.
/** * The singleton instance. */
INSTANCE;
{@inheritDoc}
/** * {@inheritDoc} */
public boolean isAlive() { return false; }
{@inheritDoc}
/** * {@inheritDoc} */
public void appendToBootstrapClassLoaderSearch(Instrumentation instrumentation, JarFile jarFile) { throw new UnsupportedOperationException("The current JVM does not support appending to the bootstrap loader"); }
{@inheritDoc}
/** * {@inheritDoc} */
public void appendToSystemClassLoaderSearch(Instrumentation instrumentation, JarFile jarFile) { throw new UnsupportedOperationException("The current JVM does not support appending to the system class loader"); } }
A dispatcher for a VM that is capable of appending to the boot and system class loader.
/** * A dispatcher for a VM that is capable of appending to the boot and system class loader. */
@HashCodeAndEqualsPlugin.Enhance class ForJava6CapableVm implements Dispatcher {
The Instrumentation#appendToBootstrapClassLoaderSearch method.
/** * The {@code Instrumentation#appendToBootstrapClassLoaderSearch} method. */
private final Method appendToBootstrapClassLoaderSearch;
The Instrumentation#appendToSystemClassLoaderSearch method.
/** * The {@code Instrumentation#appendToSystemClassLoaderSearch} method. */
private final Method appendToSystemClassLoaderSearch;
Creates a new dispatcher for a Java 6 compatible VM.
Params:
  • appendToBootstrapClassLoaderSearch – The Instrumentation#appendToBootstrapClassLoaderSearch method.
  • appendToSystemClassLoaderSearch – The Instrumentation#appendToSystemClassLoaderSearch method.
/** * Creates a new dispatcher for a Java 6 compatible VM. * * @param appendToBootstrapClassLoaderSearch The {@code Instrumentation#appendToBootstrapClassLoaderSearch} method. * @param appendToSystemClassLoaderSearch The {@code Instrumentation#appendToSystemClassLoaderSearch} method. */
protected ForJava6CapableVm(Method appendToBootstrapClassLoaderSearch, Method appendToSystemClassLoaderSearch) { this.appendToBootstrapClassLoaderSearch = appendToBootstrapClassLoaderSearch; this.appendToSystemClassLoaderSearch = appendToSystemClassLoaderSearch; }
{@inheritDoc}
/** * {@inheritDoc} */
public boolean isAlive() { return true; }
{@inheritDoc}
/** * {@inheritDoc} */
public void appendToBootstrapClassLoaderSearch(Instrumentation instrumentation, JarFile jarFile) { try { appendToBootstrapClassLoaderSearch.invoke(instrumentation, jarFile); } catch (IllegalAccessException exception) { throw new IllegalStateException("Cannot access java.lang.instrument.Instrumentation#appendToBootstrapClassLoaderSearch", exception); } catch (InvocationTargetException exception) { throw new IllegalStateException("Error invoking java.lang.instrument.Instrumentation#appendToBootstrapClassLoaderSearch", exception.getCause()); } }
{@inheritDoc}
/** * {@inheritDoc} */
public void appendToSystemClassLoaderSearch(Instrumentation instrumentation, JarFile jarFile) { try { appendToSystemClassLoaderSearch.invoke(instrumentation, jarFile); } catch (IllegalAccessException exception) { throw new IllegalStateException("Cannot access java.lang.instrument.Instrumentation#appendToSystemClassLoaderSearch", exception); } catch (InvocationTargetException exception) { throw new IllegalStateException("Error invoking java.lang.instrument.Instrumentation#appendToSystemClassLoaderSearch", exception.getCause()); } } } }
A representation of the target to which Java classes should be appended to.
/** * A representation of the target to which Java classes should be appended to. */
public enum Target {
Representation of the bootstrap class loader.
/** * Representation of the bootstrap class loader. */
BOOTSTRAP { @Override protected void inject(Instrumentation instrumentation, JarFile jarFile) { DISPATCHER.appendToBootstrapClassLoaderSearch(instrumentation, jarFile); } },
Representation of the system class loader.
/** * Representation of the system class loader. */
SYSTEM { @Override protected void inject(Instrumentation instrumentation, JarFile jarFile) { DISPATCHER.appendToSystemClassLoaderSearch(instrumentation, jarFile); } };
Adds the given classes to the represented class loader.
Params:
  • instrumentation – The instrumentation instance to use.
  • jarFile – The jar file to append.
/** * Adds the given classes to the represented class loader. * * @param instrumentation The instrumentation instance to use. * @param jarFile The jar file to append. */
protected abstract void inject(Instrumentation instrumentation, JarFile jarFile); } } }