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

net.bytebuddy.dynamic.Nexus Maven / Gradle / Ivy

There is a newer version: 1.36.0
Show newest version
/*
 * Copyright 2014 - Present Rafael Winterhalter
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.bytebuddy.dynamic;

import net.bytebuddy.utility.nullability.AlwaysNull;
import net.bytebuddy.utility.nullability.MaybeNull;

import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * 

* This nexus is a global dispatcher for initializing classes with * {@link net.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. *

*

* Note: Availability of the {@link Nexus} class and its injection into the system class loader * can be disabled entirely by setting the {@link Nexus#PROPERTY} system property to {@code false}. *

*

* Important: The nexus must never be accessed directly but only by the {@link 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 extends WeakReference { /** * A system property that allows to disable the use of the {@link Nexus} class which is normally injected into the system class loader. */ public static final String PROPERTY = "net.bytebuddy.nexus.disabled"; /** * An type-safe constant for a non-operational reference queue. */ @AlwaysNull private static final ReferenceQueue NO_QUEUE = null; /** * A map of keys identifying a loaded type by its name and class loader mapping their * potential {@link net.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's hash code upon registration. */ private final int classLoaderHashCode; /** * 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(), NO_QUEUE, 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 referenceQueue The reference queue to notify upon the class loader's collection or {@code null} if no queue should be notified. * @param identification An identification for the initializer to run. */ private Nexus(String name, @MaybeNull ClassLoader classLoader, @MaybeNull ReferenceQueue referenceQueue, int identification) { super(classLoader, classLoader == null ? null : referenceQueue); this.name = name; classLoaderHashCode = System.identityHashCode(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. This method must only be invoked via the system class loader. *

*

* Important: This method must never be called directly but only by using a {@link NexusAccessor.InitializationAppender} which enforces to * access this class for the system class loader to assure a VM global singleton. 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 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) { Class.forName("net.bytebuddy.implementation.LoadedTypeInitializer", true, typeInitializer.getClass().getClassLoader()).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 NexusAccessor} which enforces to access this class * for the system class loader to assure a VM global singleton. 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 referenceQueue The reference queue to notify upon the class loader's collection which will be enqueued a reference which can be * handed to {@link Nexus#clean(Reference)} or {@code null} if no reference queue should be notified. * @param identification An identification for the initializer to run. * @param typeInitializer The type initializer to register. The initializer must be an instance * of {@link net.bytebuddy.implementation.LoadedTypeInitializer} where * it does however not matter which class loader loaded this latter type. */ public static void register(String name, @MaybeNull ClassLoader classLoader, @MaybeNull ReferenceQueue referenceQueue, int identification, Object typeInitializer) { TYPE_INITIALIZERS.put(new Nexus(name, classLoader, referenceQueue, identification), typeInitializer); } /** *

* Cleans any stale entries from this nexus. Entries are considered stale if their class loader was collected before a class was initialized. *

*

* Important: This method must never be called directly but only by using a {@link NexusAccessor} which enforces to access this class * for the system class loader to assure a VM global singleton. 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 reference The stale reference to clean. */ public static void clean(Reference reference) { TYPE_INITIALIZERS.remove(reference); } @Override public int hashCode() { int result = name.hashCode(); result = 31 * result + classLoaderHashCode; result = 31 * result + identification; return result; } @Override public boolean equals(@MaybeNull Object other) { if (this == other) { return true; } else if (other == null || getClass() != other.getClass()) { return false; } Nexus nexus = (Nexus) other; return classLoaderHashCode == nexus.classLoaderHashCode && identification == nexus.identification && name.equals(nexus.name) && get() == nexus.get(); } @Override public String toString() { return "Nexus{" + "name='" + name + '\'' + ", classLoaderHashCode=" + classLoaderHashCode + ", identification=" + identification + ", classLoader=" + get() + '}'; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy