/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You 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 org.apache.geode.internal;
import static org.junit.Assert.*;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.Vector;
import org.apache.bcel.Constants;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.generic.ClassGen;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.contrib.java.lang.system.RestoreSystemProperties;
import org.junit.experimental.categories.Category;
import org.apache.geode.test.junit.categories.UnitTest;
/**
* Unit tests for {@link ClassPathLoader}.
*
* @since GemFire 6.5.1.4
*/
@Category(UnitTest.class)
public class ClassPathLoaderTest {
private static final int GENERATED_CLASS_BYTES_COUNT = 354;
@Rule
public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties();
@Before
public void setUp() throws Exception {
System.setProperty(ClassPathLoader.EXCLUDE_TCCL_PROPERTY, "false");
}
/**
* Verifies that {@link ClassPathLoader#getLatest()} is always initialized and returns a
* <tt>ClassPathLoader</tt> instance.
*/
@Test
public void testLatestExists() throws Exception {
System.out.println("\nStarting ClassPathLoaderTest#testLatestExists");
assertNotNull(ClassPathLoader.getLatest());
}
/**
* Verifies that {@link ClassPathLoader#getLatest()} throws <tt>ClassNotFoundException</tt> when
* class does not exist.
*/
@Test
public void testForNameThrowsClassNotFoundException() throws Exception {
System.out.println("\nStarting ClassPathLoaderTest#testForNameThrowsClassNotFoundException");
try {
String classToLoad = "com.nowhere.DoesNotExist";
ClassPathLoader.getLatest().forName(classToLoad);
fail();
} catch (ClassNotFoundException expected) {
// Expected
}
}
/**
* Verifies that {@link ClassPathLoader#getLatest()} finds and loads class via
* <tt>Class.forName(String, boolean, ClassLoader)</tt> when class does exist.
*/
@Test
public void testForName() throws Exception {
System.out.println("\nStarting ClassPathLoaderTest#testForName");
String classToLoad = "org.apache.geode.internal.classpathloaderjunittest.DoesExist";
Class<?> clazz = ClassPathLoader.getLatest().forName(classToLoad);
assertNotNull(clazz);
}
/**
* Verifies that {@link ClassPathLoader#getLatest()} can actually <tt>getResource</tt> when it
* exists.
*/
@Test
public void testGetResource() throws Exception {
System.out.println("\nStarting ClassPathLoaderTest#testGetResource");
String resourceToGet = "org/apache/geode/internal/classpathloaderjunittest/DoesExist.class";
URL url = ClassPathLoader.getLatest().getResource(resourceToGet);
assertNotNull(url);
InputStream is = url != null ? url.openStream() : null;
assertNotNull(is);
int totalBytesRead = 0;
byte[] input = new byte[256];
BufferedInputStream bis = new BufferedInputStream(is);
for (int bytesRead = bis.read(input); bytesRead > -1;) {
totalBytesRead += bytesRead;
bytesRead = bis.read(input);
}
bis.close();
// if the following fails then maybe javac changed and DoesExist.class
// contains other than 374 bytes of data... consider updating this test
assertEquals(GENERATED_CLASS_BYTES_COUNT, totalBytesRead);
}
/**
* Verifies that {@link ClassPathLoader#getLatest()} can actually <tt>getResources</tt> when it
* exists.
*/
@Test
public void testGetResources() throws Exception {
System.out.println("\nStarting ClassPathLoaderTest#testGetResources");
String resourceToGet = "org/apache/geode/internal/classpathloaderjunittest/DoesExist.class";
Enumeration<URL> urls = ClassPathLoader.getLatest().getResources(resourceToGet);
assertNotNull(urls);
assertTrue(urls.hasMoreElements());
URL url = urls.nextElement();
InputStream is = url != null ? url.openStream() : null;
assertNotNull(is);
int totalBytesRead = 0;
byte[] input = new byte[256];
BufferedInputStream bis = new BufferedInputStream(is);
for (int bytesRead = bis.read(input); bytesRead > -1;) {
totalBytesRead += bytesRead;
bytesRead = bis.read(input);
}
bis.close();
// if the following fails then maybe javac changed and DoesExist.class
// contains other than 374 bytes of data... consider updating this test
assertEquals(GENERATED_CLASS_BYTES_COUNT, totalBytesRead);
}
/**
* Verifies that {@link ClassPathLoader#getLatest()} can actually <tt>getResourceAsStream</tt>
* when it exists.
*/
@Test
public void testGetResourceAsStream() throws Exception {
System.out.println("\nStarting ClassPathLoaderTest#testGetResourceAsStream");
String resourceToGet = "org/apache/geode/internal/classpathloaderjunittest/DoesExist.class";
InputStream is = ClassPathLoader.getLatest().getResourceAsStream(resourceToGet);
assertNotNull(is);
int totalBytesRead = 0;
byte[] input = new byte[256];
BufferedInputStream bis = new BufferedInputStream(is);
for (int bytesRead = bis.read(input); bytesRead > -1;) {
totalBytesRead += bytesRead;
bytesRead = bis.read(input);
}
bis.close();
// if the following fails then maybe javac changed and DoesExist.class
// contains other than 374 bytes of data... consider updating this test
assertEquals(GENERATED_CLASS_BYTES_COUNT, totalBytesRead);
}
/**
* Verifies that the {@link GeneratingClassLoader} works and always generates the named class.
* This is a control which ensures that tests depending on <tt>GeneratingClassLoader</tt> are
* valid.
*/
@Test
public void testGeneratingClassLoader() throws Exception {
System.out.println("\nStarting ClassPathLoaderTest#testGeneratingClassLoader");
ClassLoader gcl = new GeneratingClassLoader();
String classToLoad = "com.nowhere.TestGeneratingClassLoader";
Class<?> clazz = gcl.loadClass(classToLoad);
assertNotNull(clazz);
assertEquals(classToLoad, clazz.getName());
Object obj = clazz.newInstance();
assertEquals(clazz.getName(), obj.getClass().getName());
try {
Class.forName(classToLoad);
fail("Should have thrown ClassNotFoundException");
} catch (ClassNotFoundException expected) {
// Expected
}
Class<?> clazzForName = Class.forName(classToLoad, true, gcl);
assertNotNull(clazzForName);
assertEquals(clazz, clazzForName);
Object objForName = clazzForName.newInstance();
assertEquals(classToLoad, objForName.getClass().getName());
}
/**
* Verifies that custom loader is used to load class.
*/
@Test
public void testForNameWithCustomLoader() throws Exception {
System.out.println("\nStarting ClassPathLoaderTest#testForNameWithCustomLoader");
ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
dcl = dcl.addOrReplace(new GeneratingClassLoader());
String classToLoad = "com.nowhere.TestForNameWithCustomLoader";
Class<?> clazz = dcl.forName(classToLoad);
assertNotNull(clazz);
assertEquals(classToLoad, clazz.getName());
Object obj = clazz.newInstance();
assertEquals(classToLoad, obj.getClass().getName());
}
/**
* Verifies that {@link Class#forName(String, boolean, ClassLoader)} used with
* {@link ClassPathLoader} works as expected with named object arrays, while
* {@link ClassLoader#loadClass(String)} throws ClassNotFoundException for named object arrays.
*/
@Test
public void testForNameWithObjectArray() throws Exception {
System.out.println("\nStarting ClassPathLoaderTest#testForNameWithObjectArray");
ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
String classToLoad = "[Ljava.lang.String;";
Class<?> clazz = null;
clazz = dcl.forName(classToLoad);
assertEquals(classToLoad, clazz.getName());
}
/**
* Verifies that TCCL finds the class when {@link Class#forName(String, boolean, ClassLoader)}
* uses {@link ClassPathLoader}.
*/
@Test
public void testForNameWithTCCL() throws Exception {
System.out.println("\nStarting ClassPathLoaderTest#testForNameWithTCCL");
final ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
final String classToLoad = "com.nowhere.TestForNameWithTCCL";
try {
dcl.forName(classToLoad);
fail("Should have thrown ClassNotFoundException");
} catch (ClassNotFoundException expected) {
// Expected
}
ClassLoader cl = Thread.currentThread().getContextClassLoader();
try {
// ensure that TCCL is only CL that can find this class
Thread.currentThread().setContextClassLoader(new GeneratingClassLoader());
Class<?> clazz = dcl.forName(classToLoad);
assertNotNull(clazz);
Object instance = clazz.newInstance();
assertNotNull(instance);
assertEquals(classToLoad, instance.getClass().getName());
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
try {
dcl.forName(classToLoad);
fail("Should have thrown ClassNotFoundException");
} catch (ClassNotFoundException expected) {
// Expected
}
}
/**
* Verifies that the {@link NullClassLoader} works and never finds the named class. This is a
* control which ensures that tests depending on <tt>NullClassLoader</tt> are valid.
*/
@Test
public void testNullClassLoader() throws Exception {
System.out.println("\nStarting ClassPathLoaderTest#testNullClassLoader");
ClassLoader cl = new NullClassLoader();
String classToLoad = "java.lang.String";
try {
Class.forName(classToLoad, true, cl);
fail();
} catch (ClassNotFoundException expected) {
// Expected
}
String resourceToGet = "java/lang/String.class";
URL url = cl.getResource(resourceToGet);
assertNull(url);
InputStream is = cl.getResourceAsStream(resourceToGet);
assertNull(is);
}
/**
* Verifies that the {@link SimpleClassLoader} works and finds classes that the parent can find.
* This is a control which ensures that tests depending on <tt>SimpleClassLoader</tt> are valid.
*/
@Test
public void testSimpleClassLoader() throws Exception {
System.out.println("\nStarting ClassPathLoaderTest#testSimpleClassLoader");
ClassLoader cl = new SimpleClassLoader(getClass().getClassLoader());
String classToLoad = "java.lang.String";
Class<?> clazz = Class.forName(classToLoad, true, cl);
assertNotNull(clazz);
String resourceToGet = "java/lang/String.class";
URL url = cl.getResource(resourceToGet);
assertNotNull(url);
InputStream is = cl.getResourceAsStream(resourceToGet);
assertNotNull(is);
}
/**
* Verifies that the {@link BrokenClassLoader} is broken and always throws errors. This is a
* control which ensures that tests depending on <tt>BrokenClassLoader</tt> are valid.
*/
@Test
public void testBrokenClassLoader() throws Exception {
System.out.println("\nStarting ClassPathLoaderTest#testBrokenClassLoader");
ClassLoader cl = new BrokenClassLoader();
String classToLoad = "java.lang.String";
try {
Class.forName(classToLoad, true, cl);
fail();
} catch (ClassNotFoundException e) {
throw e;
} catch (BrokenError expected) {
// Expected
}
String resourceToGet = "java/lang/String.class";
try {
cl.getResource(resourceToGet);
fail();
} catch (BrokenError expected) {
// Expected
}
try {
cl.getResourceAsStream(resourceToGet);
fail();
} catch (BrokenError expected) {
// Expected
}
}
/**
* Verifies that the {@link BrokenClassLoader} is broken and always throws errors even when used
* as a TCCL from {@link ClassPathLoader}. This is primarily a control which ensures that tests
* depending on <tt>BrokenClassLoader</tt> are valid, but it also verifies that TCCL is included
* by default by <tt>ClassPathLoader</tt>.
*/
@Test
public void testBrokenTCCLThrowsErrors() throws Exception {
System.out.println("\nStarting ClassPathLoaderTest#testBrokenTCCLThrowsErrors");
ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
dcl.addOrReplace(new NullClassLoader());
ClassLoader cl = Thread.currentThread().getContextClassLoader();
try {
// set the TCCL to throw errors
Thread.currentThread().setContextClassLoader(new BrokenClassLoader());
String classToLoad = "java.lang.String";
try {
dcl.forName(classToLoad);
fail();
} catch (ClassNotFoundException e) {
throw e;
} catch (BrokenError expected) {
// Expected
}
String resourceToGet = "java/lang/String.class";
try {
dcl.getResource(resourceToGet);
fail();
} catch (BrokenError expected) {
// Expected
}
try {
dcl.getResourceAsStream(resourceToGet);
fail();
} catch (BrokenError expected) {
// Expected
}
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
}
/**
* Verifies that the class classloader or system classloader will find the class or resource.
* Parent is a {@link NullClassLoader} while the TCCL is an excluded {@link BrokenClassLoader}.
*/
@Test
public void testEverythingWithDefaultLoader() throws Exception {
System.out.println("\nStarting ClassPathLoaderTest#testEverythingWithDefaultLoader");
// create DCL such that parent cannot find anything
ClassPathLoader dcl = ClassPathLoader.createWithDefaults(true);
dcl.addOrReplace(new NullClassLoader());
ClassLoader cl = Thread.currentThread().getContextClassLoader();
try {
// set the TCCL to never find anything
Thread.currentThread().setContextClassLoader(new BrokenClassLoader());
String classToLoad = "java.lang.String";
Class<?> clazz = dcl.forName(classToLoad);
assertNotNull(clazz);
String resourceToGet = "java/lang/String.class";
URL url = dcl.getResource(resourceToGet);
assertNotNull(url);
InputStream is = dcl.getResourceAsStream(resourceToGet);
assertNotNull(is);
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
}
/**
* Verifies that the 3rd custom loader will find the class. Parent cannot find it and TCCL is
* broken. This verifies that all custom loaders are checked and that the custom loaders are all
* checked before TCCL.
*/
@Test
public void testForNameWithMultipleCustomLoaders() throws Exception {
System.out.println("\nStarting ClassPathLoaderTest#testForNameWithMultipleCustomLoaders");
// create DCL such that the 3rd loader should find the class
// first custom loader becomes parent which won't find anything
ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
final GeneratingClassLoader generatingClassLoader = new GeneratingClassLoader();
dcl = dcl.addOrReplace(generatingClassLoader);
dcl = dcl.addOrReplace(new SimpleClassLoader(getClass().getClassLoader()));
dcl = dcl.addOrReplace(new NullClassLoader());
String classToLoad = "com.nowhere.TestForNameWithMultipleCustomLoaders";
ClassLoader cl = Thread.currentThread().getContextClassLoader();
try {
// set TCCL to throw errors which makes sure we find before checking TCCL
Thread.currentThread().setContextClassLoader(new BrokenClassLoader());
Class<?> clazz = dcl.forName(classToLoad);
assertNotNull(clazz);
assertEquals(classToLoad, clazz.getName());
assertTrue("Class not loaded by a GeneratingClassLoader.",
clazz.getClassLoader() instanceof GeneratingClassLoader);
assertEquals("Class not loaded by generatingClassLoader.", generatingClassLoader,
clazz.getClassLoader());
Object obj = clazz.newInstance();
assertEquals(classToLoad, obj.getClass().getName());
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
}
/**
* Verifies that setting <tt>excludeThreadContextClassLoader</tt> to true will indeed exclude the
* TCCL.
*/
@Test
public void testExcludeTCCL() throws Exception {
System.out.println("\nStarting ClassPathLoaderTest#testExcludeTCCL");
ClassPathLoader dcl = ClassPathLoader.createWithDefaults(true);
String classToLoad = "com.nowhere.TestExcludeTCCL";
try {
dcl.forName(classToLoad);
fail("Should have thrown ClassNotFoundException");
} catch (ClassNotFoundException expected) {
// Expected
}
ClassLoader cl = Thread.currentThread().getContextClassLoader();
try {
// ensure that TCCL is only CL that can find this class
Thread.currentThread().setContextClassLoader(new GeneratingClassLoader());
dcl.forName(classToLoad);
fail("Should have thrown ClassNotFoundException");
} catch (ClassNotFoundException expected) {
// Expected
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
}
/**
* Verifies that <tt>getResource</tt> will skip TCCL if <tt>excludeThreadContextClassLoader</tt>
* has been set to true.
*/
@Test
public void testGetResourceExcludeTCCL() throws Exception {
System.out.println("\nStarting ClassPathLoaderTest#testGetResourceExcludeTCCL");
ClassPathLoader dcl = ClassPathLoader.createWithDefaults(true);
String resourceToGet = "com/nowhere/testGetResourceExcludeTCCL.rsc";
assertNull(dcl.getResource(resourceToGet));
ClassLoader cl = Thread.currentThread().getContextClassLoader();
try {
// ensure that TCCL is only CL that can find this resource
Thread.currentThread().setContextClassLoader(new GeneratingClassLoader());
assertNull(dcl.getResource(resourceToGet));
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
}
/**
* Verifies that <tt>getResourceAsStream</tt> will skip TCCL if
* <tt>excludeThreadContextClassLoader</tt> has been set to true.
*/
@Test
public void testGetResourceAsStreamExcludeTCCL() throws Exception {
System.out.println("\nStarting ClassPathLoaderTest#testGetResourceAsStreamExcludeTCCL");
ClassPathLoader dcl = ClassPathLoader.createWithDefaults(true);
String resourceToGet = "com/nowhere/testGetResourceAsStreamExcludeTCCL.rsc";
assertNull(dcl.getResourceAsStream(resourceToGet));
ClassLoader cl = Thread.currentThread().getContextClassLoader();
try {
// ensure that TCCL is only CL that can find this resource
Thread.currentThread().setContextClassLoader(new GeneratingClassLoader());
assertNull(dcl.getResourceAsStream(resourceToGet));
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
}
@Test
public void testAddFindsLatestClassLoader() throws Exception {
System.out.println("\nStarting ClassPathLoaderTest#testAddFindsLatestClassLoader");
ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
dcl = dcl.addOrReplace(new GeneratingClassLoader());
String classToLoad = "com.nowhere.TestAddFindsLatestClassLoader";
Class<?> clazz = dcl.forName(classToLoad);
assertNotNull(clazz);
dcl = dcl.addOrReplace(new BrokenClassLoader());
try {
dcl.forName(classToLoad);
fail();
} catch (BrokenError expected) {
// Expected
}
}
/**
* Verifies removing a ClassLoader.
*/
@Test
public void testRemoveClassLoader() throws Exception {
System.out.println("\nStarting ClassPathLoaderTest#testRemoveClassLoader");
GeneratingClassLoader genClassLoader = new GeneratingClassLoader();
ClassPathLoader cpl = ClassPathLoader.createWithDefaults(false);
cpl = cpl.addOrReplace(genClassLoader);
String classToLoad = "com.nowhere.TestRemoveClassLoader";
Class<?> clazz = cpl.forName(classToLoad);
assertNotNull(clazz);
cpl = cpl.remove(genClassLoader);
try {
clazz = cpl.forName(classToLoad);
fail();
} catch (ClassNotFoundException expected) {
// Expected
}
}
/**
* Verifies that a ClassLoader will be replaced when added more than once.
*/
@Test
public void testClassLoaderReplace() throws Exception {
System.out.println("\nStarting ClassPathLoaderTest#testClassLoaderReplace");
String class1ToLoad = "ClassA";
String class2ToLoad = "ClassB";
ClassPathLoader cpl = ClassPathLoader.createWithDefaults(false);
cpl = cpl.addOrReplace(new OneClassClassLoader(class1ToLoad));
try {
@SuppressWarnings("unused")
Class<?> clazz = cpl.forName(class1ToLoad);
} catch (ClassNotFoundException unexpected) {
fail();
}
try {
@SuppressWarnings("unused")
Class<?> clazz = cpl.forName(class2ToLoad);
fail();
} catch (ClassNotFoundException expected) {
// Expected
}
cpl = cpl.addOrReplace(new OneClassClassLoader(class2ToLoad));
try {
@SuppressWarnings("unused")
Class<?> clazz = cpl.forName(class2ToLoad);
} catch (ClassNotFoundException unexpected) {
fail();
}
try {
@SuppressWarnings("unused")
Class<?> clazz = cpl.forName(class1ToLoad);
fail();
} catch (ClassNotFoundException expected) {
// Expected
}
}
@Test
public void testAsClassLoaderLoadClassWithMultipleCustomLoaders() throws Exception {
System.out.println(
"\nStarting ClassPathLoaderTest#testAsClassLoaderLoadClassWithMultipleCustomLoaders");
// create DCL such that the 3rd loader should find the class
// first custom loader becomes parent which won't find anything
ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
final GeneratingClassLoader generatingClassLoader = new GeneratingClassLoader();
dcl = dcl.addOrReplace(generatingClassLoader);
dcl = dcl.addOrReplace(new SimpleClassLoader(getClass().getClassLoader()));
dcl = dcl.addOrReplace(new NullClassLoader());
final String classToLoad = "com.nowhere.TestForNameWithMultipleCustomLoaders";
ClassLoader cl = Thread.currentThread().getContextClassLoader();
try {
// set TCCL to throw errors which makes sure we find before checking TCCL
Thread.currentThread().setContextClassLoader(new BrokenClassLoader());
final ClassLoader classLoader = dcl.asClassLoader();
final Class<?> clazz = classLoader.loadClass(classToLoad);
assertNotNull(clazz);
assertEquals(classToLoad, clazz.getName());
assertTrue(clazz.getClassLoader() instanceof GeneratingClassLoader);
assertEquals(generatingClassLoader, clazz.getClassLoader());
final Object obj = clazz.newInstance();
assertEquals(classToLoad, obj.getClass().getName());
final Class<?> clazz2 = dcl.forName(classToLoad);
assertSame("Should load same class as calling classLoader.", clazz, clazz2);
final Class<?> clazz3 = Class.forName(classToLoad, true, classLoader);
assertSame("Should load same class as calling classLoader.", clazz, clazz3);
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
}
private static void exploreClassLoaders() {
System.out.println("Thread.currentThread().getContextClassLoader()...");
exploreClassLoader(Thread.currentThread().getContextClassLoader(), 1);
System.out.println("class.getClassLoader()...");
exploreClassLoader(ClassPathLoaderTest.class.getClassLoader(), 1);
System.out.println("ClassLoader.getSystemClassLoader()...");
exploreClassLoader(ClassLoader.getSystemClassLoader(), 1);
}
private static void exploreClassLoader(ClassLoader cl, int indent) {
String prefix = "";
for (int i = 0; i < indent; i++) {
prefix += "\t";
}
System.out.println(prefix + "ClassLoader toString() = " + cl);
Class<?> clazz = cl.getClass();
System.out.println(prefix + "ClassLoader getClass().getName() = " + clazz.getName());
exploreClassLoaderSuperClass(prefix, clazz);
try {
URL[] urls = ((URLClassLoader) cl).getURLs();
StringBuilder sb = new StringBuilder(prefix).append("ClassLoader getURLs = [");
for (int i = 0; i < urls.length; i++) {
if (i > 0)
sb.append(", ");
sb.append(urls[i].toString());
}
sb.append("]");
System.out.println(sb.toString());
} catch (Exception e) {
System.out.println(prefix + "ClassLoader is not a URLClassLoader");
}
ClassLoader parent = cl.getParent();
if (parent != null) {
System.out.println(prefix + "ClassLoader has parent...");
exploreClassLoader(parent, ++indent);
}
}
private static void exploreClassLoaderSuperClass(String prefix, Class<?> clazz) {
Class<?> superClazz = clazz.getSuperclass();
if (superClazz != null) {
System.out.println(
prefix + " getSuperclass().getName() = " + superClazz.getName());
exploreClassLoaderSuperClass(prefix, superClazz);
}
}
/**
* Custom class loader which will never find any class or resource.
*/
static class NullClassLoader extends ClassLoader {
public NullClassLoader() {
super(null); // no parent!!
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
@Override
public URL getResource(String name) {
return null;
}
}
/**
* Custom class loader which will find anything the parent can find.
*/
static class SimpleClassLoader extends ClassLoader {
public SimpleClassLoader(ClassLoader parent) {
super(parent);
}
}
/**
* Custom class loader which is broken and always throws errors.
*/
static class BrokenClassLoader extends ClassLoader {
public BrokenClassLoader() {
super(null); // no parent!!
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
throw new BrokenError();
}
@Override
public URL getResource(String name) {
throw new BrokenError();
}
}
/**
* Custom class loader which uses BCEL to always dynamically generate a class for any class name
* it tries to load.
*/
static class GeneratingClassLoader extends ClassLoader {
/**
* Currently unused but potentially useful for some future test. This causes this loader to only
* generate a class that the parent could not find.
*
* @param parent the parent class loader to check with first
*/
@SuppressWarnings("unused")
public GeneratingClassLoader(ClassLoader parent) {
super(parent);
}
/**
* Specifies no parent to ensure that this loader generates the named class.
*/
public GeneratingClassLoader() {
super(null); // no parent!!
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
ClassGen cg = new ClassGen(name, "java.lang.Object", "<generated>",
Constants.ACC_PUBLIC | Constants.ACC_SUPER, null);
cg.addEmptyConstructor(Constants.ACC_PUBLIC);
JavaClass jClazz = cg.getJavaClass();
byte[] bytes = jClazz.getBytes();
return defineClass(jClazz.getClassName(), bytes, 0, bytes.length);
}
@Override
protected URL findResource(String name) {
URL url = null;
try {
url = getTempFile().getAbsoluteFile().toURI().toURL();
System.out.println("GeneratingClassLoader#findResource returning " + url);
} catch (IOException e) {
}
return url;
}
@Override
protected Enumeration<URL> findResources(String name) throws IOException {
URL url = null;
try {
url = getTempFile().getAbsoluteFile().toURI().toURL();
System.out.println("GeneratingClassLoader#findResources returning " + url);
} catch (IOException e) {
}
Vector<URL> urls = new Vector<URL>();
urls.add(url);
return urls.elements();
}
protected File getTempFile() {
return null;
}
}
static class OneClassClassLoader extends ClassLoader {
private final GeneratingClassLoader genClassLoader = new GeneratingClassLoader();
private String className;
public OneClassClassLoader(final String className) {
super(null); // no parent!!
this.className = className;
}
@Override
public Class<?> findClass(String name) throws ClassNotFoundException {
if (!name.equals(className)) {
throw new ClassNotFoundException();
} else {
return this.genClassLoader.findClass(name);
}
}
@Override
public boolean equals(final Object other) {
return (other instanceof OneClassClassLoader);
}
}
@SuppressWarnings("serial")
static class BrokenError extends Error {
}
}