View Javadoc

1   package org.codehaus.classworlds;
2   
3   /*
4    $Id: DefaultClassRealm.java,v 1.8 2004/08/09 22:00:16 jvanzyl Exp $
5   
6    Copyright 2002 (C) The Werken Company. All Rights Reserved.
7   
8    Redistribution and use of this software and associated documentation
9    ("Software"), with or without modification, are permitted provided
10   that the following conditions are met:
11  
12   1. Redistributions of source code must retain copyright
13      statements and notices.  Redistributions must also contain a
14      copy of this document.
15  
16   2. Redistributions in binary form must reproduce the
17      above copyright notice, this list of conditions and the
18      following disclaimer in the documentation and/or other
19      materials provided with the distribution.
20  
21   3. The name "classworlds" must not be used to endorse or promote
22      products derived from this Software without prior written
23      permission of The Werken Company.  For written permission,
24      please contact bob@werken.com.
25  
26   4. Products derived from this Software may not be called "classworlds"
27      nor may "classworlds" appear in their names without prior written
28      permission of The Werken Company. "classworlds" is a registered
29      trademark of The Werken Company.
30  
31   5. Due credit should be given to The Werken Company.
32      (http://classworlds.werken.com/).
33  
34   THIS SOFTWARE IS PROVIDED BY THE WERKEN COMPANY AND CONTRIBUTORS
35   ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
36   NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
37   FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
38   THE WERKEN COMPANY OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
39   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
40   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
41   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
42   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
43   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
44   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
45   OF THE POSSIBILITY OF SUCH DAMAGE.
46  
47   */
48  
49  
50  import java.io.File;
51  import java.io.IOException;
52  import java.io.InputStream;
53  import java.net.URL;
54  import java.util.Enumeration;
55  import java.util.Iterator;
56  import java.util.TreeSet;
57  import java.util.Vector;
58  
59  
60  /***
61   * Implementation of <code>ClassRealm</code>.  The realm is the class loading gateway.
62   * The search is proceded as follows:
63   * <ol>
64   * <li>Search the parent class loader (passed via the constructor) if there
65   * is one.</li>
66   * <li>Search the imports.</li>
67   * <li>Search this realm's constituents.</li>
68   * <li>Search the parent realm.</li>
69   * </ol>
70   *
71   * @author <a href="mailto:bob@eng.werken.com">bob mcwhirter</a>
72   * @author <a href="mailto:jason@zenplex.com">Jason van Zyl</a>
73   * @version $Id: DefaultClassRealm.java,v 1.8 2004/08/09 22:00:16 jvanzyl Exp $
74   * @todo allow inheritance to be turn on/off at runtime.
75   * @todo allow direction of search
76   */
77  public class DefaultClassRealm
78      implements ClassRealm
79  {
80      private ClassWorld world;
81  
82      private String id;
83  
84      private TreeSet imports;
85  
86      private ClassLoader foreignClassLoader;
87  
88      private RealmClassLoader classLoader;
89  
90      private ClassRealm parent;
91  
92      public DefaultClassRealm( ClassWorld world, String id )
93      {
94          this( world, id, null );
95      }
96  
97      public DefaultClassRealm( ClassWorld world, String id, ClassLoader foreignClassLoader )
98      {
99          this.world = world;
100 
101         this.id = id;
102 
103         imports = new TreeSet();
104 
105         if ( foreignClassLoader != null )
106         {
107             this.foreignClassLoader = foreignClassLoader;
108         }
109 
110         if ( "true".equals( System.getProperty( "classworlds.bootstrapped" ) ) )
111         {
112             classLoader = new UberJarRealmClassLoader( this );
113         }
114         else
115         {
116             classLoader = new RealmClassLoader( this );
117         }
118     }
119 
120     public URL[] getConstituents()
121     {
122         return classLoader.getURLs();
123     }
124 
125     public ClassRealm getParent()
126     {
127         return parent;
128     }
129 
130     public void setParent( ClassRealm parent )
131     {
132         this.parent = parent;
133     }
134 
135     public String getId()
136     {
137         return this.id;
138     }
139 
140     public ClassWorld getWorld()
141     {
142         return this.world;
143     }
144 
145     publicong> void importFrom( String realmId, String packageName )
146         throws NoSuchRealmException
147     {
148         imports.add( new Entry( getWorld().getRealm( realmId ), packageName ) );
149         imports.add( new Entry( getWorld().getRealm( realmId ), packageName.replace('.', '/') ) );
150     }
151 
152     public void addConstituent( URL constituent )
153     {
154         classLoader.addConstituent( constituent );
155     }
156     
157     /***
158      *  Adds a byte[] class definition as a constituent for locating classes.
159      *  Currently uses BytesURLStreamHandler to hold a reference of the byte[] in memory.
160      *  This ensures we have a unifed URL resource model for all constituents.
161      *  The code to cache to disk is commented out - maybe a property to choose which method?
162      *
163      *  @param constituent class name
164      *  @param b the class definition as a byte[]
165      */
166     public void addConstituent(String constituent,
167                                byte[] b) throws ClassNotFoundException
168     {
169         try
170         {
171             File path, file;
172             if (constituent.lastIndexOf('.') != -1)
173             {
174                 path = new File("byteclass/" + constituent.substring(0, constituent.lastIndexOf('.') + 1).replace('.', File.separatorChar));
175 
176                 file = new File(path, constituent.substring(constituent.lastIndexOf('.') + 1) + ".class");
177             }
178             else
179             {
180                 path = new File("byteclass/");
181 
182                 file = new File(path, constituent + ".class");
183             }
184 
185             addConstituent( new URL( null,
186                                      file.toURL().toExternalForm(),
187                                      new BytesURLStreamHandler(b) ) );
188         }
189         catch (java.io.IOException e)
190         {
191             throw new ClassNotFoundException( "Couldn't load byte stream.", e );
192         }
193     }
194     
195     public ClassRealm locateSourceRealm( String classname )
196     {
197         for ( Iterator iterator = imports.iterator(); iterator.hasNext(); )
198         {
199             Entry entry = (Entry) iterator.next();
200 
201             if ( entry.matches( classname ) )
202             {
203                 return entry.getRealm();
204             }
205         }
206 
207         return this;
208     }
209 
210     public ClassLoader getClassLoader()
211     {
212         return classLoader;
213     }
214 
215     public ClassRealm createChildRealm( String id )
216         throws DuplicateRealmException
217     {
218         ClassRealm childRealm = getWorld().newRealm( id );
219 
220         childRealm.setParent( this );
221 
222         return childRealm;
223     }
224 
225     // ----------------------------------------------------------------------
226     // ClassLoader API
227     // ----------------------------------------------------------------------
228 
229     public Class loadClass( String name )
230         throws ClassNotFoundException
231     {
232         if ( name.startsWith( "org.codehaus.classworlds." ) )
233         {
234             return getWorld().loadClass( name );
235         }
236 
237         try
238         {
239             if ( foreignClassLoader != null )
240             {
241                 try
242                 {
243                     return foreignClassLoader.loadClass( name );
244                 }
245                 catch ( ClassNotFoundException e )
246                 {
247                     // Do nothing as we will now look in the realm.
248                 }
249             }
250 
251             ClassRealm sourceRealm = locateSourceRealm( name );
252             
253             if ( sourceRealm == this )
254             {
255                 return classLoader.loadClassDirect( name );
256             }
257             else
258             {
259                 try
260                 {
261                     return sourceRealm.loadClass( name );
262                 }
263                 catch ( ClassNotFoundException cnfe )
264                 {
265                     // If we can't find it in an import, try loading directly.
266                     return classLoader.loadClassDirect( name );
267                 }
268             }
269         }
270         catch ( ClassNotFoundException e )
271         {
272             if ( getParent() != null )
273             {
274                 return getParent().loadClass( name );
275             }
276 
277             throw e;
278         }
279     }
280 
281     public URL getResource( String name )
282     {
283         URL resource = null;
284         name = UrlUtils.normalizeUrlPath( name );
285         
286         if ( foreignClassLoader != null )
287         {
288             resource = foreignClassLoader.getResource( name );
289 
290             if ( resource != null )
291             {
292                 return resource;
293             }
294         }
295 
296         ClassRealm sourceRealm = locateSourceRealm( name );
297         
298         if ( sourceRealm == this )
299         {
300             resource = classLoader.getResourceDirect( name );
301         }
302         else
303         {
304         	resource = sourceRealm.getResource( name );
305 
306         	if ( resource == null )
307             {
308                 resource = classLoader.getResourceDirect( name );
309             }
310         }
311 
312         if ( resource == null && getParent() != null )
313         {
314             resource = getParent().getResource( name );
315         }
316 
317         return resource;
318     }
319 
320     public InputStream getResourceAsStream( String name )
321     {
322         URL url = getResource( name );
323 
324         InputStream is = null;
325 
326         if ( url != null )
327         {
328             try
329             {
330                 is = url.openStream();
331             }
332             catch ( IOException e )
333             {
334                 // do nothing
335             }
336         }
337 
338         return is;
339     }
340     
341     public Enumeration findResources(String name)
342         throws IOException
343 	{
344 		name = UrlUtils.normalizeUrlPath(name);
345 
346 		Vector resources = new Vector();
347 
348         // Find resources from the parent class loader
349         if ( foreignClassLoader != null )
350         {
351             for ( Enumeration res = foreignClassLoader.getResources(name); res.hasMoreElements(); )
352             {
353                 resources.addElement(res.nextElement());
354             }
355         }
356         
357         // Load imports
358         ClassRealm sourceRealm = locateSourceRealm( name );
359         
360         if ( sourceRealm != this )
361         {
362         	// Attempt to load directly first, then go to the imported packages.
363             for ( Enumeration res = sourceRealm.findResources(name); res.hasMoreElements(); )
364             {
365                 resources.addElement(res.nextElement());
366             }
367         }
368 
369 		// Load from our classloader
370 		for ( Enumeration direct = classLoader.findResourcesDirect(name); direct.hasMoreElements(); )
371 		{
372 			resources.addElement(direct.nextElement());
373 		}
374         
375 		// Find resources from the parent realm.
376 		if (parent != null)
377 		{
378 			for ( Enumeration parent = getParent().findResources(name); parent.hasMoreElements(); )
379             {
380 				resources.addElement(parent.nextElement());
381             }
382 		}
383 
384 		return resources.elements();
385 	}
386 
387     public void display()
388     {
389         ClassRealm cr = this;
390 
391         System.out.println( "-----------------------------------------------------" );
392 
393 
394         showUrls( cr );
395 
396         while( cr.getParent() != null )
397         {
398             System.out.println( "\n" );
399 
400             cr = cr.getParent();
401 
402             showUrls( cr );
403         }
404 
405         System.out.println( "-----------------------------------------------------" );
406     }
407 
408     private void showUrls( ClassRealm classRealm )
409     {
410         System.out.println( "this realm = " + classRealm.getId() );
411 
412         URL[] urls = classRealm.getConstituents();
413 
414         for ( int i = 0; i < urls.length; i++ )
415         {
416             System.out.println( "urls[" + i + "] = " + urls[i] );
417         }
418 
419         System.out.println( "Number of imports: " + imports.size() );
420 
421         for ( Iterator i = imports.iterator(); i.hasNext(); )
422         {
423             System.out.println( "import: " + i.next() );
424         }
425     }
426 }