1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
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
160
161 if (!ignoreInner) {
162 try {
163 return inner.loadClass(name);
164 } catch (ClassNotFoundException cnfe) {
165
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 }