View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.jci;
19  
20  import junit.framework.TestCase;
21  import org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  
24  import org.apache.commons.jci.classes.SimpleDump;
25  import org.apache.commons.jci.stores.ResourceStore;
26  import org.apache.commons.jci.stores.MemoryResourceStore;
27  
28  /**
29   * Test ReloadingClassLoader's <code>removeResourceStore({@link ResourceStore})</code>
30   * method.
31   */
32  public class ReloadingClassLoaderRemoveTestCase extends TestCase {
33  
34      private final Log log = LogFactory.getLog(ReloadingClassLoaderRemoveTestCase.class);
35      
36      private final byte[] clazzSimpleA;
37      private MemoryResourceStore store1 = new MemoryResourceStore();
38      private MemoryResourceStore store2 = new MemoryResourceStore();
39      private MemoryResourceStore store3 = new MemoryResourceStore();
40      private MemoryResourceStore store4 = new MemoryResourceStore();
41  
42      public ReloadingClassLoaderRemoveTestCase() throws Exception {
43          clazzSimpleA = SimpleDump.dump("SimpleA");
44          assertTrue(clazzSimpleA.length > 0);
45      }
46  
47      @Override
48      protected void setUp() throws Exception {
49          System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.SimpleLog");
50      }
51      
52      @Override
53      protected void tearDown() throws Exception {
54      }
55  
56      /**
57       * Test trying to remove a ResourceStore from the ReloadingClassLoader
58       * which can't be found - when the ClassLoader contains NO other ResourceStore.
59       *
60       * Bug: The While loop in the removeResourceStore() throws an ArrayOutOfBoundsException
61       */
62      public void testRemoveStoreNotFoundClassLoaderNoStores() {
63          ReloadingClassLoader loader = new ReloadingClassLoader(getClass().getClassLoader());
64          checkRemoveResourceStore("No ResourceStore", loader, store1, false);
65      }
66  
67      /**
68       * Test trying to remove a ResourceStore from the ReloadingClassLoader
69       * which can't be found - when the ClassLoader DOES contain other ResourceStore.
70       *
71       * Bug: The While loop in the removeResourceStore() throws an ArrayOutOfBoundsException
72       */
73      public void testRemoveStoreNotFoundClassLoaderHasStores() {
74          ReloadingClassLoader loader = new ReloadingClassLoader(getClass().getClassLoader());
75          loader.addResourceStore(store1);
76          loader.addResourceStore(store2);
77          checkRemoveResourceStore("Has ResourceStore", loader, store3, false);
78      }
79  
80      /**
81       * Test trying to remove the first ResourceStore added
82       *
83       * Bug: ReloadingClassLoader addes ResourceStore at the start of the array. Removing the
84       *      first one added (last in array) causes the second System.arraycopy() statement to throw a
85       *      ArrayIndexOutOfBoundsException because the destination array position in the new smaller
86       *      array is too large.
87       */
88      public void testRemoveStoresOne() {
89          ReloadingClassLoader loader = new ReloadingClassLoader(getClass().getClassLoader());
90          loader.addResourceStore(store1);
91          loader.addResourceStore(store2);
92          loader.addResourceStore(store3);
93          loader.addResourceStore(store4);
94  
95          checkRemoveResourceStore("One: Remove Store 1", loader, store1, true);
96          checkRemoveResourceStore("One: Store 1 Not Found", loader, store1, false);
97  
98          checkRemoveResourceStore("One: Remove Store 2", loader, store2, true);
99          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 }