View Javadoc

1   /*
2   $Id: RootLoader.java,v 1.6 2005/09/06 08:19:32 hmeling Exp $
3   
4   Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
5   
6   Redistribution and use of this software and associated documentation
7   ("Software"), with or without modification, are permitted provided
8   that the following conditions are met:
9   
10  1. Redistributions of source code must retain copyright
11     statements and notices.  Redistributions must also contain a
12     copy of this document.
13  
14  2. Redistributions in binary form must reproduce the
15     above copyright notice, this list of conditions and the
16     following disclaimer in the documentation and/or other
17     materials provided with the distribution.
18  
19  3. The name "groovy" must not be used to endorse or promote
20     products derived from this Software without prior written
21     permission of The Codehaus.  For written permission,
22     please contact info@codehaus.org.
23  
24  4. Products derived from this Software may not be called "groovy"
25     nor may "groovy" appear in their names without prior written
26     permission of The Codehaus. "groovy" is a registered
27     trademark of The Codehaus.
28  
29  5. Due credit should be given to The Codehaus -
30     http://groovy.codehaus.org/
31  
32  THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
33  ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
34  NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
35  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
36  THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
37  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
38  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
39  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
41  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
42  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
43  OF THE POSSIBILITY OF SUCH DAMAGE.
44  
45  */
46  package org.codehaus.groovy.tools;
47  
48  import java.io.IOException;
49  import java.net.URL;
50  import java.net.URLClassLoader;
51  import java.util.Enumeration;
52  
53  /***
54   * This ClassLoader should be used as root of class loaders. Any
55   * RootLoader does have it's own classpath. When searching for a 
56   * class or resource this classpath will be used. Parent 
57   * Classloaders are ignored first. If a class or resource 
58   * can't be found in the classpath of the RootLoader, then parent is
59   * checked.
60   * 
61   * <b>Note:</b> this is very against the normal behavior of 
62   * classloaders. Normal is to frist check parent and then look in
63   * the ressources you gave this classloader.
64   * 
65   * It's possible to add urls to the classpath at runtime through
66   * @see #addURL(URL)
67   * 
68   * <b>Why using RootLoader?</b>
69   * If you have to load classes with multiple classloaders and a
70   * classloader does know a class which depends on a class only 
71   * a child of this loader does know, then you won't be able to 
72   * load the class. To load the class the child is not allowed 
73   * to redirect it's search for the class to the parent first.
74   * That way the child can load the class. If the child does not
75   * have all classes to do this, this fails of course.
76   *  
77   * For example:
78   *  
79   *  <pre>
80   *  parentLoader   (has classpath: a.jar;c.jar)
81   *      |
82   *      |
83   *  childLoader    (has classpath: a.jar;b.jar;c.jar)
84   *  </pre>
85   *  
86   *  class C (from c.jar) extends B (from b.jar)
87   *  
88   *  childLoader.find("C")
89   *  --> parentLoader does know C.class, try to load it
90   *  --> to load C.class it has to load B.class
91   *  --> parentLoader is unable to find B.class in a.jar or c.jar
92   *  --> NoClassDefFoundException!
93   *  
94   *  if childLoader had tried to load the class by itself, there
95   *  would be no problem. Changing childLoader to be a RootLoader 
96   *  instance will solve that problem.
97   *   
98   * @author Jochen Theodorou
99   */
100 public class RootLoader extends ClassLoader {
101 
102     private ClassLoader parent; 
103     private InnerLoader inner;
104     
105     private class InnerLoader extends URLClassLoader {
106         public InnerLoader(URL[] urls) {
107             super(urls,null);
108         }        
109         public void addPathEntry(URL url) {
110             addURL(url);
111         }
112         protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
113             try {
114                 return super.loadClass(name, resolve);
115             } catch (ClassNotFoundException cnfe) {
116                 return RootLoader.this.loadClassByName(name,true,resolve);
117             }
118         }
119     }
120     
121     /***
122      * constructs a new RootLoader without classpath
123      * @param parent the parent Loader
124      */   
125     private RootLoader(ClassLoader parent) {
126         super(parent);
127     }
128     
129     /***
130      * constructs a new RootLoader with a parent loader and an
131      * array of URLs as classpath
132      */
133     public RootLoader(URL[] urls, ClassLoader parent) {
134         this(parent);
135         inner = new InnerLoader(urls);
136     }
137     
138     /***
139      * constructs a new RootLoader with a @see LoaderConfiguration
140      * object which holds the classpath
141      */
142     public RootLoader(LoaderConfiguration lc) {
143         this(RootLoader.class.getClassLoader());
144         Thread.currentThread().setContextClassLoader(this);
145         inner = new InnerLoader(lc.getClassPathUrls());
146     }
147 
148     /***
149      * loads a class using the name of the class
150      */
151     protected synchronized Class loadClass(final String name, boolean resolve) throws ClassNotFoundException {
152         return loadClassByName(name,false,resolve);
153     }
154     
155     /***
156      * method to avoid endless loops
157      */
158     private Class loadClassByName(String name, boolean ignoreInner, boolean resolve) throws ClassNotFoundException {
159         // if the searched class can't be found in inner, then try the 
160         // old behavior which searches in parent first
161         if (!ignoreInner) {
162             try {
163                 return inner.loadClass(name);
164             } catch (ClassNotFoundException cnfe) {
165                 // fall through
166             }
167         }
168         return super.loadClass(name,true);
169     }
170     
171     /***
172      * returns the URL of a resource, or null if it is not found
173      */
174     public URL getResource(String name) {
175         URL url = inner.getResource(name);
176         url = super.getResource(name);
177         return url;
178     }    
179     
180     /***
181      * returns an Enumeration of all found ressources. Resources found
182      * in the classpath of this loader are at the beginning of the
183      * returned enumeration
184      */
185     protected Enumeration findResources(String name) throws IOException {
186         final Enumeration enum1 = inner.findResources(name);
187         final Enumeration enum2 = super.findResources(name);
188         return new Enumeration() {
189             public boolean hasMoreElements() {
190                 return enum1.hasMoreElements() || enum2.hasMoreElements();
191             }
192             public Object nextElement() {
193                 if (enum1.hasMoreElements()) return enum1.nextElement();
194                 if (enum2.hasMoreElements()) return enum2.nextElement();
195                 return null;
196             }
197         };
198     }
199  
200     /***
201      * adds an url to the classpath of this classloader
202      */
203     public void addURL(URL url) {
204         inner.addPathEntry(url);
205     }
206     
207     /***
208      * returns all classpath entries of this classloader
209      */
210     public URL[] getURLs() {
211         return inner.getURLs();
212     }
213 }