blob: 4ce7eeb299ec895f3ececbdd5b20550b7f3bd150 [file] [log] [blame]
/*
* 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.commons.jci2;
import junit.framework.TestCase;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.jci2.classes.SimpleDump;
import org.apache.commons.jci2.stores.ResourceStore;
import org.apache.commons.jci2.stores.MemoryResourceStore;
/**
* Test ReloadingClassLoader's <code>removeResourceStore({@link ResourceStore})</code>
* method.
*/
public class ReloadingClassLoaderRemoveTestCase extends TestCase {
private final Log log = LogFactory.getLog(ReloadingClassLoaderRemoveTestCase.class);
private final byte[] clazzSimpleA;
private final MemoryResourceStore store1 = new MemoryResourceStore();
private final MemoryResourceStore store2 = new MemoryResourceStore();
private final MemoryResourceStore store3 = new MemoryResourceStore();
private final MemoryResourceStore store4 = new MemoryResourceStore();
public ReloadingClassLoaderRemoveTestCase() throws Exception {
clazzSimpleA = SimpleDump.dump("SimpleA");
assertTrue(clazzSimpleA.length > 0);
}
@Override
protected void setUp() throws Exception {
System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.SimpleLog");
}
@Override
protected void tearDown() throws Exception {
}
/**
* Test trying to remove a ResourceStore from the ReloadingClassLoader
* which can't be found - when the ClassLoader contains NO other ResourceStore.
*
* Bug: The While loop in the removeResourceStore() throws an ArrayOutOfBoundsException
*/
public void testRemoveStoreNotFoundClassLoaderNoStores() {
final ReloadingClassLoader loader = new ReloadingClassLoader(getClass().getClassLoader());
checkRemoveResourceStore("No ResourceStore", loader, store1, false);
}
/**
* Test trying to remove a ResourceStore from the ReloadingClassLoader
* which can't be found - when the ClassLoader DOES contain other ResourceStore.
*
* Bug: The While loop in the removeResourceStore() throws an ArrayOutOfBoundsException
*/
public void testRemoveStoreNotFoundClassLoaderHasStores() {
final ReloadingClassLoader loader = new ReloadingClassLoader(getClass().getClassLoader());
loader.addResourceStore(store1);
loader.addResourceStore(store2);
checkRemoveResourceStore("Has ResourceStore", loader, store3, false);
}
/**
* Test trying to remove the first ResourceStore added
*
* Bug: ReloadingClassLoader addes ResourceStore at the start of the array. Removing the
* first one added (last in array) causes the second System.arraycopy() statement to throw a
* ArrayIndexOutOfBoundsException because the destination array position in the new smaller
* array is too large.
*/
public void testRemoveStoresOne() {
final ReloadingClassLoader loader = new ReloadingClassLoader(getClass().getClassLoader());
loader.addResourceStore(store1);
loader.addResourceStore(store2);
loader.addResourceStore(store3);
loader.addResourceStore(store4);
checkRemoveResourceStore("One: Remove Store 1", loader, store1, true);
checkRemoveResourceStore("One: Store 1 Not Found", loader, store1, false);
checkRemoveResourceStore("One: Remove Store 2", loader, store2, true);
checkRemoveResourceStore("One: Store 2 Not Found", loader, store2, false);
checkRemoveResourceStore("One: Remove Store 3", loader, store3, true);
checkRemoveResourceStore("One: Store 3 Not Found", loader, store3, false);
checkRemoveResourceStore("One: Remove Store 4", loader, store4, true);
checkRemoveResourceStore("One: Store 4 Not Found", loader, store4, false);
}
/**
* Test trying to remove the second ResourceStore added
*
* Bug: ReloadingClassLoader addes ResourceStore at the start of the array. Removing the
* first one added (last in array) causes the second System.arraycopy() statement to throw a
* ArrayIndexOutOfBoundsException (??not sure why??)
*/
public void testRemoveStoresTwo() {
final ReloadingClassLoader loader = new ReloadingClassLoader(getClass().getClassLoader());
loader.addResourceStore(store1);
loader.addResourceStore(store2);
loader.addResourceStore(store3);
loader.addResourceStore(store4);
checkRemoveResourceStore("Two: Remove Store 2", loader, store2, true);
checkRemoveResourceStore("Two: Store 2 Not Found", loader, store2, false);
checkRemoveResourceStore("Two: Remove Store 4", loader, store4, true);
checkRemoveResourceStore("Two: Store 4 Not Found", loader, store4, false);
checkRemoveResourceStore("Two: Remove Store 3", loader, store3, true);
checkRemoveResourceStore("Two: Store 3 Not Found", loader, store3, false);
checkRemoveResourceStore("Two: Remove Store 1", loader, store1, true);
checkRemoveResourceStore("Two: Store 1 Not Found", loader, store1, false);
}
/**
* Test trying to remove the third ResourceStore added
*
* Bug: In this scenario the two System.arraycopy() statements don't copy the correct
* ResourceStore - it creates a new array where the first resource store is null
* and copies store3 and store2 to their same positions
*/
public void testRemoveStoresThree() {
final ReloadingClassLoader loader = new ReloadingClassLoader(getClass().getClassLoader());
loader.addResourceStore(store1);
loader.addResourceStore(store2);
loader.addResourceStore(store3);
loader.addResourceStore(store4);
checkRemoveResourceStore("Three: Remove Store 3", loader, store3, true);
checkRemoveResourceStore("Three: Store 3 Not Found", loader, store3, false);
checkRemoveResourceStore("Three: Remove Store 1", loader, store1, true);
checkRemoveResourceStore("Three: Store 1 Not Found", loader, store1, false);
checkRemoveResourceStore("Three: Remove Store 4", loader, store4, true);
checkRemoveResourceStore("Three: Store 4 Not Found", loader, store4, false);
checkRemoveResourceStore("Three: Remove Store 2", loader, store2, true);
checkRemoveResourceStore("Three: Store 2 Not Found", loader, store2, false);
}
/**
* Test trying to remove the fourth ResourceStore added
*
* Bug: ReloadingClassLoader addes ResourceStore at the start of the array. Removing the
* last one added (first in array) causes the first System.arraycopy() statement to throw a
* ArrayIndexOutOfBoundsException because the length to copy is -1
*/
public void testRemoveStoresFour() {
final ReloadingClassLoader loader = new ReloadingClassLoader(getClass().getClassLoader());
loader.addResourceStore(store1);
loader.addResourceStore(store2);
loader.addResourceStore(store3);
loader.addResourceStore(store4);
checkRemoveResourceStore("Four: Remove Store 4", loader, store4, true);
checkRemoveResourceStore("Four: Store 4 Not Found", loader, store4, false);
checkRemoveResourceStore("Four: Remove Store 3", loader, store3, true);
checkRemoveResourceStore("Four: Store 3 Not Found", loader, store3, false);
checkRemoveResourceStore("Four: Remove Store 2", loader, store2, true);
checkRemoveResourceStore("Four: Store 2 Not Found", loader, store2, false);
checkRemoveResourceStore("Four: Remove Store 1", loader, store1, true);
checkRemoveResourceStore("Four: Store 1 Not Found", loader, store1, false);
}
/**
* Test that a class can't be loaded after the ResourceStore containing
* it has been removed.
*
* Bug: When theres a single ResourceStore in the ClassLoader and its removed
* a new "delegate" ClassLoader with the new ResourceStore array isn't being
* created - which means that calling loadClass() still returns the classes
* from the removed ResourceStore rather than throwing a ClassNotFoundException
*/
public void testLoadClassAfterResourceStoreRemoved() {
// Create a class loader & add resource store
final ReloadingClassLoader loader = new ReloadingClassLoader(this.getClass().getClassLoader());
final MemoryResourceStore store = new MemoryResourceStore();
loader.addResourceStore(store);
// Check "jci2.Simple" class can't be loaded
try {
loader.loadClass("jci2.Simple").newInstance();
fail("Success loadClass[1]");
} catch(final ClassNotFoundException e) {
// expected not found
} catch(final Exception e) {
log.error(e);
fail("Error loadClass[1]: " + e);
}
// Add "jci2.Simple" class to the resource store
final String toStringValue = "FooBar";
try {
final byte[] classBytes = SimpleDump.dump(toStringValue);
store.write("jci2/Simple.class", classBytes);
} catch(final Exception e) {
log.error(e);
fail("Error adding class to store: " + e);
}
// Check "jci2.Simple" class can now be loaded
try {
final Object simple2 = loader.loadClass("jci2.Simple").newInstance();
assertNotNull("Found loadClass[2]", simple2);
assertEquals("toString loadClass[2]", toStringValue, simple2.toString());
} catch(final Exception e) {
log.error(e);
fail("Error loadClass[2]: " + e);
}
// Remove the resource store from the class loader
checkRemoveResourceStore("Remove Resource Store", loader, store, true);
// Test "jci2.Simple" class can't be loaded after ResourceStore removed
try {
loader.loadClass("jci2.Simple").newInstance();
fail("Success loadClass[3]");
} catch(final ClassNotFoundException e) {
// expected not found
} catch(final Exception e) {
log.error(e);
fail("Error loadClass[3]: " + e);
}
}
/**
* Check removing a ResourceStore from ReloadingClassLoader
*/
private void checkRemoveResourceStore(final String label, final ReloadingClassLoader loader, final ResourceStore store, final boolean expected) {
try {
assertEquals(label, expected, loader.removeResourceStore(store));
} catch(final Exception e) {
log.error(label, e);
fail(label + " failed: " + e);
}
}
}