001 /*
002 $Id: RootLoader.java,v 1.6 2005/09/06 08:19:32 hmeling Exp $
003
004 Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
005
006 Redistribution and use of this software and associated documentation
007 ("Software"), with or without modification, are permitted provided
008 that the following conditions are met:
009
010 1. Redistributions of source code must retain copyright
011 statements and notices. Redistributions must also contain a
012 copy of this document.
013
014 2. Redistributions in binary form must reproduce the
015 above copyright notice, this list of conditions and the
016 following disclaimer in the documentation and/or other
017 materials provided with the distribution.
018
019 3. The name "groovy" must not be used to endorse or promote
020 products derived from this Software without prior written
021 permission of The Codehaus. For written permission,
022 please contact info@codehaus.org.
023
024 4. Products derived from this Software may not be called "groovy"
025 nor may "groovy" appear in their names without prior written
026 permission of The Codehaus. "groovy" is a registered
027 trademark of The Codehaus.
028
029 5. Due credit should be given to The Codehaus -
030 http://groovy.codehaus.org/
031
032 THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
033 ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
034 NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
035 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
036 THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
037 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
038 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
039 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
040 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
041 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
042 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
043 OF THE POSSIBILITY OF SUCH DAMAGE.
044
045 */
046 package org.codehaus.groovy.tools;
047
048 import java.io.IOException;
049 import java.net.URL;
050 import java.net.URLClassLoader;
051 import java.util.Enumeration;
052
053 /**
054 * This ClassLoader should be used as root of class loaders. Any
055 * RootLoader does have it's own classpath. When searching for a
056 * class or resource this classpath will be used. Parent
057 * Classloaders are ignored first. If a class or resource
058 * can't be found in the classpath of the RootLoader, then parent is
059 * checked.
060 *
061 * <b>Note:</b> this is very against the normal behavior of
062 * classloaders. Normal is to frist check parent and then look in
063 * the ressources you gave this classloader.
064 *
065 * It's possible to add urls to the classpath at runtime through
066 * @see #addURL(URL)
067 *
068 * <b>Why using RootLoader?</b>
069 * If you have to load classes with multiple classloaders and a
070 * classloader does know a class which depends on a class only
071 * a child of this loader does know, then you won't be able to
072 * load the class. To load the class the child is not allowed
073 * to redirect it's search for the class to the parent first.
074 * That way the child can load the class. If the child does not
075 * have all classes to do this, this fails of course.
076 *
077 * For example:
078 *
079 * <pre>
080 * parentLoader (has classpath: a.jar;c.jar)
081 * |
082 * |
083 * childLoader (has classpath: a.jar;b.jar;c.jar)
084 * </pre>
085 *
086 * class C (from c.jar) extends B (from b.jar)
087 *
088 * childLoader.find("C")
089 * --> parentLoader does know C.class, try to load it
090 * --> to load C.class it has to load B.class
091 * --> parentLoader is unable to find B.class in a.jar or c.jar
092 * --> NoClassDefFoundException!
093 *
094 * if childLoader had tried to load the class by itself, there
095 * would be no problem. Changing childLoader to be a RootLoader
096 * instance will solve that problem.
097 *
098 * @author Jochen Theodorou
099 */
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 }