blob: 4f8832f52f3555104c3d287ea1724ddaf6df60c4 [file] [log] [blame]
/*=========================================================================
* Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved.
* This product is protected by U.S. and international copyright
* and intellectual property laws. Pivotal products are covered by
* one or more patents listed at http://www.pivotal.io/patents.
*=========================================================================
*/
package com.gemstone.gemfire.internal;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.Vector;
import junit.framework.TestCase;
import org.apache.bcel.Constants;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.generic.ClassGen;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import com.gemstone.gemfire.test.junit.categories.UnitTest;
/**
* Test the {@link ClassPathLoader}.
*
* @author Kirk Lund
* @since 6.5.1.4
*/
@Category(UnitTest.class)
public class ClassPathLoaderJUnitTest extends TestCase {
private static final int TEMP_FILE_BYTES_COUNT = 256;
private static final int GENERATED_CLASS_BYTES_COUNT = 362;
private static final String ORIGINAL_EXCLUDE_TCCL_VALUE;
private boolean deleteExtDir = false;
private boolean deleteLibDir = false;
static {
ORIGINAL_EXCLUDE_TCCL_VALUE = System.getProperty(ClassPathLoader.EXCLUDE_TCCL_PROPERTY, Boolean
.toString(ClassPathLoader.EXCLUDE_TCCL_DEFAULT_VALUE));
exploreClassLoaders(); // optional output for developer's convenience
}
private volatile File tempFile;
private volatile File tempFile2;
public ClassPathLoaderJUnitTest(String name) {
super(name);
}
@Override
public void setUp() throws Exception {
System.setProperty(ClassPathLoader.EXCLUDE_TCCL_PROPERTY, "false");
File workingDir = new File(System.getProperty("user.dir")).getAbsoluteFile();
this.tempFile = File.createTempFile("ClassPathLoaderJUnitTest.", null, workingDir);
FileOutputStream fos = new FileOutputStream(this.tempFile);
fos.write(new byte[TEMP_FILE_BYTES_COUNT]);
fos.close();
this.tempFile2 = File.createTempFile("ClassPathLoaderJUnitTest.", null, workingDir);
fos = new FileOutputStream(this.tempFile2);
fos.write(new byte[TEMP_FILE_BYTES_COUNT]);
fos.close();
}
@Override
public void tearDown() throws Exception {
System.setProperty(ClassPathLoader.EXCLUDE_TCCL_PROPERTY, ORIGINAL_EXCLUDE_TCCL_VALUE);
// these deletions fail on windows
// if (this.deleteLibDir) {
// FileUtil.delete(ClassPathLoader.EXT_LIB_DIR.getParentFile());
// } else {
// if (this.deleteExtDir) {
// FileUtil.delete(ClassPathLoader.EXT_LIB_DIR);
// } else {
// FileUtil.delete(new File(ClassPathLoader.EXT_LIB_DIR, "ClassPathLoaderJUnit1.jar"));
// FileUtil.delete(new File(ClassPathLoader.EXT_LIB_DIR, "cplju"));
// }
// }
assertTrue(this.tempFile.delete());
}
/**
* Verifies that {@link ClassPathLoader#getLatest()} is always initialized and returns a <tt>ClassPathLoader</tt>
* instance.
*/
public void testLatestExists() throws Exception {
System.out.println("\nStarting ClassPathLoaderJUnitTest#testLatestExists");
assertNotNull(ClassPathLoader.getLatest());
}
/**
* Verifies that {@link ClassPathLoader#getLatest()} throws <tt>ClassNotFoundException</tt> when class does not exist.
*/
public void testForNameThrowsClassNotFoundException() throws Exception {
System.out.println("\nStarting ClassPathLoaderJUnitTest#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.
*/
public void testForName() throws Exception {
System.out.println("\nStarting ClassPathLoaderJUnitTest#testForName");
String classToLoad = "com.gemstone.gemfire.internal.classpathloaderjunittest.DoesExist";
Class<?> clazz = ClassPathLoader.getLatest().forName(classToLoad);
assertNotNull(clazz);
}
/**
* Verifies that {@link ClassPathLoader#getLatest()} can actually <tt>getResource</tt> when it exists.
*/
public void testGetResource() throws Exception {
System.out.println("\nStarting ClassPathLoaderJUnitTest#testGetResource");
String resourceToGet = "com/gemstone/gemfire/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.
*/
public void testGetResources() throws Exception {
System.out.println("\nStarting ClassPathLoaderJUnitTest#testGetResources");
String resourceToGet = "com/gemstone/gemfire/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.
*/
public void testGetResourceAsStream() throws Exception {
System.out.println("\nStarting ClassPathLoaderJUnitTest#testGetResourceAsStream");
String resourceToGet = "com/gemstone/gemfire/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.
*/
public void testGeneratingClassLoader() throws Exception {
System.out.println("\nStarting ClassPathLoaderJUnitTest#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.
*/
public void testForNameWithCustomLoader() throws Exception {
System.out.println("\nStarting ClassPathLoaderJUnitTest#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.
*/
public void testForNameWithObjectArray() throws Exception {
System.out.println("\nStarting ClassPathLoaderJUnitTest#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}.
*/
public void testForNameWithTCCL() throws Exception {
System.out.println("\nStarting ClassPathLoaderJUnitTest#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.
*/
public void testNullClassLoader() throws Exception {
System.out.println("\nStarting ClassPathLoaderJUnitTest#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.
*/
public void testSimpleClassLoader() throws Exception {
System.out.println("\nStarting ClassPathLoaderJUnitTest#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.
*/
public void testBrokenClassLoader() throws Exception {
System.out.println("\nStarting ClassPathLoaderJUnitTest#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>.
*/
public void testBrokenTCCLThrowsErrors() throws Exception {
System.out.println("\nStarting ClassPathLoaderJUnitTest#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}.
*/
public void testEverythingWithDefaultLoader() throws Exception {
System.out.println("\nStarting ClassPathLoaderJUnitTest#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.
*/
public void testForNameWithMultipleCustomLoaders() throws Exception {
System.out.println("\nStarting ClassPathLoaderJUnitTest#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 the 3rd custom loader will get the resource. 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.
*/
public void testGetResourceWithMultipleCustomLoaders() throws Exception {
System.out.println("\nStarting ClassPathLoaderJUnitTest#testGetResourceWithMultipleCustomLoaders");
// create DCL such that the 3rd loader should find the resource
// first custom loader becomes parent which won't find anything
ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
dcl = dcl.addOrReplace(new GeneratingClassLoader());
dcl = dcl.addOrReplace(new SimpleClassLoader(getClass().getClassLoader()));
dcl = dcl.addOrReplace(new NullClassLoader());
String resourceToGet = "com/nowhere/testGetResourceWithMultipleCustomLoaders.rsc";
ClassLoader cl = Thread.currentThread().getContextClassLoader();
try {
// set TCCL to throw errors which makes sure we find before checking TCCL
Thread.currentThread().setContextClassLoader(new BrokenClassLoader());
URL url = dcl.getResource(resourceToGet);
assertNotNull(url);
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
}
/**
* Verifies that the 3rd custom loader will get the resources. 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.
*/
public void testGetResourcesWithMultipleCustomLoaders() throws Exception {
System.out.println("\nStarting ClassPathLoaderJUnitTest#testGetResourceWithMultipleCustomLoaders");
// create DCL such that the 3rd loader should find the resource
// first custom loader becomes parent which won't find anything
ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
dcl = dcl.addOrReplace(new GeneratingClassLoader());
dcl = dcl.addOrReplace(new GeneratingClassLoader2());
dcl = dcl.addOrReplace(new SimpleClassLoader(getClass().getClassLoader()));
dcl = dcl.addOrReplace(new NullClassLoader());
String resourceToGet = "com/nowhere/testGetResourceWithMultipleCustomLoaders.rsc";
ClassLoader cl = Thread.currentThread().getContextClassLoader();
try {
// set TCCL to throw errors which makes sure we find before checking TCCL
Thread.currentThread().setContextClassLoader(new BrokenClassLoader());
Enumeration<URL> urls = dcl.getResources(resourceToGet);
assertNotNull(urls);
assertTrue(urls.hasMoreElements());
URL url = urls.nextElement();
assertNotNull(url);
// Should find two with unique URLs
assertTrue("Did not find all resources.", urls.hasMoreElements());
URL url2 = urls.nextElement();
assertNotNull(url2);
assertTrue("Resource URLs should be unique.", !url.equals(url2));
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
}
/**
* Verifies that the 3rd custom loader will get the resource. 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.
*/
public void testGetResourceAsStreamWithMultipleCustomLoaders() throws Exception {
System.out.println("\nStarting ClassPathLoaderJUnitTest#testGetResourceAsStreamWithMultipleCustomLoaders");
// create DCL such that the 3rd loader should find the resource
// first custom loader becomes parent which won't find anything
ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
dcl = dcl.addOrReplace(new GeneratingClassLoader());
dcl = dcl.addOrReplace(new SimpleClassLoader(getClass().getClassLoader()));
dcl = dcl.addOrReplace(new NullClassLoader());
String resourceToGet = "com/nowhere/testGetResourceAsStreamWithMultipleCustomLoaders.rsc";
ClassLoader cl = Thread.currentThread().getContextClassLoader();
try {
// set TCCL to throw errors which makes sure we find before checking TCCL
Thread.currentThread().setContextClassLoader(new BrokenClassLoader());
InputStream is = dcl.getResourceAsStream(resourceToGet);
assertNotNull(is);
is.close();
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
}
/**
* Verifies that setting <tt>excludeThreadContextClassLoader</tt> to true will indeed exclude the TCCL.
*/
public void testExcludeTCCL() throws Exception {
System.out.println("\nStarting ClassPathLoaderJUnitTest#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> works with custom loader from {@link ClassPathLoader}.
*/
public void testGetResourceWithCustomLoader() throws Exception {
System.out.println("\nStarting ClassPathLoaderJUnitTest#testGetResourceWithCustomLoader");
ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
dcl = dcl.addOrReplace(new GeneratingClassLoader());
String resourceToGet = "com/nowhere/testGetResourceWithCustomLoader.rsc";
URL url = dcl.getResource(resourceToGet);
assertNotNull(url);
InputStream is = url != null ? url.openStream() : null;
assertNotNull(is);
int totalBytesRead = 0;
byte[] input = new byte[128];
BufferedInputStream bis = new BufferedInputStream(is);
for (int bytesRead = bis.read(input); bytesRead > -1;) {
totalBytesRead += bytesRead;
bytesRead = bis.read(input);
}
bis.close();
assertEquals(TEMP_FILE_BYTES_COUNT, totalBytesRead);
}
/**
* Verifies that <tt>getResources</tt> works with custom loader from {@link ClassPathLoader}.
*/
public void testGetResourcesWithCustomLoader() throws Exception {
System.out.println("\nStarting ClassPathLoaderJUnitTest#testGetResourceWithCustomLoader");
ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
dcl = dcl.addOrReplace(new GeneratingClassLoader());
String resourceToGet = "com/nowhere/testGetResourceWithCustomLoader.rsc";
Enumeration<URL> urls = dcl.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[128];
BufferedInputStream bis = new BufferedInputStream(is);
for (int bytesRead = bis.read(input); bytesRead > -1;) {
totalBytesRead += bytesRead;
bytesRead = bis.read(input);
}
bis.close();
assertEquals(TEMP_FILE_BYTES_COUNT, totalBytesRead);
}
/**
* Verifies that <tt>getResourceAsStream</tt> works with custom loader from {@link ClassPathLoader}.
*/
public void testGetResourceAsStreamWithCustomLoader() throws Exception {
System.out.println("\nStarting ClassPathLoaderJUnitTest#testGetResourceAsStreamWithCustomLoader");
ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
dcl = dcl.addOrReplace(new GeneratingClassLoader());
String resourceToGet = "com/nowhere/testGetResourceAsStreamWithCustomLoader.rsc";
InputStream is = dcl.getResourceAsStream(resourceToGet);
assertNotNull(is);
int totalBytesRead = 0;
byte[] input = new byte[128];
BufferedInputStream bis = new BufferedInputStream(is);
for (int bytesRead = bis.read(input); bytesRead > -1;) {
totalBytesRead += bytesRead;
bytesRead = bis.read(input);
}
bis.close();
assertEquals(TEMP_FILE_BYTES_COUNT, totalBytesRead);
}
/**
* Verifies that <tt>getResource</tt> works with TCCL from {@link ClassPathLoader}.
*/
public void testGetResourceWithTCCL() throws Exception {
System.out.println("\nStarting ClassPathLoaderJUnitTest#testGetResourceWithTCCL");
ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
String resourceToGet = "com/nowhere/testGetResourceWithTCCL.rsc";
assertNull(dcl.getResource(resourceToGet));
ClassLoader cl = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(new GeneratingClassLoader());
URL url = dcl.getResource(resourceToGet);
assertNotNull(url);
InputStream is = url.openStream();
assertNotNull(is);
int totalBytesRead = 0;
byte[] input = new byte[128];
BufferedInputStream bis = new BufferedInputStream(is);
for (int bytesRead = bis.read(input); bytesRead > -1;) {
totalBytesRead += bytesRead;
bytesRead = bis.read(input);
}
bis.close();
assertEquals(TEMP_FILE_BYTES_COUNT, totalBytesRead);
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
}
/**
* Verifies that <tt>getResources</tt> works with TCCL from {@link ClassPathLoader}.
*/
public void testGetResourcesWithTCCL() throws Exception {
System.out.println("\nStarting ClassPathLoaderJUnitTest#testGetResourceWithTCCL");
ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
String resourceToGet = "com/nowhere/testGetResourceWithTCCL.rsc";
Enumeration<URL> urls = dcl.getResources(resourceToGet);
assertNotNull(urls);
assertFalse(urls.hasMoreElements());
ClassLoader cl = Thread.currentThread().getContextClassLoader();
try {
Thread.currentThread().setContextClassLoader(new GeneratingClassLoader());
urls = dcl.getResources(resourceToGet);
assertNotNull(urls);
URL url = urls.nextElement();
InputStream is = url.openStream();
assertNotNull(is);
int totalBytesRead = 0;
byte[] input = new byte[128];
BufferedInputStream bis = new BufferedInputStream(is);
for (int bytesRead = bis.read(input); bytesRead > -1;) {
totalBytesRead += bytesRead;
bytesRead = bis.read(input);
}
bis.close();
assertEquals(TEMP_FILE_BYTES_COUNT, totalBytesRead);
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
}
/**
* Verifies that <tt>getResourceAsStream</tt> works with TCCL from {@link ClassPathLoader}.
*/
public void testGetResourceAsStreamWithTCCL() throws Exception {
System.out.println("\nStarting ClassPathLoaderJUnitTest#testGetResourceAsStreamWithTCCL");
ClassPathLoader dcl = ClassPathLoader.createWithDefaults(false);
String resourceToGet = "com/nowhere/testGetResourceAsStreamWithTCCL.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());
InputStream is = dcl.getResourceAsStream(resourceToGet);
assertNotNull(is);
int totalBytesRead = 0;
byte[] input = new byte[128];
BufferedInputStream bis = new BufferedInputStream(is);
for (int bytesRead = bis.read(input); bytesRead > -1;) {
totalBytesRead += bytesRead;
bytesRead = bis.read(input);
}
bis.close();
assertEquals(TEMP_FILE_BYTES_COUNT, totalBytesRead);
} finally {
Thread.currentThread().setContextClassLoader(cl);
}
}
/**
* Verifies that <tt>getResource</tt> will skip TCCL if <tt>excludeThreadContextClassLoader</tt> has been set to true.
*/
public void testGetResourceExcludeTCCL() throws Exception {
System.out.println("\nStarting ClassPathLoaderJUnitTest#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.
*/
public void testGetResourceAsStreamExcludeTCCL() throws Exception {
System.out.println("\nStarting ClassPathLoaderJUnitTest#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);
}
}
public void testAddFindsLatestClassLoader() throws Exception {
System.out.println("\nStarting ClassPathLoaderJUnitTest#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.
*/
public void testRemoveClassLoader() throws Exception {
System.out.println("\nStarting ClassPathLoaderJUnitTest#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.
*/
public void testClassLoaderReplace() throws Exception {
System.out.println("\nStarting ClassPathLoaderJUnitTest#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
}
}
/**
* Verifies that JAR files found in the extlib directory will be correctly
* added to the {@link ClassPathLoader}.
*/
public void testJarsInExtLib() throws Exception {
System.out.println("\nStarting ClassPathLoaderJUnitTest#testJarsInExtLib");
this.deleteLibDir = ClassPathLoader.EXT_LIB_DIR.getParentFile().mkdirs();
this.deleteExtDir = ClassPathLoader.EXT_LIB_DIR.mkdirs();
File subdir = new File(ClassPathLoader.EXT_LIB_DIR, "cplju");
subdir.mkdir();
final ClassBuilder classBuilder = new ClassBuilder();
writeJarBytesToFile(new File(ClassPathLoader.EXT_LIB_DIR, "ClassPathLoaderJUnit1.jar"),
classBuilder.createJarFromClassContent("com/cpljunit1/ClassPathLoaderJUnit1", "package com.cpljunit1; public class ClassPathLoaderJUnit1 {}"));
writeJarBytesToFile(new File(subdir, "ClassPathLoaderJUnit2.jar"),
classBuilder.createJarFromClassContent("com/cpljunit2/ClassPathLoaderJUnit2", "package com.cpljunit2; public class ClassPathLoaderJUnit2 {}"));
ClassPathLoader classPathLoader = ClassPathLoader.createWithDefaults(false);
try {
classPathLoader.forName("com.cpljunit1.ClassPathLoaderJUnit1");
} catch (ClassNotFoundException cnfex) {
fail("JAR file not correctly added to Classpath");
}
try {
classPathLoader.forName("com.cpljunit2.ClassPathLoaderJUnit2");
} catch (ClassNotFoundException cnfex) {
fail("JAR file not correctly added to Classpath");
}
assertNotNull(classPathLoader.getResource("com/cpljunit2/ClassPathLoaderJUnit2.class"));
Enumeration<URL> urls = classPathLoader.getResources("com/cpljunit1");
if (!urls.hasMoreElements()) {
fail("Resources should return one element");
}
}
@Test
public void testAsClassLoaderLoadClassWithMultipleCustomLoaders() throws Exception {
System.out.println("\nStarting ClassPathLoaderJUnitTest#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 void writeJarBytesToFile(File jarFile, byte[] jarBytes) throws IOException {
final OutputStream outStream = new FileOutputStream(jarFile);
outStream.write(jarBytes);
outStream.close();
}
private static void exploreClassLoaders() {
System.out.println("Thread.currentThread().getContextClassLoader()...");
exploreClassLoader(Thread.currentThread().getContextClassLoader(), 1);
System.out.println("class.getClassLoader()...");
exploreClassLoader(ClassPathLoaderJUnitTest.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 uses BCEL to always dynamically generate a class for any class name it tries to load.
*/
private 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 ClassPathLoaderJUnitTest.this.tempFile;
}
}
/**
* Custom class loader which uses BCEL to always dynamically generate a class for any class name it tries to load.
*/
private class GeneratingClassLoader2 extends GeneratingClassLoader {
@Override
protected File getTempFile() {
return ClassPathLoaderJUnitTest.this.tempFile2;
}
}
/**
* Custom class loader which will never find any class or resource.
*/
private 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.
*/
private class SimpleClassLoader extends ClassLoader {
public SimpleClassLoader(ClassLoader parent) {
super(parent);
}
}
private 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);
}
}
/**
* Custom class loader which is broken and always throws errors.
*/
private 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();
}
}
@SuppressWarnings("serial")
private class BrokenError extends Error {
}
}