View Javadoc
1   package org.codehaus.plexus.classworlds.realm;
2   
3   /*
4    * Copyright 2001-2006 Codehaus Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  import java.net.URL;
20  import java.net.URLClassLoader;
21  import java.util.Collections;
22  import java.util.concurrent.CountDownLatch;
23  
24  import org.codehaus.classworlds.ClassRealmAdapter;
25  import org.codehaus.plexus.classworlds.AbstractClassWorldsTestCase;
26  import org.codehaus.plexus.classworlds.ClassWorld;
27  
28  public class DefaultClassRealmTest
29      extends AbstractClassWorldsTestCase
30  {
31      public DefaultClassRealmTest( String name )
32      {
33          super( name );
34      }
35  
36      // ----------------------------------------------------------------------
37      // Class testing
38      // ----------------------------------------------------------------------
39  
40      public void testLoadClassFromRealm()
41          throws Exception
42      {
43          ClassRealm mainRealm = new ClassRealm( new ClassWorld(), "main", null );
44  
45          mainRealm.addURL( getJarUrl( "component0-1.0.jar" ) );
46  
47          loadClass( mainRealm, "org.codehaus.plexus.Component0" );
48      }
49  
50      public void testLoadClassFromChildRealmWhereClassIsLocatedInParentRealm()
51          throws Exception
52      {
53          ClassRealm mainRealm = new ClassRealm( new ClassWorld(), "main", null );
54  
55          mainRealm.addURL( getJarUrl( "component0-1.0.jar" ) );
56  
57          ClassRealm childRealm = mainRealm.createChildRealm( "child" );
58  
59          loadClass( childRealm, "org.codehaus.plexus.Component0" );
60      }
61  
62      public void testLoadClassFromChildRealmWhereClassIsLocatedInGrantParentRealm()
63          throws Exception
64      {
65          ClassRealm mainRealm = new ClassRealm( new ClassWorld(), "main", null );
66  
67          mainRealm.addURL( getJarUrl( "component0-1.0.jar" ) );
68  
69          ClassRealm childRealm = mainRealm.createChildRealm( "child" );
70  
71          ClassRealm grandchildRealm = childRealm.createChildRealm( "grandchild" );
72  
73          loadClass( grandchildRealm, "org.codehaus.plexus.Component0" );
74      }
75  
76      public void testLoadClassFromChildRealmWhereClassIsLocatedInBothChildRealmAndParentRealm()
77          throws Exception
78      {
79          ClassRealm mainRealm = new ClassRealm( new ClassWorld(), "parent", null );
80  
81          mainRealm.addURL( getJarUrl( "component5-1.0.jar" ) );
82  
83          ClassRealm childRealm = mainRealm.createChildRealm( "child" );
84  
85          childRealm.addURL( getJarUrl( "component5-2.0.jar" ) );
86  
87          Class<?> cls = loadClass( childRealm, "test.Component5" );
88  
89          assertSame( childRealm, cls.getClassLoader() );
90          assertEquals( 1, cls.getMethods().length );
91          assertEquals( "printNew", cls.getMethods()[0].getName() );
92      }
93  
94      public void testLoadNonExistentClass()
95          throws Exception
96      {
97          ClassRealm mainRealm = new ClassRealm( new ClassWorld(), "main", null );
98  
99          mainRealm.addURL( getJarUrl( "component0-1.0.jar" ) );
100 
101         try
102         {
103             mainRealm.loadClass( "org.foo.bar.NonExistentClass" );
104 
105             fail( "A ClassNotFoundException should have been thrown!" );
106         }
107         catch ( ClassNotFoundException e )
108         {
109             // expected
110         }
111     }
112 
113     public void testImport()
114         throws Exception
115     {
116         ClassWorld world = new ClassWorld();
117 
118         ClassRealm r0 = world.newRealm( "r0" );
119 
120         ClassRealm r1 = world.newRealm( "r1" );
121 
122         r0.addURL( getJarUrl( "component0-1.0.jar" ) );
123 
124         r1.importFrom( "r0", "org.codehaus.plexus" );
125 
126         loadClass( r1, "org.codehaus.plexus.Component0" );
127     }
128 
129     public void testParentImport()
130         throws Exception
131     {
132         ClassWorld world = new ClassWorld();
133 
134         ClassRealm parent = world.newRealm( "parent" );
135 
136         ClassRealm child = world.newRealm( "child" );
137 
138         parent.addURL( getJarUrl( "component0-1.0.jar" ) );
139 
140         child.setParentRealm( parent );
141 
142         Class<?> type = loadClass( child, "org.codehaus.plexus.Component0" );
143 
144         child.importFromParent( "non-existing" );
145 
146         assertSame( null, loadClassOrNull( child, "org.codehaus.plexus.Component0" ) );
147 
148         child.importFromParent( "org.codehaus.plexus" );
149 
150         assertSame( type, loadClass( child, "org.codehaus.plexus.Component0" ) );
151     }
152 
153     public void testLoadClassFromBaseClassLoaderBeforeSelf()
154         throws Exception
155     {
156         ClassWorld world = new ClassWorld();
157 
158         ClassRealm base = world.newRealm( "base" );
159 
160         base.addURL( getJarUrl( "a.jar" ) );
161 
162         ClassRealm child = world.newRealm( "child", base );
163 
164         child.addURL( getJarUrl( "a.jar" ) );
165 
166         Class<?> baseClass = loadClass( base, "a.A" );
167         Class<?> childClass = loadClass( child, "a.A" );
168 
169         assertSame( base, baseClass.getClassLoader() );
170         assertSame( base, childClass.getClassLoader() );
171         assertSame( baseClass, childClass );
172     }
173 
174     public void testLoadClassFromRealmWithCircularClassReferences()
175         throws Exception
176     {
177         ClassRealm mainRealm = new ClassRealm( new ClassWorld(), "main", null );
178 
179         mainRealm.addURL( getJarUrl( "circular-0.1.jar" ) );
180 
181         /*
182          * This was reported to fail with a ClassCircularityError in IBM JDK 1.5.0-SR2, 1.5.0-SR7 and 1.6.0-SR2. It
183          * works in IBM JDK 1.5.0-SR10 and 1.6.0-SR6.
184          */
185         loadClass( mainRealm, "A$C" );
186     }
187 
188     // ----------------------------------------------------------------------
189     // Resource testing
190     // ----------------------------------------------------------------------
191 
192     public void testResource()
193         throws Exception
194     {
195         ClassRealm mainRealm = new ClassRealm( new ClassWorld(), "main", null );
196 
197         mainRealm.addURL( getJarUrl( "component0-1.0.jar" ) );
198 
199         getResource( mainRealm, "META-INF/plexus/components.xml" );
200     }
201 
202     public void testMalformedResource()
203         throws Exception
204     {
205         URL jarUrl = getJarUrl( "component0-1.0.jar" );
206 
207         ClassRealm mainRealm = new ClassRealm( new ClassWorld(), "main", null );
208 
209         mainRealm.addURL( jarUrl );
210 
211         ClassLoader officialClassLoader = new URLClassLoader( new URL[] { jarUrl } );
212 
213         String resource = "META-INF/plexus/components.xml";
214 
215         assertNotNull( mainRealm.getResource( resource ) );
216         assertNotNull( officialClassLoader.getResource( resource ) );
217 
218         /*
219          * NOTE: Resource names with a leading slash are invalid when passed to a class loader and must not be found!
220          * One can use a leading slash in Class.getResource() but not in ClassLoader.getResource().
221          */
222 
223         assertSame( null, mainRealm.getResource( "/" + resource ) );
224         assertSame( null, officialClassLoader.getResource( "/" + resource ) );
225 
226         /*
227          * For backward-compat, legacy class realms have to support leading slashes.
228          */
229 
230         org.codehaus.classworlds.ClassRealm legacyRealm = ClassRealmAdapter.getInstance( mainRealm );
231         assertNotNull( legacyRealm.getResource( "/" + resource ) );
232         assertNotNull( legacyRealm.getResourceAsStream( "/" + resource ) );
233         assertTrue( legacyRealm.findResources( "/" + resource ).hasMoreElements() );
234     }
235 
236     public void testFindResourceOnlyScansSelf()
237         throws Exception
238     {
239         ClassRealm mainRealm = new ClassRealm( new ClassWorld(), "main", null );
240 
241         mainRealm.addURL( getJarUrl( "a.jar" ) );
242 
243         ClassRealm childRealm = mainRealm.createChildRealm( "child" );
244 
245         childRealm.addURL( getJarUrl( "b.jar" ) );
246 
247         assertNotNull( childRealm.getResource( "a.properties" ) );
248         assertNotNull( childRealm.getResource( "b.properties" ) );
249 
250         assertNull( childRealm.findResource( "a.properties" ) );
251 
252         assertNotNull( childRealm.findResource( "b.properties" ) );
253     }
254 
255     public void testFindResourcesOnlyScansSelf()
256         throws Exception
257     {
258         ClassRealm mainRealm = new ClassRealm( new ClassWorld(), "main", null );
259 
260         mainRealm.addURL( getJarUrl( "a.jar" ) );
261 
262         ClassRealm childRealm = mainRealm.createChildRealm( "child" );
263 
264         childRealm.addURL( getJarUrl( "b.jar" ) );
265 
266         assertTrue( childRealm.getResources( "a.properties" ).hasMoreElements() );
267         assertTrue( childRealm.getResources( "b.properties" ).hasMoreElements() );
268 
269         assertFalse( childRealm.findResources( "a.properties" ).hasMoreElements() );
270 
271         assertTrue( childRealm.findResources( "b.properties" ).hasMoreElements() );
272     }
273 
274     /** Should never deadlock. Ever */
275     public void testParallelDeadlockClassRealm()
276         throws InterruptedException
277     {
278         for (int i = 0; i < 100; i++){
279            doOneDeadlockAttempt();
280         }
281 
282     }
283 
284     private void doOneDeadlockAttempt()
285         throws InterruptedException
286     {
287         // Deadlock sample graciously ripped from http://docs.oracle.com/javase/7/docs/technotes/guides/lang/cl-mt.html
288         final ClassRealm cl1 = new ClassRealm( new ClassWorld(), "cl1", null );
289         final ClassRealm cl2 = new ClassRealm( new ClassWorld(), "cl2", cl1 );
290         cl1.setParentRealm( cl2 );
291         cl1.addURL( getJarUrl( "deadlock.jar" ) );
292         cl2.addURL( getJarUrl( "deadlock.jar" ) );
293         final CountDownLatch latch = new CountDownLatch( 1 );
294 
295         Runnable r1 = new Runnable()
296         {
297             public void run()
298             {
299                 try
300                 {
301                     latch.await();
302                     cl1.loadClass( "deadlock.A" );
303                 }
304                 catch ( ClassNotFoundException e )
305                 {
306                     throw new RuntimeException( e );
307                 }
308                 catch ( InterruptedException e )
309                 {
310                     throw new RuntimeException( e );
311                 }
312             }
313         };
314 
315         Runnable r2 = new Runnable()
316         {
317             public void run()
318             {
319                 try
320                 {
321                     latch.await();
322                     cl1.loadClass( "deadlock.C" );
323                 }
324                 catch ( ClassNotFoundException e )
325                 {
326                     throw new RuntimeException( e );
327                 }
328                 catch ( InterruptedException e )
329                 {
330                     throw new RuntimeException( e );
331                 }
332             }
333         };
334 
335         Thread thread = new Thread( r1 );
336         thread.start();
337         Thread thread1 = new Thread( r2 );
338         thread1.start();
339         latch.countDown();
340         thread.join();
341         thread1.join();
342     }
343 
344     // ----------------------------------------------------------------------
345     //
346     // ----------------------------------------------------------------------
347 
348     private Class<?> loadClassOrNull( ClassRealm realm, String name )
349     {
350         try
351         {
352             return loadClass( realm, name );
353         }
354         catch ( ClassNotFoundException e )
355         {
356             return null;
357         }
358     }
359 
360     private Class<?> loadClass( ClassRealm realm, String name )
361         throws ClassNotFoundException
362     {
363         Class<?> cls = realm.loadClass( name );
364 
365         /*
366          * NOTE: Load the class both directly from the realm and indirectly from an (ordinary) child class loader which
367          * uses the specified class realm for parent delegation. The child class loader itself has no additional class
368          * path entries but relies entirely on the provided class realm. Hence, the created child class loader should in
369          * theory be able to load exactly the same classes/resources as the underlying class realm. In practice, it will
370          * test that class realms properly integrate into the standard Java class loader hierarchy.
371          */
372         ClassLoader childLoader = new URLClassLoader( new URL[0], realm );
373         assertEquals( cls, childLoader.loadClass( name ) );
374 
375         return cls;
376     }
377 
378     private void getResource( ClassRealm realm, String name )
379         throws Exception
380     {
381         ClassLoader childLoader = new URLClassLoader( new URL[0], realm );
382         assertNotNull( realm.getResource( name ) );
383         assertEquals( realm.getResource( name ), childLoader.getResource( name ) );
384         assertEquals( Collections.list( realm.getResources( name ) ),
385                       Collections.list( childLoader.getResources( name ) ) );
386     }
387 
388 }