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

com.fitbur.bytebuddy.dynamic.loading.ByteArrayClassLoader Maven / Gradle / Ivy

The newest version!
package com.fitbur.bytebuddy.dynamic.loading;

import com.fitbur.bytebuddy.description.type.TypeDescription;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.com.fitbur.*;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.ProtectionDomain;
import java.util.*;

/**
 * 

* A {@link java.lang.ClassLoader} that is capable of loading explicitly defined classes. The class loader will free * any binary resources once a class that is defined by its binary data is loaded. This class loader is thread safe since * the class loading mechanics are only called from synchronized context. *

*

* Note: Instances of this class loader return URLs for their represented class loaders with the bytebuddy schema. * These URLs do not represent URIs as two classes with the same name yield identical URLs but might represents different byte * arrays. *

*/ public class ByteArrayClassLoader extends ClassLoader { /** * The schema for URLs that represent a class file of byte array class loaders. */ public static final String URL_SCHEMA = "bytebuddy"; /** * Indicates that an array should be included from its first index. Improves the source code readability. */ private static final int FROM_BEGINNING = 0; /** * Indicates that a URL does not exist to improve code readability. */ private static final URL NO_URL = null; /** * A mutable map of type names mapped to their binary representation. */ protected final Map typeDefinitions; /** * The persistence handler of this class loader. */ protected final PersistenceHandler persistenceHandler; /** * The protection domain to apply. Might be {@code null} when referencing the default protection domain. */ protected final ProtectionDomain protectionDomain; /** * The package definer to be queried for package definitions. */ protected final PackageDefinitionStrategy packageDefinitionStrategy; /** * The access control context to use for loading classes. */ protected final AccessControlContext accessControlContext; /** * Creates a new class loader for a given definition of classes. * * @param parent The {@link java.lang.ClassLoader} that is the parent of this class loader. * @param typeDefinitions A map of fully qualified class names pointing to their binary representations. * @param protectionDomain The protection domain to apply where {@code null} references an implicit protection domain. * @param accessControlContext The access control context to use for loading classes. * @param packageDefinitionStrategy The package definer to be queried for package definitions. * @param persistenceHandler The persistence handler of this class loader. */ public ByteArrayClassLoader(ClassLoader parent, Map typeDefinitions, ProtectionDomain protectionDomain, AccessControlContext accessControlContext, PersistenceHandler persistenceHandler, PackageDefinitionStrategy packageDefinitionStrategy) { super(parent); this.typeDefinitions = new HashMap(typeDefinitions); this.protectionDomain = protectionDomain; this.accessControlContext = accessControlContext; this.persistenceHandler = persistenceHandler; this.packageDefinitionStrategy = packageDefinitionStrategy; } /** * Creates a new class loader for a given definition of classes. * * @param parent The {@link java.lang.ClassLoader} that is the parent of this class loader. * @param typeDefinitions A map of type descriptions pointing to their binary representations. * @param protectionDomain The protection domain to apply where {@code null} references an implicit protection domain. * @param accessControlContext The access control context to use for loading classes. * @param persistenceHandler The persistence handler to be used by the created class loader. * @param packageDefinitionStrategy The package definer to be queried for package definitions. * @param childFirst {@code true} if the class loader should apply child first semantics when loading * the {@code typeDefinitions}. * @return A corresponding class loader. */ public static ClassLoader of(ClassLoader parent, Map typeDefinitions, ProtectionDomain protectionDomain, AccessControlContext accessControlContext, PersistenceHandler persistenceHandler, PackageDefinitionStrategy packageDefinitionStrategy, boolean childFirst) { Map namedTypeDefinitions = new HashMap(); for (Map.Entry entry : typeDefinitions.entrySet()) { namedTypeDefinitions.put(entry.getKey().getName(), entry.getValue()); } return AccessController.doPrivileged(new ClassLoaderCreationAction(parent, namedTypeDefinitions, protectionDomain, accessControlContext, persistenceHandler, packageDefinitionStrategy, childFirst), accessControlContext); } /** * Loads a given set of class descriptions and their binary representations. * * @param classLoader The parent class loader. * @param types The unloaded types to be loaded. * @param protectionDomain The protection domain to apply where {@code null} references an implicit protection domain. * @param accessControlContext The access control context to use for loading classes. * @param persistenceHandler The persistence handler of the created class loader. * @param packageDefinitionStrategy The package definer to be queried for package definitions. * @param childFirst {@code true} if the created class loader should apply child-first semantics when loading the {@code types}. * @param forbidExisting {@code true} if the class loading should throw an exception if a class was already loaded by a parent class loader. * @return A map of the given type descriptions pointing to their loaded representations. */ public static Map> load(ClassLoader classLoader, Map types, ProtectionDomain protectionDomain, AccessControlContext accessControlContext, PersistenceHandler persistenceHandler, PackageDefinitionStrategy packageDefinitionStrategy, boolean childFirst, boolean forbidExisting) { Map> loadedTypes = new LinkedHashMap>(); classLoader = ByteArrayClassLoader.of(classLoader, types, protectionDomain, accessControlContext, persistenceHandler, packageDefinitionStrategy, childFirst); for (TypeDescription typeDescription : types.keySet()) { try { Class type = classLoader.loadClass(typeDescription.getName()); if (forbidExisting && type.getClassLoader() != classLoader) { throw new IllegalStateException("Class already loaded: " + type); } loadedTypes.put(typeDescription, type); } catch (ClassNotFoundException exception) { throw new IllegalStateException("Cannot load class " + typeDescription, exception); } } return loadedTypes; } @Override protected Class findClass(String name) throws ClassNotFoundException { byte[] javaType = persistenceHandler.lookup(name, typeDefinitions); if (javaType != null) { int packageIndex = name.lastIndexOf('.'); if (packageIndex != -1) { String packageName = name.substring(0, packageIndex); PackageDefinitionStrategy.Definition definition = packageDefinitionStrategy.define(ByteArrayClassLoader.this, packageName, name); if (definition.isDefined()) { Package definedPackage = getPackage(packageName); if (definedPackage == null) { definePackage(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); } } } return defineClass(name, javaType, FROM_BEGINNING, javaType.length, protectionDomain); } throw new ClassNotFoundException(name); } @Override protected URL findResource(String name) { return persistenceHandler.url(name, typeDefinitions, accessControlContext); } @Override protected Enumeration findResources(String name) { URL url = persistenceHandler.url(name, typeDefinitions, accessControlContext); return url == null ? EmptyEnumeration.INSTANCE : new SingletonEnumeration(url); } @Override public String toString() { return "ByteArrayClassLoader{" + "parent=" + getParent() + ", typeDefinitions=" + typeDefinitions + ", persistenceHandler=" + persistenceHandler + ", protectionDomain=" + protectionDomain + ", packageDefinitionStrategy=" + packageDefinitionStrategy + ", accessControlContext=" + accessControlContext + '}'; } /** * A persistence handler decides on weather the byte array that represents a loaded class is exposed by * the {@link java.lang.ClassLoader#getResourceAsStream(String)} method. */ public enum PersistenceHandler { /** * The manifest persistence handler retains all class file representations and makes them accessible. */ MANIFEST(true) { @Override protected byte[] lookup(String name, Map typeDefinitions) { return typeDefinitions.get(name); } @Override protected URL url(String resourceName, Map typeDefinitions, AccessControlContext accessControlContext) { if (!resourceName.endsWith(CLASS_FILE_SUFFIX)) { return NO_URL; } else if (resourceName.startsWith("/")) { resourceName = resourceName.substring(1); } String typeName = resourceName.replace('/', '.').substring(FROM_BEGINNING, resourceName.length() - CLASS_FILE_SUFFIX.length()); byte[] binaryRepresentation = typeDefinitions.get(typeName); return binaryRepresentation == null ? NO_URL : AccessController.doPrivileged(new UrlDefinitionAction(resourceName, binaryRepresentation), accessControlContext); } }, /** * The latent persistence handler hides all class file representations and does not make them accessible * even before they are loaded. */ LATENT(false) { @Override protected byte[] lookup(String name, Map typeDefinitions) { return typeDefinitions.remove(name); } @Override protected URL url(String resourceName, Map typeDefinitions, AccessControlContext accessControlContext) { return NO_URL; } }; /** * The suffix of files in the Java class file format. */ private static final String CLASS_FILE_SUFFIX = ".class"; /** * {@code true} if this persistence handler represents manifest class file storage. */ private final boolean manifest; /** * Creates a new persistence handler. * * @param manifest {@code true} if this persistence handler represents manifest class file storage. */ PersistenceHandler(boolean manifest) { this.manifest = manifest; } /** * Checks if this persistence handler represents manifest class file storage. * * @return {@code true} if this persistence handler represents manifest class file storage. */ public boolean isManifest() { return manifest; } /** * Performs a lookup of a class file by its name. * * @param name The name of the class to be loaded. * @param typeDefinitions A map of fully qualified class names pointing to their binary representations. * @return The byte array representing the requested class or {@code null} if no such class is known. */ protected abstract byte[] lookup(String name, Map typeDefinitions); /** * Returns a URL representing a class file. * * @param resourceName The name of the requested resource. * @param typeDefinitions A mapping of byte arrays by their type names. * @param accessControlContext The access control context to be used for creating the URL. * @return A URL representing the type definition or {@code null} if the requested resource does not represent a class file. */ protected abstract URL url(String resourceName, Map typeDefinitions, AccessControlContext accessControlContext); @Override public String toString() { return "ByteArrayClassLoader.PersistenceHandler." + name(); } /** * An action to define a URL that represents a class file. */ protected static class UrlDefinitionAction implements PrivilegedAction { /** * The URL's encoding character set. */ private static final String ENCODING = "UTF-8"; /** * A value to define a standard port as Byte Buddy's URLs do not represent a port. */ private static final int NO_PORT = -1; /** * Indicates that Byte Buddy's URLs do not have a file segment. */ private static final String NO_FILE = ""; /** * The name of the type that this URL represents. */ private final String typeName; /** * The binary representation of the type's class file. */ private final byte[] binaryRepresentation; /** * Creates a new URL definition action. * * @param typeName The name of the type that this URL represents. * @param binaryRepresentation The binary representation of the type's class file. */ protected UrlDefinitionAction(String typeName, byte[] binaryRepresentation) { this.typeName = typeName; this.binaryRepresentation = binaryRepresentation; } @Override public URL run() { try { return new URL(URL_SCHEMA, URLEncoder.encode(typeName.replace('.', '/'), ENCODING), NO_PORT, NO_FILE, new ByteArrayUrlStreamHandler(binaryRepresentation)); } catch (MalformedURLException exception) { throw new IllegalStateException("Cannot create URL for " + typeName, exception); } catch (UnsupportedEncodingException exception) { throw new IllegalStateException("Could not find encoding: " + ENCODING, exception); } } @Override public boolean equals(Object other) { if (this == other) return true; if (other == null || getClass() != other.getClass()) return false; UrlDefinitionAction that = (UrlDefinitionAction) other; return typeName.equals(that.typeName) && Arrays.equals(binaryRepresentation, that.binaryRepresentation); } @Override public int hashCode() { int result = typeName.hashCode(); result = 31 * result + Arrays.hashCode(binaryRepresentation); return result; } @Override public String toString() { return "ByteArrayClassLoader.PersistenceHandler.UrlDefinitionAction{" + "typeName='" + typeName + '\'' + "binaryRepresentation=<" + binaryRepresentation.length + " bytes>" + '}'; } /** * A stream handler that returns the given binary representation. */ protected static class ByteArrayUrlStreamHandler extends URLStreamHandler { /** * The binary representation of a type's class file. */ private final byte[] binaryRepresentation; /** * Creates a new byte array URL stream handler. * * @param binaryRepresentation The binary representation of a type's class file. */ protected ByteArrayUrlStreamHandler(byte[] binaryRepresentation) { this.binaryRepresentation = binaryRepresentation; } @Override protected URLConnection openConnection(URL url) throws IOException { return new ByteArrayUrlConnection(url, new ByteArrayInputStream(binaryRepresentation)); } @Override public boolean equals(Object other) { if (this == other) return true; if (other == null || getClass() != other.getClass()) return false; ByteArrayUrlStreamHandler that = (ByteArrayUrlStreamHandler) other; return Arrays.equals(binaryRepresentation, that.binaryRepresentation); } @Override public int hashCode() { return Arrays.hashCode(binaryRepresentation); } @Override public String toString() { return "ByteArrayClassLoader.PersistenceHandler.UrlDefinitionAction.ByteArrayUrlStreamHandler{" + "binaryRepresentation=<" + binaryRepresentation.length + " bytes>" + '}'; } /** * A URL connection for a given byte array. */ protected static class ByteArrayUrlConnection extends URLConnection { /** * The input stream to return for this connection. */ private final InputStream inputStream; /** * Creates a new byte array URL connection. * * @param url The URL that this connection represents. * @param inputStream The input stream to return from this connection. */ protected ByteArrayUrlConnection(URL url, InputStream inputStream) { super(url); this.inputStream = inputStream; } @Override public void connect() { connected = true; } @Override public InputStream getInputStream() { connect(); // Mimics the semantics of an actual URL connection. return inputStream; } @Override public String toString() { return "ByteArrayClassLoader.PersistenceHandler.UrlDefinitionAction.ByteArrayUrlStreamHandler.ByteArrayUrlConnection{" + "inputStream=" + inputStream + '}'; } } } } } /** *

* A {@link com.fitbur.bytebuddy.dynamic.loading.ByteArrayClassLoader} which applies child-first semantics for the * given type definitions. *

*

* Important: Package definitions remain their parent-first semantics as loaded package definitions do not expose their class loaders. * Also, it is not possible to make this class or its subclass parallel-capable as the loading strategy is overridden. *

*/ public static class ChildFirst extends ByteArrayClassLoader { /** * The suffix of files in the Java class file format. */ private static final String CLASS_FILE_SUFFIX = ".class"; /** * The synchronization engine for the executing JVM. */ private static final SynchronizationStrategy SYNCHRONIZATION_STRATEGY; /* * Sets up the suitable synchronization engine (Java 8+ or earlier). */ static { SynchronizationStrategy synchronizationStrategy; try { synchronizationStrategy = SynchronizationStrategy.ForJava7CapableVm.resolve(); } catch (Exception ignored) { synchronizationStrategy = SynchronizationStrategy.ForLegacyVm.INSTANCE; } SYNCHRONIZATION_STRATEGY = synchronizationStrategy; } /** * Creates a new child-first byte array class loader. * * @param parent The {@link java.lang.ClassLoader} that is the parent of this class loader. * @param typeDefinitions A map of fully qualified class names pointing to their binary representations. * @param protectionDomain The protection domain to apply where {@code null} references an implicit protection domain. * @param accessControlContext The access control context to use for loading classes. * @param persistenceHandler The persistence handler of this class loader. * @param packageDefinitionStrategy The package definer to be queried for package definitions. */ public ChildFirst(ClassLoader parent, Map typeDefinitions, ProtectionDomain protectionDomain, AccessControlContext accessControlContext, PersistenceHandler persistenceHandler, PackageDefinitionStrategy packageDefinitionStrategy) { super(parent, typeDefinitions, protectionDomain, accessControlContext, persistenceHandler, packageDefinitionStrategy); } @Override protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (SYNCHRONIZATION_STRATEGY.classLoadingLock(name, this)) { Class type = findLoadedClass(name); if (type != null) { return type; } try { type = findClass(name); if (resolve) { resolveClass(type); } return type; } catch (ClassNotFoundException exception) { // If an unknown class is loaded, this implementation causes the findClass method of this instance // to be triggered twice. This is however of minor importance because this would result in a // ClassNotFoundException what does not alter the outcome. return super.loadClass(name, resolve); } } } @Override public URL getResource(String name) { URL url = persistenceHandler.url(name, typeDefinitions, accessControlContext); // If a class resource is defined by this class loader but it is not defined in a manifest manner, // the resource of the parent class loader should be shadowed by 'null'. Note that the delegation // model causes a redundant query to the persistent handler but renders a correct result. return url != null || isShadowed(name) ? url : super.getResource(name); } @Override public Enumeration getResources(String name) throws IOException { URL url = persistenceHandler.url(name, typeDefinitions, accessControlContext); return url == null ? super.getResources(name) : new PrependingEnumeration(url, super.getResources(name)); } /** * Checks if a resource name represents a class file of a class that was loaded by this class loader. * * @param resourceName The resource name of the class to be exposed as its class file. * @return {@code true} if this class represents a class that is being loaded by this class loader. */ private boolean isShadowed(String resourceName) { if (persistenceHandler.isManifest() || !resourceName.endsWith(CLASS_FILE_SUFFIX)) { return false; } // This synchronization is required to avoid a racing condition to the actual class loading. synchronized (this) { String typeName = resourceName.replace('/', '.').substring(0, resourceName.length() - CLASS_FILE_SUFFIX.length()); if (typeDefinitions.containsKey(typeName)) { return true; } Class loadedClass = findLoadedClass(typeName); return loadedClass != null && loadedClass.getClassLoader() == this; } } @Override public String toString() { return "ByteArrayClassLoader.ChildFirst{" + "parent=" + getParent() + ", typeDefinitions=" + typeDefinitions + ", protectionDomain=" + protectionDomain + ", accessControlContext=" + accessControlContext + ", persistenceHandler=" + persistenceHandler + ", packageDefinitionStrategy=" + packageDefinitionStrategy + '}'; } /** * An engine for receiving a class loading lock when loading a class. */ protected interface SynchronizationStrategy { /** * Receives the class loading lock. * * @param name The name of the class being loaded. * @param classLoader The class loader loading the class. * @return The corresponding class loading lock. */ Object classLoadingLock(String name, ClassLoader classLoader); /** * A synchronization engine for a VM that is not aware of parallel-capable class loaders. */ enum ForLegacyVm implements SynchronizationStrategy { /** * The singleton instance. */ INSTANCE; @Override public Object classLoadingLock(String name, ClassLoader classLoader) { return classLoader; } @Override public String toString() { return "ByteArrayClassLoader.ChildFirst.SynchronizationStrategy.ForLegacyVm." + name(); } } /** * A synchronization engine for a VM that is aware of parallel-capable class loaders. */ class ForJava7CapableVm implements SynchronizationStrategy, PrivilegedAction { /** * The {@code ClassLoader#getClassLoadingLock(String)} method. */ private final Method method; /** * Creates a new synchronization engine. * * @param method The {@code ClassLoader#getClassLoadingLock(String)} method. */ protected ForJava7CapableVm(Method method) { this.method = method; } /** * Resolves a synchronization engine for a modern VM if this is possible. * * @return A modern synchronization engine. * @throws NoSuchMethodException If the executing VM is not a modern VM. */ protected static SynchronizationStrategy resolve() throws NoSuchMethodException { return AccessController.doPrivileged(new ForJava7CapableVm(ClassLoader.class.getDeclaredMethod("getClassLoadingLock", String.class))); } @Override public Object classLoadingLock(String name, ClassLoader classLoader) { try { return method.invoke(classLoader, name); } catch (IllegalAccessException exception) { throw new IllegalStateException("Cannot access class loading lock for " + name + " on " + classLoader, exception); } catch (InvocationTargetException exception) { throw new IllegalStateException("Error when getting " + name + " on " + classLoader, exception); } } @Override public SynchronizationStrategy run() { method.setAccessible(true); return this; } @Override public boolean equals(Object other) { return this == other || !(other == null || getClass() != other.getClass()) && method.equals(((ForJava7CapableVm) other).method); } @Override public int hashCode() { return method.hashCode(); } @Override public String toString() { return "ByteArrayClassLoader.ChildFirst.SynchronizationStrategy.ForJava7CapableVm{method=" + method + '}'; } } } /** * An enumeration that prepends an element to another enumeration and skips the last element of the provided enumeration. */ protected static class PrependingEnumeration implements Enumeration { /** * The next element to return from this enumeration or {@code null} if such an element does not exist. */ private URL nextElement; /** * The enumeration from which the next elements should be pulled. */ private final Enumeration enumeration; /** * Creates a new prepending enumeration. * * @param url The first element of the enumeration. * @param enumeration An enumeration that is used for pulling subsequent urls. */ protected PrependingEnumeration(URL url, Enumeration enumeration) { nextElement = url; this.enumeration = enumeration; } @Override public boolean hasMoreElements() { return nextElement != null && enumeration.hasMoreElements(); } @Override public URL nextElement() { if (nextElement != null && enumeration.hasMoreElements()) { try { return nextElement; } finally { nextElement = enumeration.nextElement(); } } else { throw new NoSuchElementException(); } } @Override public String toString() { return "ByteArrayClassLoader.ChildFirst.PrependingEnumeration{" + "nextElement=" + nextElement + ", enumeration=" + enumeration + '}'; } } } /** * An action for creating a class loader. */ protected static class ClassLoaderCreationAction implements PrivilegedAction { /** * The {@link java.lang.ClassLoader} that is the parent of this class loader. */ private final ClassLoader parent; /** * A map of fully qualified class names pointing to their binary representations. */ private final Map typeDefinitions; /** * The protection domain to apply where {@code null} references an implicit protection domain. */ private final ProtectionDomain protectionDomain; /** * The access control context to use for loading classes. */ private final AccessControlContext accessControlContext; /** * The persistence handler of this class loader. */ private final PersistenceHandler persistenceHandler; /** * The package definer to be queried for package definitions. */ private final PackageDefinitionStrategy packageDefinitionStrategy; /** * {@code true} if this action should create a child-first class loader. */ private final boolean childFirst; /** * Creates a new class loader creation action. * * @param parent The {@link java.lang.ClassLoader} that is the parent of this class loader. * @param typeDefinitions A map of fully qualified class names pointing to their binary representations. * @param protectionDomain The protection domain to apply where {@code null} references an implicit protection domain. * @param accessControlContext The access control context to use for loading classes. * @param persistenceHandler The persistence handler of this class loader. * @param packageDefinitionStrategy The package definer to be queried for package definitions. * @param childFirst {@code true} if this action should create a child-first class loader. */ protected ClassLoaderCreationAction(ClassLoader parent, Map typeDefinitions, ProtectionDomain protectionDomain, AccessControlContext accessControlContext, PersistenceHandler persistenceHandler, PackageDefinitionStrategy packageDefinitionStrategy, boolean childFirst) { this.parent = parent; this.typeDefinitions = typeDefinitions; this.protectionDomain = protectionDomain; this.accessControlContext = accessControlContext; this.persistenceHandler = persistenceHandler; this.packageDefinitionStrategy = packageDefinitionStrategy; this.childFirst = childFirst; } @Override public ClassLoader run() { return childFirst ? new ChildFirst(parent, typeDefinitions, protectionDomain, accessControlContext, persistenceHandler, packageDefinitionStrategy) : new ByteArrayClassLoader(parent, typeDefinitions, protectionDomain, accessControlContext, persistenceHandler, packageDefinitionStrategy); } @Override public boolean equals(Object other) { if (this == other) return true; if (other == null || getClass() != other.getClass()) return false; ClassLoaderCreationAction that = (ClassLoaderCreationAction) other; return childFirst == that.childFirst && parent.equals(that.parent) && typeDefinitions.equals(that.typeDefinitions) && !(protectionDomain != null ? !protectionDomain.equals(that.protectionDomain) : that.protectionDomain != null) && accessControlContext.equals(that.accessControlContext) && persistenceHandler == that.persistenceHandler && packageDefinitionStrategy.equals(that.packageDefinitionStrategy); } @Override public int hashCode() { int result = parent.hashCode(); result = 31 * result + typeDefinitions.hashCode(); result = 31 * result + (protectionDomain != null ? protectionDomain.hashCode() : 0); result = 31 * result + accessControlContext.hashCode(); result = 31 * result + persistenceHandler.hashCode(); result = 31 * result + packageDefinitionStrategy.hashCode(); result = 31 * result + (childFirst ? 1 : 0); return result; } @Override public String toString() { return "ByteArrayClassLoader.ClassLoaderCreationAction{" + "parent=" + parent + ", typeDefinitions=" + typeDefinitions + ", protectionDomain=" + protectionDomain + ", accessControlContext=" + accessControlContext + ", persistenceHandler=" + persistenceHandler + ", packageDefinitionStrategy=" + packageDefinitionStrategy + ", childFirst=" + childFirst + '}'; } } /** * An enumeration without any elements. */ protected enum EmptyEnumeration implements Enumeration { /** * The singleton instance. */ INSTANCE; @Override public boolean hasMoreElements() { return false; } @Override public URL nextElement() { throw new NoSuchElementException(); } @Override public String toString() { return "ByteArrayClassLoader.EmptyEnumeration." + name(); } } /** * An enumeration that contains a single element. */ protected static class SingletonEnumeration implements Enumeration { /** * The current element or {@code null} if this enumeration does not contain further elements. */ private URL element; /** * Creates a new singleton enumeration. * * @param element The only element. */ protected SingletonEnumeration(URL element) { this.element = element; } @Override public boolean hasMoreElements() { return element != null; } @Override public URL nextElement() { if (element == null) { throw new NoSuchElementException(); } else { try { return element; } finally { element = null; } } } @Override public String toString() { return "ByteArrayClassLoader.SingletonEnumeration{" + "element=" + element + '}'; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy