1 /*
2 * Copyright 2003-2007 the original author or authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.codehaus.groovy.tools;
17
18 import java.net.URL;
19 import java.net.URLClassLoader;
20 import java.util.Map;
21 import java.util.HashMap;
22
23 /**
24 * This ClassLoader should be used as root of class loaders. Any
25 * RootLoader does have it's own classpath. When searching for a
26 * class or resource this classpath will be used. Parent
27 * Classloaders are ignored first. If a class or resource
28 * can't be found in the classpath of the RootLoader, then parent is
29 * checked.
30 * <p/>
31 * <b>Note:</b> this is very against the normal behavior of
32 * classloaders. Normal is to first check parent and then look in
33 * the resources you gave this classloader.
34 * <p/>
35 * It's possible to add urls to the classpath at runtime through
36 * @see #addURL(URL)
37 * <p/>
38 * <b>Why using RootLoader?</b>
39 * If you have to load classes with multiple classloaders and a
40 * classloader does know a class which depends on a class only
41 * a child of this loader does know, then you won't be able to
42 * load the class. To load the class the child is not allowed
43 * to redirect it's search for the class to the parent first.
44 * That way the child can load the class. If the child does not
45 * have all classes to do this, this fails of course.
46 * <p/>
47 * For example:
48 * <p/>
49 * <pre>
50 * parentLoader (has classpath: a.jar;c.jar)
51 * |
52 * |
53 * childLoader (has classpath: a.jar;b.jar;c.jar)
54 * </pre>
55 * <p/>
56 * class C (from c.jar) extends B (from b.jar)
57 * <p/>
58 * childLoader.find("C")
59 * --> parentLoader does know C.class, try to load it
60 * --> to load C.class it has to load B.class
61 * --> parentLoader is unable to find B.class in a.jar or c.jar
62 * --> NoClassDefFoundException!
63 * <p/>
64 * if childLoader had tried to load the class by itself, there
65 * would be no problem. Changing childLoader to be a RootLoader
66 * instance will solve that problem.
67 *
68 * @author Jochen Theodorou
69 */
70 public class RootLoader extends URLClassLoader {
71
72 private Map customClasses = new HashMap();
73
74 /**
75 * constructs a new RootLoader without classpath
76 *
77 * @param parent the parent Loader
78 */
79 private RootLoader(ClassLoader parent) {
80 this(new URL[0], parent);
81 }
82
83 /**
84 * constructs a new RootLoader with a parent loader and an
85 * array of URLs as classpath
86 */
87 public RootLoader(URL[] urls, ClassLoader parent) {
88 super(urls, parent);
89 // major hack here...!
90 try{
91 customClasses.put("org.w3c.dom.Node",super.loadClass("org.w3c.dom.Node",false));
92 } catch (Exception e) {}
93 }
94
95 private static ClassLoader chooseParent() {
96 ClassLoader cl = RootLoader.class.getClassLoader();
97 if (cl != null) return cl;
98 return ClassLoader.getSystemClassLoader();
99 }
100
101 /**
102 * constructs a new RootLoader with a @see LoaderConfiguration
103 * object which holds the classpath
104 */
105 public RootLoader(LoaderConfiguration lc) {
106 this(chooseParent());
107 Thread.currentThread().setContextClassLoader(this);
108 URL[] urls = lc.getClassPathUrls();
109 for (int i = 0; i < urls.length; i++) {
110 addURL(urls[i]);
111 }
112 }
113
114 /**
115 * loads a class using the name of the class
116 */
117 protected Class loadClass(final String name, boolean resolve) throws ClassNotFoundException {
118 Class c = this.findLoadedClass(name);
119 if (c != null) return c;
120 c = (Class) customClasses.get(name);
121 if (c != null) return c;
122
123 try {
124 c = oldFindClass(name);
125 } catch (ClassNotFoundException cnfe) {
126 // IGNORE
127 }
128 if (c == null) c = super.loadClass(name, resolve);
129
130 if (resolve) resolveClass(c);
131
132 return c;
133 }
134
135 /**
136 * returns the URL of a resource, or null if it is not found
137 */
138 public URL getResource(String name) {
139 URL url = findResource(name);
140 if (url == null) url = super.getResource(name);
141 return url;
142 }
143
144 /**
145 * adds an url to the classpath of this classloader
146 */
147 public void addURL(URL url) {
148 super.addURL(url);
149 }
150
151 private Class oldFindClass(String name) throws ClassNotFoundException {
152 return super.findClass(name);
153 }
154
155 protected Class findClass(String name) throws ClassNotFoundException {
156 throw new ClassNotFoundException(name);
157 }
158 }