001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018 package org.apache.commons.jci; 019 020 import junit.framework.TestCase; 021 import org.apache.commons.logging.Log; 022 import org.apache.commons.logging.LogFactory; 023 024 import org.apache.commons.jci.classes.SimpleDump; 025 import org.apache.commons.jci.stores.ResourceStore; 026 import org.apache.commons.jci.stores.MemoryResourceStore; 027 028 /** 029 * Test ReloadingClassLoader's <code>removeResourceStore({@link ResourceStore})</code> 030 * method. 031 */ 032 public class ReloadingClassLoaderRemoveTestCase extends TestCase { 033 034 private final Log log = LogFactory.getLog(ReloadingClassLoaderRemoveTestCase.class); 035 036 private final byte[] clazzSimpleA; 037 private MemoryResourceStore store1 = new MemoryResourceStore(); 038 private MemoryResourceStore store2 = new MemoryResourceStore(); 039 private MemoryResourceStore store3 = new MemoryResourceStore(); 040 private MemoryResourceStore store4 = new MemoryResourceStore(); 041 042 public ReloadingClassLoaderRemoveTestCase() throws Exception { 043 clazzSimpleA = SimpleDump.dump("SimpleA"); 044 assertTrue(clazzSimpleA.length > 0); 045 } 046 047 @Override 048 protected void setUp() throws Exception { 049 System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.SimpleLog"); 050 } 051 052 @Override 053 protected void tearDown() throws Exception { 054 } 055 056 /** 057 * Test trying to remove a ResourceStore from the ReloadingClassLoader 058 * which can't be found - when the ClassLoader contains NO other ResourceStore. 059 * 060 * Bug: The While loop in the removeResourceStore() throws an ArrayOutOfBoundsException 061 */ 062 public void testRemoveStoreNotFoundClassLoaderNoStores() { 063 ReloadingClassLoader loader = new ReloadingClassLoader(getClass().getClassLoader()); 064 checkRemoveResourceStore("No ResourceStore", loader, store1, false); 065 } 066 067 /** 068 * Test trying to remove a ResourceStore from the ReloadingClassLoader 069 * which can't be found - when the ClassLoader DOES contain other ResourceStore. 070 * 071 * Bug: The While loop in the removeResourceStore() throws an ArrayOutOfBoundsException 072 */ 073 public void testRemoveStoreNotFoundClassLoaderHasStores() { 074 ReloadingClassLoader loader = new ReloadingClassLoader(getClass().getClassLoader()); 075 loader.addResourceStore(store1); 076 loader.addResourceStore(store2); 077 checkRemoveResourceStore("Has ResourceStore", loader, store3, false); 078 } 079 080 /** 081 * Test trying to remove the first ResourceStore added 082 * 083 * Bug: ReloadingClassLoader addes ResourceStore at the start of the array. Removing the 084 * first one added (last in array) causes the second System.arraycopy() statement to throw a 085 * ArrayIndexOutOfBoundsException because the destination array position in the new smaller 086 * array is too large. 087 */ 088 public void testRemoveStoresOne() { 089 ReloadingClassLoader loader = new ReloadingClassLoader(getClass().getClassLoader()); 090 loader.addResourceStore(store1); 091 loader.addResourceStore(store2); 092 loader.addResourceStore(store3); 093 loader.addResourceStore(store4); 094 095 checkRemoveResourceStore("One: Remove Store 1", loader, store1, true); 096 checkRemoveResourceStore("One: Store 1 Not Found", loader, store1, false); 097 098 checkRemoveResourceStore("One: Remove Store 2", loader, store2, true); 099 checkRemoveResourceStore("One: Store 2 Not Found", loader, store2, false); 100 101 checkRemoveResourceStore("One: Remove Store 3", loader, store3, true); 102 checkRemoveResourceStore("One: Store 3 Not Found", loader, store3, false); 103 104 checkRemoveResourceStore("One: Remove Store 4", loader, store4, true); 105 checkRemoveResourceStore("One: Store 4 Not Found", loader, store4, false); 106 } 107 108 /** 109 * Test trying to remove the second ResourceStore added 110 * 111 * Bug: ReloadingClassLoader addes ResourceStore at the start of the array. Removing the 112 * first one added (last in array) causes the second System.arraycopy() statement to throw a 113 * ArrayIndexOutOfBoundsException (??not sure why??) 114 */ 115 public void testRemoveStoresTwo() { 116 ReloadingClassLoader loader = new ReloadingClassLoader(getClass().getClassLoader()); 117 loader.addResourceStore(store1); 118 loader.addResourceStore(store2); 119 loader.addResourceStore(store3); 120 loader.addResourceStore(store4); 121 122 checkRemoveResourceStore("Two: Remove Store 2", loader, store2, true); 123 checkRemoveResourceStore("Two: Store 2 Not Found", loader, store2, false); 124 125 checkRemoveResourceStore("Two: Remove Store 4", loader, store4, true); 126 checkRemoveResourceStore("Two: Store 4 Not Found", loader, store4, false); 127 128 checkRemoveResourceStore("Two: Remove Store 3", loader, store3, true); 129 checkRemoveResourceStore("Two: Store 3 Not Found", loader, store3, false); 130 131 checkRemoveResourceStore("Two: Remove Store 1", loader, store1, true); 132 checkRemoveResourceStore("Two: Store 1 Not Found", loader, store1, false); 133 } 134 135 /** 136 * Test trying to remove the third ResourceStore added 137 * 138 * Bug: In this scenario the two System.arraycopy() statements don't copy the correct 139 * ResourceStore - it creates a new array where the first resource store is null 140 * and copies store3 and store2 to their same positions 141 */ 142 public void testRemoveStoresThree() { 143 ReloadingClassLoader loader = new ReloadingClassLoader(getClass().getClassLoader()); 144 loader.addResourceStore(store1); 145 loader.addResourceStore(store2); 146 loader.addResourceStore(store3); 147 loader.addResourceStore(store4); 148 149 checkRemoveResourceStore("Three: Remove Store 3", loader, store3, true); 150 checkRemoveResourceStore("Three: Store 3 Not Found", loader, store3, false); 151 152 checkRemoveResourceStore("Three: Remove Store 1", loader, store1, true); 153 checkRemoveResourceStore("Three: Store 1 Not Found", loader, store1, false); 154 155 checkRemoveResourceStore("Three: Remove Store 4", loader, store4, true); 156 checkRemoveResourceStore("Three: Store 4 Not Found", loader, store4, false); 157 158 checkRemoveResourceStore("Three: Remove Store 2", loader, store2, true); 159 checkRemoveResourceStore("Three: Store 2 Not Found", loader, store2, false); 160 } 161 162 /** 163 * Test trying to remove the fourth ResourceStore added 164 * 165 * Bug: ReloadingClassLoader addes ResourceStore at the start of the array. Removing the 166 * last one added (first in array) causes the first System.arraycopy() statement to throw a 167 * ArrayIndexOutOfBoundsException because the length to copy is -1 168 */ 169 public void testRemoveStoresFour() { 170 ReloadingClassLoader loader = new ReloadingClassLoader(getClass().getClassLoader()); 171 loader.addResourceStore(store1); 172 loader.addResourceStore(store2); 173 loader.addResourceStore(store3); 174 loader.addResourceStore(store4); 175 176 checkRemoveResourceStore("Four: Remove Store 4", loader, store4, true); 177 checkRemoveResourceStore("Four: Store 4 Not Found", loader, store4, false); 178 179 checkRemoveResourceStore("Four: Remove Store 3", loader, store3, true); 180 checkRemoveResourceStore("Four: Store 3 Not Found", loader, store3, false); 181 182 checkRemoveResourceStore("Four: Remove Store 2", loader, store2, true); 183 checkRemoveResourceStore("Four: Store 2 Not Found", loader, store2, false); 184 185 checkRemoveResourceStore("Four: Remove Store 1", loader, store1, true); 186 checkRemoveResourceStore("Four: Store 1 Not Found", loader, store1, false); 187 } 188 189 190 /** 191 * Test that a class can't be loaded after the ResourceStore containing 192 * it has been removed. 193 * 194 * Bug: When theres a single ResourceStore in the ClassLoader and its removed 195 * a new "delegate" ClassLoader with the new ResourceStore array isn't being 196 * created - which means that calling loadClass() still returns the classes 197 * from the removed ResourceStore rather than throwing a ClassNotFoundException 198 */ 199 public void testLoadClassAfterResourceStoreRemoved() { 200 201 // Create a class loader & add resource store 202 ReloadingClassLoader loader = new ReloadingClassLoader(this.getClass().getClassLoader()); 203 MemoryResourceStore store = new MemoryResourceStore(); 204 loader.addResourceStore(store); 205 206 // Check "jci.Simple" class can't be loaded 207 try { 208 loader.loadClass("jci.Simple").newInstance(); 209 fail("Success loadClass[1]"); 210 } catch(ClassNotFoundException e) { 211 // expected not found 212 } catch(Exception e) { 213 log.error(e); 214 fail("Error loadClass[1]: " + e); 215 } 216 217 // Add "jci.Simple" class to the resource store 218 String toStringValue = "FooBar"; 219 try { 220 byte[] classBytes = SimpleDump.dump(toStringValue); 221 store.write("jci/Simple.class", classBytes); 222 } catch(Exception e) { 223 log.error(e); 224 fail("Error adding class to store: " + e); 225 } 226 227 // Check "jci.Simple" class can now be loaded 228 try { 229 Object simple2 = loader.loadClass("jci.Simple").newInstance(); 230 assertNotNull("Found loadClass[2]", simple2); 231 assertEquals("toString loadClass[2]", toStringValue, simple2.toString()); 232 } catch(Exception e) { 233 log.error(e); 234 fail("Error loadClass[2]: " + e); 235 } 236 237 // Remove the resource store from the class loader 238 checkRemoveResourceStore("Remove Resource Store", loader, store, true); 239 240 // Test "jci.Simple" class can't be loaded after ResourceStore removed 241 try { 242 loader.loadClass("jci.Simple").newInstance(); 243 fail("Success loadClass[3]"); 244 } catch(ClassNotFoundException e) { 245 // expected not found 246 } catch(Exception e) { 247 log.error(e); 248 fail("Error loadClass[3]: " + e); 249 } 250 251 } 252 253 /** 254 * Check removing a ResourceStore from ReloadingClassLoader 255 */ 256 private void checkRemoveResourceStore(String label, ReloadingClassLoader loader, ResourceStore store, boolean expected) { 257 try { 258 assertEquals(label, expected, loader.removeResourceStore(store)); 259 } catch(Exception e) { 260 log.error(label, e); 261 fail(label + " failed: " + e); 262 } 263 } 264 }