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 org.codehaus.plexus.classworlds.ClassWorld;
20  import org.codehaus.plexus.classworlds.strategy.Strategy;
21  import org.codehaus.plexus.classworlds.strategy.StrategyFactory;
22  
23  import java.io.Closeable;
24  import java.io.IOException;
25  import java.io.PrintStream;
26  import java.net.MalformedURLException;
27  import java.net.URL;
28  import java.net.URLClassLoader;
29  import java.util.Collection;
30  import java.util.Collections;
31  import java.util.Enumeration;
32  import java.util.HashSet;
33  import java.util.LinkedHashSet;
34  import java.util.SortedSet;
35  import java.util.TreeSet;
36  
37  /**
38   * The class loading gateway. Each class realm has access to a base class loader, imports form zero or more other class
39   * loaders, an optional parent class loader and of course its own class path. When queried for a class/resource, a class
40   * realm will always query its base class loader first before it delegates to a pluggable strategy. The strategy in turn
41   * controls the order in which imported class loaders, the parent class loader and the realm itself are searched. The
42   * base class loader is assumed to be capable of loading of the bootstrap classes.
43   *
44   * @author <a href="mailto:bob@eng.werken.com">bob mcwhirter</a>
45   * @author Jason van Zyl
46   */
47  public class ClassRealm
48      extends URLClassLoader
49  {
50  
51      private ClassWorld world;
52  
53      private String id;
54  
55      private SortedSet<Entry> foreignImports;
56  
57      private SortedSet<Entry> parentImports;
58  
59      private Strategy strategy;
60  
61      private ClassLoader parentClassLoader;
62  
63      private static final boolean isParallelCapable = Closeable.class.isAssignableFrom( URLClassLoader.class );
64  
65      /**
66       * Creates a new class realm.
67       *
68       * @param world           The class world this realm belongs to, must not be <code>null</code>.
69       * @param id              The identifier for this realm, must not be <code>null</code>.
70       * @param baseClassLoader The base class loader for this realm, may be <code>null</code> to use the bootstrap class
71       *                        loader.
72       */
73      public ClassRealm( ClassWorld world, String id, ClassLoader baseClassLoader )
74      {
75          super( new URL[0], baseClassLoader );
76  
77          this.world = world;
78  
79          this.id = id;
80  
81          foreignImports = new TreeSet<Entry>();
82  
83          strategy = StrategyFactory.getStrategy( this );
84      }
85  
86      public String getId()
87      {
88          return this.id;
89      }
90  
91      public ClassWorld getWorld()
92      {
93          return this.world;
94      }
95  
96      public void importFromParent( String packageName )
97      {
98          if ( parentImports == null )
99          {
100             parentImports = new TreeSet<Entry>();
101         }
102 
103         parentImports.add( new Entry( null, packageName ) );
104     }
105 
106     boolean isImportedFromParent( String name )
107     {
108         if ( parentImports != null && !parentImports.isEmpty() )
109         {
110             for ( Entry entry : parentImports )
111             {
112                 if ( entry.matches( name ) )
113                 {
114                     return true;
115                 }
116             }
117 
118             return false;
119         }
120 
121         return true;
122     }
123 
124     public void importFrom( String realmId, String packageName )
125         throws NoSuchRealmException
126     {
127         importFrom( getWorld().getRealm( realmId ), packageName );
128     }
129 
130     public void importFrom( ClassLoader classLoader, String packageName )
131     {
132         foreignImports.add( new Entry( classLoader, packageName ) );
133     }
134 
135     public ClassLoader getImportClassLoader( String name )
136     {
137         for ( Entry entry : foreignImports )
138         {
139             if ( entry.matches( name ) )
140             {
141                 return entry.getClassLoader();
142             }
143         }
144 
145         return null;
146     }
147 
148     public Collection<ClassRealm> getImportRealms()
149     {
150         Collection<ClassRealm> importRealms = new HashSet<ClassRealm>();
151 
152         for ( Entry entry : foreignImports )
153         {
154             if ( entry.getClassLoader() instanceof ClassRealm )
155             {
156                 importRealms.add( (ClassRealm) entry.getClassLoader() );
157             }
158         }
159 
160         return importRealms;
161     }
162 
163     public Strategy getStrategy()
164     {
165         return strategy;
166     }
167 
168     public void setParentClassLoader( ClassLoader parentClassLoader )
169     {
170         this.parentClassLoader = parentClassLoader;
171     }
172 
173     public ClassLoader getParentClassLoader()
174     {
175         return parentClassLoader;
176     }
177 
178     public void setParentRealm( ClassRealm realm )
179     {
180         this.parentClassLoader = realm;
181     }
182 
183     public ClassRealm getParentRealm()
184     {
185         return ( parentClassLoader instanceof ClassRealm ) ? (ClassRealm) parentClassLoader : null;
186     }
187 
188     public ClassRealm createChildRealm( String id )
189         throws DuplicateRealmException
190     {
191         ClassRealm childRealm = getWorld().newRealm( id, null );
192 
193         childRealm.setParentRealm( this );
194 
195         return childRealm;
196     }
197 
198     public void addURL( URL url )
199     {
200         String urlStr = url.toExternalForm();
201 
202         if ( urlStr.startsWith( "jar:" ) && urlStr.endsWith( "!/" ) )
203         {
204             urlStr = urlStr.substring( 4, urlStr.length() - 2 );
205 
206             try
207             {
208                 url = new URL( urlStr );
209             }
210             catch ( MalformedURLException e )
211             {
212                 e.printStackTrace();
213             }
214         }
215 
216         super.addURL( url );
217     }
218 
219     // ----------------------------------------------------------------------
220     // We delegate to the Strategy here so that we can change the behavior
221     // of any existing ClassRealm.
222     // ----------------------------------------------------------------------
223 
224     public Class<?> loadClass( String name )
225         throws ClassNotFoundException
226     {
227         return loadClass( name, false );
228     }
229 
230     protected Class<?> loadClass( String name, boolean resolve )
231         throws ClassNotFoundException
232     {
233         if ( isParallelCapable )
234         {
235             return unsynchronizedLoadClass( name, resolve );
236 
237         }
238         else
239         {
240             synchronized ( this )
241             {
242                 return unsynchronizedLoadClass( name, resolve );
243             }
244 
245         }
246     }
247 
248     private Class<?> unsynchronizedLoadClass( String name, boolean resolve )
249         throws ClassNotFoundException
250     {
251         try
252         {
253             // first, try loading bootstrap classes
254             return super.loadClass( name, resolve );
255         }
256         catch ( ClassNotFoundException e )
257         {
258             // next, try loading via imports, self and parent as controlled by strategy
259             return strategy.loadClass( name );
260         }
261     }
262 
263     protected Class<?> findClass( String name )
264         throws ClassNotFoundException
265     {
266         /*
267          * NOTE: This gets only called from ClassLoader.loadClass(Class, boolean) while we try to check for bootstrap
268          * stuff. Don't scan our class path yet, loadClassFromSelf() will do this later when called by the strategy.
269          */
270         throw new ClassNotFoundException( name );
271     }
272 
273     public URL getResource( String name )
274     {
275         URL resource = super.getResource( name );
276         return resource != null ? resource : strategy.getResource( name );
277     }
278 
279     public URL findResource( String name )
280     {
281         return super.findResource( name );
282     }
283 
284     public Enumeration<URL> getResources( String name )
285         throws IOException
286     {
287         Collection<URL> resources = new LinkedHashSet<URL>( Collections.list( super.getResources( name ) ) );
288         resources.addAll( Collections.list( strategy.getResources( name ) ) );
289         return Collections.enumeration( resources );
290     }
291 
292     public Enumeration<URL> findResources( String name )
293         throws IOException
294     {
295         return super.findResources( name );
296     }
297 
298     // ----------------------------------------------------------------------------
299     // Display methods
300     // ----------------------------------------------------------------------------
301 
302     public void display()
303     {
304         display( System.out );
305     }
306 
307     public void display( PrintStream out )
308     {
309         out.println( "-----------------------------------------------------" );
310 
311         for ( ClassRealm cr = this; cr != null; cr = cr.getParentRealm() )
312         {
313             out.println( "realm =    " + cr.getId() );
314             out.println( "strategy = " + cr.getStrategy().getClass().getName() );
315 
316             showUrls( cr, out );
317 
318             out.println();
319         }
320 
321         out.println( "-----------------------------------------------------" );
322     }
323 
324     private static void showUrls( ClassRealm classRealm, PrintStream out )
325     {
326         URL[] urls = classRealm.getURLs();
327 
328         for ( int i = 0; i < urls.length; i++ )
329         {
330             out.println( "urls[" + i + "] = " + urls[i] );
331         }
332 
333         out.println( "Number of foreign imports: " + classRealm.foreignImports.size() );
334 
335         for ( Entry entry : classRealm.foreignImports )
336         {
337             out.println( "import: " + entry );
338         }
339 
340         if ( classRealm.parentImports != null )
341         {
342             out.println( "Number of parent imports: " + classRealm.parentImports.size() );
343 
344             for ( Entry entry : classRealm.parentImports )
345             {
346                 out.println( "import: " + entry );
347             }
348         }
349     }
350 
351     public String toString()
352     {
353         return "ClassRealm[" + getId() + ", parent: " + getParentClassLoader() + "]";
354     }
355 
356     //---------------------------------------------------------------------------------------------
357     // Search methods that can be ordered by strategies to load a class
358     //---------------------------------------------------------------------------------------------
359 
360     public Class<?> loadClassFromImport( String name )
361     {
362         ClassLoader importClassLoader = getImportClassLoader( name );
363 
364         if ( importClassLoader != null )
365         {
366             try
367             {
368                 return importClassLoader.loadClass( name );
369             }
370             catch ( ClassNotFoundException e )
371             {
372                 return null;
373             }
374         }
375 
376         return null;
377     }
378 
379     public Class<?> loadClassFromSelf( String name )
380     {
381         synchronized ( getClassRealmLoadingLock( name ) )
382         {
383             try
384             {
385                 Class<?> clazz = findLoadedClass( name );
386 
387                 if ( clazz == null )
388                 {
389                     clazz = super.findClass( name );
390                 }
391 
392                 return clazz;
393             }
394             catch ( ClassNotFoundException e )
395             {
396                 return null;
397             }
398         }
399     }
400 
401     private Object getClassRealmLoadingLock( String name )
402     {
403         if ( isParallelCapable )
404         {
405             return getClassLoadingLock( name );
406         }
407         else
408         {
409             return this;
410         }
411     }
412 
413     public Class<?> loadClassFromParent( String name )
414     {
415         ClassLoader parent = getParentClassLoader();
416 
417         if ( parent != null && isImportedFromParent( name ) )
418         {
419             try
420             {
421                 return parent.loadClass( name );
422             }
423             catch ( ClassNotFoundException e )
424             {
425                 return null;
426             }
427         }
428 
429         return null;
430     }
431 
432     //---------------------------------------------------------------------------------------------
433     // Search methods that can be ordered by strategies to get a resource
434     //---------------------------------------------------------------------------------------------
435 
436     public URL loadResourceFromImport( String name )
437     {
438         ClassLoader importClassLoader = getImportClassLoader( name );
439 
440         if ( importClassLoader != null )
441         {
442             return importClassLoader.getResource( name );
443         }
444 
445         return null;
446     }
447 
448     public URL loadResourceFromSelf( String name )
449     {
450         return super.findResource( name );
451     }
452 
453     public URL loadResourceFromParent( String name )
454     {
455         ClassLoader parent = getParentClassLoader();
456 
457         if ( parent != null && isImportedFromParent( name ) )
458         {
459             return parent.getResource( name );
460         }
461         else
462         {
463             return null;
464         }
465     }
466 
467     //---------------------------------------------------------------------------------------------
468     // Search methods that can be ordered by strategies to get resources
469     //---------------------------------------------------------------------------------------------
470 
471     public Enumeration<URL> loadResourcesFromImport( String name )
472     {
473         ClassLoader importClassLoader = getImportClassLoader( name );
474 
475         if ( importClassLoader != null )
476         {
477             try
478             {
479                 return importClassLoader.getResources( name );
480             }
481             catch ( IOException e )
482             {
483                 return null;
484             }
485         }
486 
487         return null;
488     }
489 
490     public Enumeration<URL> loadResourcesFromSelf( String name )
491     {
492         try
493         {
494             return super.findResources( name );
495         }
496         catch ( IOException e )
497         {
498             return null;
499         }
500     }
501 
502     public Enumeration<URL> loadResourcesFromParent( String name )
503     {
504         ClassLoader parent = getParentClassLoader();
505 
506         if ( parent != null && isImportedFromParent( name ) )
507         {
508             try
509             {
510                 return parent.getResources( name );
511             }
512             catch ( IOException e )
513             {
514                 // eat it
515             }
516         }
517 
518         return null;
519     }
520 
521     static
522     {
523         if  (isParallelCapable) // Avoid running this method on older jdks
524         {
525             registerAsParallelCapable();
526         }
527     }
528 
529 }