CompositeClassLoader.java
/*
* Copyright (C) 2004, 2005 Joe Walnes.
* Copyright (C) 2006, 2007, 2011, 2013, 2014, 2015, 2017, 2020 XStream Committers.
* All rights reserved.
*
* The software in this package is published under the terms of the BSD
* style license a copy of which has been included with this distribution in
* the LICENSE.txt file.
*
* Created on 16. November 2004 by Joe Walnes
*/
package com.thoughtworks.xstream.core.util;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* ClassLoader that is composed of other classloaders. Each loader will be used to try to load the particular class,
* until one of them succeeds. <b>Note:</b> The loaders will always be called in the REVERSE order they were added in.
* <p>
* The Composite class loader also has registered the classloader that loaded xstream.jar and (if available) the
* thread's context classloader.
* </p>
* <h1>Example</h1>
*
* <pre>
* <code>CompositeClassLoader loader = new CompositeClassLoader();
* loader.add(MyClass.class.getClassLoader());
* loader.add(new AnotherClassLoader());
*
* loader.loadClass("com.blah.ChickenPlucker");
* </code>
* </pre>
* <p>
* The above code will attempt to load a class from the following classloaders (in order):
* </p>
* <ul>
* <li>AnotherClassLoader (and all its parents)</li>
* <li>The classloader for MyClas (and all its parents)</li>
* <li>The thread's context classloader (and all its parents)</li>
* <li>The classloader for XStream (and all its parents)</li>
* </ul>
* <p>
* The added classloaders are kept with weak references to allow an application container to reload classes.
* </p>
*
* @author Joe Walnes
* @author Jörg Schaible
* @since 1.0.3
*/
public class CompositeClassLoader extends ClassLoader {
static {
// see http://www.cs.duke.edu/csed/java/jdk1.7/technotes/guides/lang/cl-mt.html
registerAsParallelCapable();
}
private final ReferenceQueue<ClassLoader> queue = new ReferenceQueue<>();
private final List<WeakReference<ClassLoader>> classLoaders = new ArrayList<>();
public CompositeClassLoader() {
addInternal(Object.class.getClassLoader()); // bootstrap loader.
addInternal(getClass().getClassLoader()); // whichever classloader loaded this jar.
}
/**
* Add a loader to the n
*
* @param classLoader
*/
public synchronized void add(final ClassLoader classLoader) {
cleanup();
if (classLoader != null) {
addInternal(classLoader);
}
}
private void addInternal(final ClassLoader classLoader) {
WeakReference<ClassLoader> refClassLoader = null;
for (final Iterator<WeakReference<ClassLoader>> iterator = classLoaders.iterator(); iterator.hasNext();) {
final WeakReference<ClassLoader> ref = iterator.next();
final ClassLoader cl = ref.get();
if (cl == null) {
iterator.remove();
} else if (cl == classLoader) {
iterator.remove();
refClassLoader = ref;
}
}
classLoaders.add(0, refClassLoader != null ? refClassLoader : new WeakReference<>(classLoader, queue));
}
@Override
public Class<?> loadClass(final String name) throws ClassNotFoundException {
final List<ClassLoader> copy = new ArrayList<>(classLoaders.size());
synchronized (this) {
cleanup();
for (final WeakReference<ClassLoader> ref : classLoaders) {
final ClassLoader cl = ref.get();
if (cl != null) {
copy.add(cl);
}
}
}
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
for (final ClassLoader classLoader : copy) {
if (classLoader == contextClassLoader) {
contextClassLoader = null;
}
try {
return classLoader.loadClass(name);
} catch (final ClassNotFoundException notFound) {
// ok.. try another one
}
}
// One last try - the context class loader associated with the current thread. Often used in j2ee servers.
// Note: The contextClassLoader cannot be added to the classLoaders list up front as the thread that constructs
// XStream is potentially different to thread that uses it.
if (contextClassLoader != null) {
return contextClassLoader.loadClass(name);
} else {
throw new ClassNotFoundException(name);
}
}
private void cleanup() {
Reference<? extends ClassLoader> ref;
while ((ref = queue.poll()) != null) {
classLoaders.remove(ref);
}
}
}