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
035: .getLog(ReloadingClassLoaderRemoveTestCase.class);
036:
037: private final byte[] clazzSimpleA;
038: private MemoryResourceStore store1 = new MemoryResourceStore();
039: private MemoryResourceStore store2 = new MemoryResourceStore();
040: private MemoryResourceStore store3 = new MemoryResourceStore();
041: private MemoryResourceStore store4 = new MemoryResourceStore();
042:
043: public ReloadingClassLoaderRemoveTestCase() throws Exception {
044: clazzSimpleA = SimpleDump.dump("SimpleA");
045: assertTrue(clazzSimpleA.length > 0);
046: }
047:
048: protected void setUp() throws Exception {
049: System.setProperty("org.apache.commons.logging.Log",
050: "org.apache.commons.logging.impl.SimpleLog");
051: }
052:
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(
064: getClass().getClassLoader());
065: checkRemoveResourceStore("No ResourceStore", loader, store1,
066: false);
067: }
068:
069: /**
070: * Test trying to remove a ResourceStore from the ReloadingClassLoader
071: * which can't be found - when the ClassLoader DOES contain other ResourceStore.
072: *
073: * Bug: The While loop in the removeResourceStore() throws an ArrayOutOfBoundsException
074: */
075: public void testRemoveStoreNotFoundClassLoaderHasStores() {
076: ReloadingClassLoader loader = new ReloadingClassLoader(
077: getClass().getClassLoader());
078: loader.addResourceStore(store1);
079: loader.addResourceStore(store2);
080: checkRemoveResourceStore("Has ResourceStore", loader, store3,
081: false);
082: }
083:
084: /**
085: * Test trying to remove the first ResourceStore added
086: *
087: * Bug: ReloadingClassLoader addes ResourceStore at the start of the array. Removing the
088: * first one added (last in array) causes the second System.arraycopy() statement to throw a
089: * ArrayIndexOutOfBoundsException because the destination array position in the new smaller
090: * array is too large.
091: */
092: public void testRemoveStoresOne() {
093: ReloadingClassLoader loader = new ReloadingClassLoader(
094: getClass().getClassLoader());
095: loader.addResourceStore(store1);
096: loader.addResourceStore(store2);
097: loader.addResourceStore(store3);
098: loader.addResourceStore(store4);
099:
100: checkRemoveResourceStore("One: Remove Store 1", loader, store1,
101: true);
102: checkRemoveResourceStore("One: Store 1 Not Found", loader,
103: store1, false);
104:
105: checkRemoveResourceStore("One: Remove Store 2", loader, store2,
106: true);
107: checkRemoveResourceStore("One: Store 2 Not Found", loader,
108: store2, false);
109:
110: checkRemoveResourceStore("One: Remove Store 3", loader, store3,
111: true);
112: checkRemoveResourceStore("One: Store 3 Not Found", loader,
113: store3, false);
114:
115: checkRemoveResourceStore("One: Remove Store 4", loader, store4,
116: true);
117: checkRemoveResourceStore("One: Store 4 Not Found", loader,
118: store4, false);
119: }
120:
121: /**
122: * Test trying to remove the second ResourceStore added
123: *
124: * Bug: ReloadingClassLoader addes ResourceStore at the start of the array. Removing the
125: * first one added (last in array) causes the second System.arraycopy() statement to throw a
126: * ArrayIndexOutOfBoundsException (??not sure why??)
127: */
128: public void testRemoveStoresTwo() {
129: ReloadingClassLoader loader = new ReloadingClassLoader(
130: getClass().getClassLoader());
131: loader.addResourceStore(store1);
132: loader.addResourceStore(store2);
133: loader.addResourceStore(store3);
134: loader.addResourceStore(store4);
135:
136: checkRemoveResourceStore("Two: Remove Store 2", loader, store2,
137: true);
138: checkRemoveResourceStore("Two: Store 2 Not Found", loader,
139: store2, false);
140:
141: checkRemoveResourceStore("Two: Remove Store 4", loader, store4,
142: true);
143: checkRemoveResourceStore("Two: Store 4 Not Found", loader,
144: store4, false);
145:
146: checkRemoveResourceStore("Two: Remove Store 3", loader, store3,
147: true);
148: checkRemoveResourceStore("Two: Store 3 Not Found", loader,
149: store3, false);
150:
151: checkRemoveResourceStore("Two: Remove Store 1", loader, store1,
152: true);
153: checkRemoveResourceStore("Two: Store 1 Not Found", loader,
154: store1, false);
155: }
156:
157: /**
158: * Test trying to remove the third ResourceStore added
159: *
160: * Bug: In this scenario the two System.arraycopy() statements don't copy the correct
161: * ResourceStore - it creates a new array where the first resource store is null
162: * and copies store3 and store2 to their same positions
163: */
164: public void testRemoveStoresThree() {
165: ReloadingClassLoader loader = new ReloadingClassLoader(
166: getClass().getClassLoader());
167: loader.addResourceStore(store1);
168: loader.addResourceStore(store2);
169: loader.addResourceStore(store3);
170: loader.addResourceStore(store4);
171:
172: checkRemoveResourceStore("Three: Remove Store 3", loader,
173: store3, true);
174: checkRemoveResourceStore("Three: Store 3 Not Found", loader,
175: store3, false);
176:
177: checkRemoveResourceStore("Three: Remove Store 1", loader,
178: store1, true);
179: checkRemoveResourceStore("Three: Store 1 Not Found", loader,
180: store1, false);
181:
182: checkRemoveResourceStore("Three: Remove Store 4", loader,
183: store4, true);
184: checkRemoveResourceStore("Three: Store 4 Not Found", loader,
185: store4, false);
186:
187: checkRemoveResourceStore("Three: Remove Store 2", loader,
188: store2, true);
189: checkRemoveResourceStore("Three: Store 2 Not Found", loader,
190: store2, false);
191: }
192:
193: /**
194: * Test trying to remove the fourth ResourceStore added
195: *
196: * Bug: ReloadingClassLoader addes ResourceStore at the start of the array. Removing the
197: * last one added (first in array) causes the first System.arraycopy() statement to throw a
198: * ArrayIndexOutOfBoundsException because the length to copy is -1
199: */
200: public void testRemoveStoresFour() {
201: ReloadingClassLoader loader = new ReloadingClassLoader(
202: getClass().getClassLoader());
203: loader.addResourceStore(store1);
204: loader.addResourceStore(store2);
205: loader.addResourceStore(store3);
206: loader.addResourceStore(store4);
207:
208: checkRemoveResourceStore("Four: Remove Store 4", loader,
209: store4, true);
210: checkRemoveResourceStore("Four: Store 4 Not Found", loader,
211: store4, false);
212:
213: checkRemoveResourceStore("Four: Remove Store 3", loader,
214: store3, true);
215: checkRemoveResourceStore("Four: Store 3 Not Found", loader,
216: store3, false);
217:
218: checkRemoveResourceStore("Four: Remove Store 2", loader,
219: store2, true);
220: checkRemoveResourceStore("Four: Store 2 Not Found", loader,
221: store2, false);
222:
223: checkRemoveResourceStore("Four: Remove Store 1", loader,
224: store1, true);
225: checkRemoveResourceStore("Four: Store 1 Not Found", loader,
226: store1, false);
227: }
228:
229: /**
230: * Test that a class can't be loaded after the ResourceStore containing
231: * it has been removed.
232: *
233: * Bug: When theres a single ResourceStore in the ClassLoader and its removed
234: * a new "delegate" ClassLoader with the new ResourceStore array isn't being
235: * created - which means that calling loadClass() still returns the classes
236: * from the removed ResourceStore rather than throwing a ClassNotFoundException
237: */
238: public void testLoadClassAfterResourceStoreRemoved() {
239:
240: // Create a class loader & add resource store
241: ReloadingClassLoader loader = new ReloadingClassLoader(this
242: .getClass().getClassLoader());
243: MemoryResourceStore store = new MemoryResourceStore();
244: loader.addResourceStore(store);
245:
246: // Check "jci.Simple" class can't be loaded
247: try {
248: Object simple1 = loader.loadClass("jci.Simple")
249: .newInstance();
250: fail("Success loadClass[1]");
251: } catch (ClassNotFoundException e) {
252: // expected not found
253: } catch (Exception e) {
254: log.error(e);
255: fail("Error loadClass[1]: " + e);
256: }
257:
258: // Add "jci.Simple" class to the resource store
259: String toStringValue = "FooBar";
260: try {
261: byte[] classBytes = SimpleDump.dump(toStringValue);
262: store.write("jci/Simple.class", classBytes);
263: } catch (Exception e) {
264: log.error(e);
265: fail("Error adding class to store: " + e);
266: }
267:
268: // Check "jci.Simple" class can now be loaded
269: try {
270: Object simple2 = loader.loadClass("jci.Simple")
271: .newInstance();
272: assertNotNull("Found loadClass[2]", simple2);
273: assertEquals("toString loadClass[2]", toStringValue,
274: simple2.toString());
275: } catch (Exception e) {
276: log.error(e);
277: fail("Error loadClass[2]: " + e);
278: }
279:
280: // Remove the resource store from the class loader
281: checkRemoveResourceStore("Remove Resource Store", loader,
282: store, true);
283:
284: // Test "jci.Simple" class can't be loaded after ResourceStore removed
285: try {
286: Object simple3 = loader.loadClass("jci.Simple")
287: .newInstance();
288: fail("Success loadClass[3]");
289: } catch (ClassNotFoundException e) {
290: // expected not found
291: } catch (Exception e) {
292: log.error(e);
293: fail("Error loadClass[3]: " + e);
294: }
295:
296: }
297:
298: /**
299: * Check removing a ResourceStore from ReloadingClassLoader
300: */
301: private void checkRemoveResourceStore(String label,
302: ReloadingClassLoader loader, ResourceStore store,
303: boolean expected) {
304: try {
305: assertEquals(label, expected, loader
306: .removeResourceStore(store));
307: } catch (Exception e) {
308: log.error(label, e);
309: fail(label + " failed: " + e);
310: }
311: }
312: }
|