/*
* 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.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
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.After;
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.junit.rules.TemporaryFolder;
import org.apache.geode.internal.ClassPathLoaderTest.BrokenClassLoader;
import org.apache.geode.internal.ClassPathLoaderTest.NullClassLoader;
import org.apache.geode.internal.ClassPathLoaderTest.SimpleClassLoader;
import org.apache.geode.test.junit.categories.IntegrationTest;
/**
* Integration tests for {@link ClassPathLoader}.
*
* Extracted from ClassPathLoaderTest.
*/
@Category(IntegrationTest.class)
public class ClassPathLoaderIntegrationTest {
private static final int TEMP_FILE_BYTES_COUNT = 256;
private volatile File tempFile;
private volatile File tempFile2;
@Rule
public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties();
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
@Before
public void setUp() throws Exception {
System.setProperty(ClassPathLoader.EXCLUDE_TCCL_PROPERTY, "false");
System.setProperty(ClassPathLoader.EXT_LIB_DIR_PARENT_PROPERTY,
this.temporaryFolder.getRoot().getAbsolutePath());
this.tempFile = this.temporaryFolder.newFile("tempFile1.tmp");
FileOutputStream fos = new FileOutputStream(this.tempFile);
fos.write(new byte[TEMP_FILE_BYTES_COUNT]);
fos.close();
this.tempFile2 = this.temporaryFolder.newFile("tempFile2.tmp");
fos = new FileOutputStream(this.tempFile2);
fos.write(new byte[TEMP_FILE_BYTES_COUNT]);
fos.close();
}
/**
* Verifies that <tt>getResource</tt> works with custom loader from {@link ClassPathLoader}.
*/
@Test
public void testGetResourceWithCustomLoader() throws Exception {
System.out.println("\nStarting ClassPathLoaderTest#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}.
*/
@Test
public void testGetResourcesWithCustomLoader() throws Exception {
System.out.println("\nStarting ClassPathLoaderTest#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}.
*/
@Test
public void testGetResourceAsStreamWithCustomLoader() throws Exception {
System.out.println("\nStarting ClassPathLoaderTest#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}.
*/
@Test
public void testGetResourceWithTCCL() throws Exception {
System.out.println("\nStarting ClassPathLoaderTest#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}.
*/
@Test
public void testGetResourcesWithTCCL() throws Exception {
System.out.println("\nStarting ClassPathLoaderTest#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}.
*/
@Test
public void testGetResourceAsStreamWithTCCL() throws Exception {
System.out.println("\nStarting ClassPathLoaderTest#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 JAR files found in the extlib directory will be correctly added to the
* {@link ClassPathLoader}.
*/
@Test
public void testJarsInExtLib() throws Exception {
System.out.println("\nStarting ClassPathLoaderTest#testJarsInExtLib");
File EXT_LIB_DIR = ClassPathLoader.defineEXT_LIB_DIR();
EXT_LIB_DIR.mkdir();
File subdir = new File(EXT_LIB_DIR, "cplju");
subdir.mkdir();
final ClassBuilder classBuilder = new ClassBuilder();
writeJarBytesToFile(new File(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");
}
}
/**
* 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.
*/
@Test
public void testGetResourceAsStreamWithMultipleCustomLoaders() throws Exception {
System.out
.println("\nStarting ClassPathLoaderTest#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 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.
*/
@Test
public void testGetResourceWithMultipleCustomLoaders() throws Exception {
System.out.println("\nStarting ClassPathLoaderTest#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.
*/
@Test
public void testGetResourcesWithMultipleCustomLoaders() throws Exception {
System.out.println("\nStarting ClassPathLoaderTest#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);
}
}
private void writeJarBytesToFile(File jarFile, byte[] jarBytes) throws IOException {
final OutputStream outStream = new FileOutputStream(jarFile);
outStream.write(jarBytes);
outStream.close();
}
/**
* 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 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 tempFile2;
}
}
}