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

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

There is a newer version: 1.0.0
Show newest version
package com.fitbur.bytebuddy.dynamic.loading;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import com.fitbur.bytebuddy.matcher.ElementMatcher;

import java.io.IOException;
import java.com.fitbur.URL;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.*;

/**
 * 

* This {@link java.lang.ClassLoader} is capable of loading classes from multiple parents. This class loader * implicitly defines the bootstrap class loader to be its direct parent as it is required for all class loaders. * This can be useful when creating a type that inherits a super type and interfaces that are defined by different, * non-compatible class loaders. *

*

* Note: Instances of this class loader can have the same class loader as its parent multiple times, * either directly or indirectly by multiple parents sharing a common parent class loader. By definition, * this implies that the bootstrap class loader is {@code #(direct parents) + 1} times a parent of this class loader. * For the {@link java.lang.ClassLoader#getResources(java.lang.String)} method, this means that this class loader * might return the same url multiple times by representing the same class loader multiple times. *

*

* Important: This class loader does not support the location of packages from its multiple parents. This breaks * package equality when loading classes by either loading them directly via this class loader (e.g. by subclassing) or * by loading classes with child class loaders of this class loader. *

*/ public class MultipleParentClassLoader extends ClassLoader { /** * The parents of this class loader in their application order. */ private final List parents; /** * Creates a new class loader with multiple parents. * * @param parents The parents of this class loader in their application order. */ public MultipleParentClassLoader(List parents) { super(null); this.parents = parents; } @Override public Class loadClass(String name) throws ClassNotFoundException { for (ClassLoader parent : parents) { try { return parent != null ? parent.loadClass(name) : super.loadClass(name); } catch (ClassNotFoundException ignored) { /* try next class loader */ } } return super.loadClass(name); } @Override public URL getResource(String name) { for (ClassLoader parent : parents) { URL url = parent != null ? parent.getResource(name) : super.getResource(name); if (url != null) { return url; } } return super.getResource(name); } @Override public Enumeration getResources(String name) throws IOException { List> enumerations = new ArrayList>(parents.size() + 1); for (ClassLoader parent : parents) { enumerations.add(parent != null ? parent.getResources(name) : super.getResources(name)); } enumerations.add(super.getResources(name)); return new CompoundEnumeration(enumerations); } @Override public String toString() { return "MultipleParentClassLoader{" + "parents=" + parents + '}'; } /** * A compound URL enumeration. */ protected static class CompoundEnumeration implements Enumeration { /** * Indicates the first index of a list. */ private static final int FIRST = 0; /** * The remaining lists of enumerations. */ private final List> enumerations; /** * The currently represented enumeration or {@code null} if no such enumeration is currently selected. */ private Enumeration currentEnumeration; /** * Creates a compound enumeration. * * @param enumerations The enumerations to represent. */ protected CompoundEnumeration(List> enumerations) { this.enumerations = enumerations; } @Override public boolean hasMoreElements() { if (currentEnumeration != null && currentEnumeration.hasMoreElements()) { return true; } else if (!enumerations.isEmpty()) { currentEnumeration = enumerations.remove(FIRST); return hasMoreElements(); } else { return false; } } @Override @SuppressFBWarnings(value = "UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR", justification = "Null reference is impossible due to element check") public URL nextElement() { if (hasMoreElements()) { return currentEnumeration.nextElement(); } else { throw new NoSuchElementException(); } } @Override public String toString() { return "MultipleParentClassLoader.CompoundEnumeration{" + "enumerations=" + enumerations + ", currentEnumeration=" + currentEnumeration + '}'; } } /** * A builder to collect class loader and that creates a * {@link com.fitbur.bytebuddy.dynamic.loading.MultipleParentClassLoader} only if multiple or no * {@link java.lang.ClassLoader}s are found in the process. If exactly a single class loader is found, * this class loader is returned. All class loaders are applied in their collection order. */ public static class Builder { /** * Indicates the first index of a list. */ private static final int ONLY = 0; /** * The class loaders that were collected. */ private final List classLoaders; /** * Creates a new builder without any class loaders. */ public Builder() { this(Collections.emptyList()); } /** * Creates a new builder. * * @param classLoaders The class loaders that were collected until now. */ private Builder(List classLoaders) { this.classLoaders = classLoaders; } /** * Appends the class loaders of the given types. * * @param type The types of which to collect the class loaders. * @return A new builder instance with the additional class loaders of the provided types if they were not * yet collected. */ public Builder append(Class... type) { return append(Arrays.asList(type)); } /** * Appends the class loaders of the given types if those class loaders were not yet collected. * * @param types The types of which to collect the class loaders. * @return A new builder instance with the additional class loaders. */ public Builder append(Collection> types) { List classLoaders = new ArrayList(types.size()); for (Class type : types) { classLoaders.add(type.getClassLoader()); } return append(classLoaders); } /** * Appends the given class loaders if they were not yet collected. * * @param classLoader The class loaders to be collected. * @return A new builder instance with the additional class loaders. */ public Builder append(ClassLoader... classLoader) { return append(Arrays.asList(classLoader)); } /** * Appends the given class loaders if they were not yet collected. * * @param classLoaders The class loaders to collected. * @return A new builder instance with the additional class loaders. */ public Builder append(List classLoaders) { List filtered = new ArrayList(this.classLoaders.size() + classLoaders.size()); Set registered = new HashSet(this.classLoaders); filtered.addAll(this.classLoaders); for (ClassLoader classLoader : classLoaders) { if (registered.add(classLoader)) { filtered.add(classLoader); } } return new Builder(filtered); } /** * Removes all class loaders that match the given filter. * * @param matcher The matcher to be used for filtering. * @return A builder that does not longer consider any appended class loaders that matched the provided matcher. */ public Builder filter(ElementMatcher matcher) { List classLoaders = new ArrayList(this.classLoaders.size()); for (ClassLoader classLoader : this.classLoaders) { if (!matcher.matches(classLoader)) { classLoaders.add(classLoader); } } return new Builder(classLoaders); } /** * Returns an appropriate class loader that represents all the collected class loaders using the current access control context. * * @return A suitable class loader. */ public ClassLoader build() { return build(AccessController.getContext()); } /** * Returns an appropriate class loader that represents all the collected class loaders. * * @param accessControlContext The access control context to be used for creating the class loader. * @return A suitable class loader. */ public ClassLoader build(AccessControlContext accessControlContext) { return classLoaders.size() == 1 ? classLoaders.get(ONLY) : AccessController.doPrivileged(new ClassLoaderCreationAction(classLoaders), accessControlContext); } @Override public boolean equals(Object other) { if (this == other) return true; if (other == null || getClass() != other.getClass()) return false; Builder builder = (Builder) other; return classLoaders.equals(builder.classLoaders); } @Override public int hashCode() { return classLoaders.hashCode(); } @Override public String toString() { return "MultipleParentClassLoader.Builder{" + "classLoaders=" + classLoaders + '}'; } /** * A privileged action for creating a multiple-parent class loader. */ protected static class ClassLoaderCreationAction implements PrivilegedAction { /** * The class loaders to combine. */ private final List classLoaders; /** * Creates a new action for creating a multiple-parent class loader. * * @param classLoaders The class loaders to combine. */ protected ClassLoaderCreationAction(List classLoaders) { this.classLoaders = classLoaders; } @Override public ClassLoader run() { return new MultipleParentClassLoader(classLoaders); } @Override public boolean equals(Object other) { return this == other || !(other == null || getClass() != other.getClass()) && classLoaders.equals(((ClassLoaderCreationAction) other).classLoaders); } @Override public int hashCode() { return classLoaders.hashCode(); } @Override public String toString() { return "MultipleParentClassLoader.Builder.ClassLoaderCreationAction{" + "classLoaders=" + classLoaders + '}'; } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy