
net.grinder.util.BlockingClassLoader Maven / Gradle / Ivy
The newest version!
// Copyright (C) 2009 - 2011 Philip Aston
// All rights reserved.
//
// This file is part of The Grinder software distribution. Refer to
// the file LICENSE which is part of The Grinder distribution for
// licensing details. The Grinder distribution is available on the
// Internet at http://grinder.sourceforge.net/
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
package net.grinder.util;
import static java.util.Arrays.asList;
import static java.util.Collections.enumeration;
import static java.util.Collections.list;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Class loader that selectively ignores its parents, allowing alternative
* implementations of classes to be loaded.
*
* @author Philip Aston
*/
public class BlockingClassLoader extends URLClassLoader {
private final boolean m_respectGrandparents;
private final Classes m_blocked;
private final Classes m_isolated;
private final Classes m_shared;
private static URL[] join(List additionalClassPath, URL[] urls) {
final List classPath = new ArrayList(additionalClassPath);
classPath.addAll(asList(urls));
return classPath.toArray(new URL[classPath.size()]);
}
/**
* Constructor.
*
*
* The behaviour is determined by three sets of class names. Each set
* contains fully qualified class names, or fully qualified prefixes ending in
* "*", that identify the classes beginning with the package prefix.
*
*
*
* Resource names may also be specified, fully qualified with '/' separators
* as necessary. Wild card package names also filter resources; the '.'
* separators are translated internally to '/'s.
*
*
*
* If multiple sets refer to the same class, {@code shared} has priority
* over {@code blocked}, and both have priority over {@code isolated},
* whether or not the class is specified explicitly or using a package prefix.
*
*
* @param parent
* Parent classloader.
* @param additionalClassPath
* Prepended to the parent's URLs to calculate the classpath for this
* class loader.
* @param blocked
* Classes to block, whether or not the parent class loader has its
* own copies.
* @param isolated
* Classes to load in this class loader, whether or not the parent
* class loader has its own copies.
* @param shared
* Classes that should be loaded by the parent class loader,
* overriding {@code blocked} and {@code isolated}.
* @param respectGrandparents
* Only block or isolate classes from the parent class loader.
*/
public BlockingClassLoader(URLClassLoader parent,
List additionalClassPath,
Set blocked,
Set isolated,
Set shared,
boolean respectGrandparents) {
super(join(additionalClassPath, parent.getURLs()), parent);
m_blocked = new Classes(blocked);
m_isolated = new Classes(isolated);
m_shared = new Classes(shared);
m_respectGrandparents = respectGrandparents;
}
/**
* Simplified constructor that uses standard application classloader as
* the parent.
*
* @param additionalClassPath
* Prepended to the parent's URLs to calculate the classpath for this
* class loader.
* @param blocked
* Classes to block, whether or not the parent class loader has its
* own copies.
* @param isolated
* Classes to load in this class loader, whether or not the parent
* class loader has its own copies.
* @param shared
* Classes that should be loaded by the parent class loader,
* overriding {@code blocked} and {@code isolated}.
* @param respectGrandparents
* Only block or isolate classes from the parent class loader.
*/
public BlockingClassLoader(List additionalClassPath,
Set blocked,
Set isolated,
Set shared,
boolean respectGrandparents) {
this((URLClassLoader)BlockingClassLoader.class.getClassLoader(),
additionalClassPath,
blocked,
isolated,
shared,
respectGrandparents);
}
/**
* Simplified constructor without {@code additionalClassPath}.
*
* @param blocked
* Classes to block, whether or not the parent class loader has its
* own copies.
* @param isolated
* Classes to load in this class loader, whether or not the parent
* class loader has its own copies.
* @param shared
* Classes that should be loaded by the parent class loader,
* overriding {@code blocked} and {@code isolated}.
* @param respectGrandparents
* Only block or isolate classes from the parent class loader.
*/
public BlockingClassLoader(Set blocked,
Set isolated,
Set shared,
boolean respectGrandparents) {
this(Collections.emptyList(),
blocked,
isolated,
shared,
respectGrandparents);
}
/**
* Override only to check parent ClassLoader if not blocked.
*
* {@inheritDoc}
*/
@Override protected Class> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
if (!m_shared.matches(name, false)) {
if (m_respectGrandparents) {
try {
// We always have a grandparent classloader.
return Class.forName(name, resolve, getParent().getParent());
}
catch (ClassNotFoundException e) {
// Grandparent knows nothing.
}
}
if (m_blocked.matches(name, false)) {
throw new ClassNotFoundException();
}
if (m_isolated.matches(name, false)) {
synchronized (this) {
Class> c = findLoadedClass(name);
if (c == null) {
c = findClass(name);
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
}
return super.loadClass(name, resolve);
}
/**
* Override only to check parent ClassLoader if not blocked.
*
* {@inheritDoc}
*/
@Override public URL getResource(String name) {
if (!m_shared.matches(name, true)) {
if (m_respectGrandparents) {
// We always have a grandparent classloader.
final URL grandParentResult = getParent().getParent().getResource(name);
if (grandParentResult != null) {
return grandParentResult;
}
}
if (m_blocked.matches(name, true)) {
return null;
}
if (m_isolated.matches(name, true)) {
return findResource(name);
}
}
return super.getResource(name);
}
/**
* Override only to check parent ClassLoader if not blocked.
*
* {@inheritDoc}
*/
@Override
public Enumeration getResources(String name) throws IOException {
if (!m_shared.matches(name, true)) {
final List result = new ArrayList();
if (m_respectGrandparents) {
// We always have a grandparent classloader.
result.addAll(list(getParent().getParent().getResources(name)));
}
if (m_blocked.matches(name, true)) {
return enumeration(result);
}
if (m_isolated.matches(name, true)) {
result.addAll(list(findResources(name)));
return enumeration(result);
}
}
return super.getResources(name);
}
private static class Classes {
private final Set m_classNames = new HashSet();
private final Set m_prefixes = new HashSet();
public Classes(Set wildcardNames) {
for (String name : wildcardNames) {
final int index = name.indexOf('*');
if (index >= 0) {
m_prefixes.add(name.substring(0, index));
}
else {
m_classNames.add(name);
}
}
}
public boolean matches(String name, boolean isResource) {
final String packageName = isResource ? name.replace('/', '.') : name;
if (m_classNames.contains(name)) {
return true;
}
for (String prefix : m_prefixes) {
if (packageName.startsWith(prefix)) {
return true;
}
}
return false;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy