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

com.fitbur.bytebuddy.agent.builder.Nexus Maven / Gradle / Ivy

There is a newer version: 1.0.0
Show newest version
package com.fitbur.bytebuddy.agent.builder;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Logger;

/**
 * 

* This nexus is a global dispatcher for initializing classes with * {@link com.fitbur.bytebuddy.implementation.LoadedTypeInitializer}s. To do so, this class is to be loaded * by the system class loader in an explicit manner. Any instrumented class is then injected a code * block into its static type initializer that makes a call to this very same nexus which had the * loaded type initializer registered before hand. *

*

* Important: The nexus must never be accessed directly but only by its * {@link com.fitbur.bytebuddy.agent.builder.AgentBuilder.Default.InitializationStrategy.SelfInjection.NexusAccessor} * which makes sure that the nexus is loaded by the system class loader. Otherwise, a class might not * be able to initialize itself if it is loaded by different class loader that does not have the * system class loader in its hierarchy. *

*/ public class Nexus { /** * A map of keys identifying a loaded type by its name and class loader mapping their * potential {@link com.fitbur.bytebuddy.implementation.LoadedTypeInitializer} where the class * loader of these initializers is however irrelevant. */ private static final ConcurrentMap TYPE_INITIALIZERS = new ConcurrentHashMap(); /** * The name of a type for which a loaded type initializer is registered. */ private final String name; /** * The class loader for which a loaded type initializer is registered. */ private final ClassLoader classLoader; /** * A random value that uniquely identifies a Nexus entry in order to avoid conflicts when * applying the self-initialization strategy in multiple transformations. */ private final int identification; /** * Creates a key for identifying a loaded type initializer. * * @param type The loaded type for which a key is to be created. * @param identification An identification for the initializer to run. */ private Nexus(Class type, int identification) { this(nonAnonymous(type.getName()), type.getClassLoader(), identification); } /** * Creates a key for identifying a loaded type initializer. * * @param name The name of a type for which a loaded type initializer is registered. * @param classLoader The class loader for which a loaded type initializer is registered. * @param identification An identification for the initializer to run. */ private Nexus(String name, ClassLoader classLoader, int identification) { this.name = name; this.classLoader = classLoader; this.identification = identification; } /** * Normalizes a type name if it is loaded by an anonymous class loader. * * @param typeName The name as returned by {@link Class#getName()}. * @return The non-anonymous name of the given class. */ private static String nonAnonymous(String typeName) { int anonymousLoaderIndex = typeName.indexOf('/'); return anonymousLoaderIndex == -1 ? typeName : typeName.substring(0, anonymousLoaderIndex); } /** * Initializes a loaded type. * * @param type The loaded type to initialize. * @param identification An identification for the initializer to run. * @throws Exception If an exception occurs. */ @SuppressWarnings("unused") public static void initialize(Class type, int identification) throws Exception { Object typeInitializer = TYPE_INITIALIZERS.remove(new Nexus(type, identification)); if (typeInitializer != null) { typeInitializer.getClass().getMethod("onLoad", Class.class).invoke(typeInitializer, type); } } /** *

* Registers a new loaded type initializer. *

*

* Important: This method must never be called directly but only by using a * {@link com.fitbur.bytebuddy.agent.builder.AgentBuilder.InitializationStrategy.SelfInjection.NexusAccessor} which enforces to access this class for the * system class loader where a Java agent always registers its instances. This avoids a duplication of the class if this nexus is loaded by different * class loaders. For this reason, the last parameter must not use a Byte Buddy specific type as those types can be loaded by different class loaders, * too. Any access of the instance is done using Java reflection instead. *

* * @param name The name of the type for the loaded type initializer. * @param classLoader The class loader of the type for the loaded type initializer. * @param identification An identification for the initializer to run. * @param typeInitializer The type initializer to register. The initializer must be an instance * of {@link com.fitbur.bytebuddy.implementation.LoadedTypeInitializer} where * it does however not matter which class loader loaded this latter type. */ public static void register(String name, ClassLoader classLoader, int identification, Object typeInitializer) { if (TYPE_INITIALIZERS.put(new Nexus(name, classLoader, identification), typeInitializer) != null) { Logger.getAnonymousLogger().warning("Initializer with id " + identification + " is already registered for " + name); } } @Override public boolean equals(Object other) { if (this == other) return true; if (other == null || getClass() != other.getClass()) return false; Nexus nexus = (Nexus) other; return identification == nexus.identification && !(classLoader != null ? !classLoader.equals(nexus.classLoader) : nexus.classLoader != null) && name.equals(nexus.name); } @Override public int hashCode() { int result = name.hashCode(); result = 31 * result + identification; result = 31 * result + (classLoader != null ? classLoader.hashCode() : 0); return result; } @Override public String toString() { return "Nexus{" + "name='" + name + '\'' + ", classLoader=" + classLoader + ", identification=" + identification + '}'; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy