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