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 }