KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > logging > PathableClassLoader


1 /*
2  * Copyright 2005 The Apache Software Foundation.
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
17 package org.apache.commons.logging;
18
19 import java.io.File JavaDoc;
20 import java.io.IOException JavaDoc;
21 import java.io.InputStream JavaDoc;
22 import java.net.URL JavaDoc;
23 import java.net.URLClassLoader JavaDoc;
24 import java.util.ArrayList JavaDoc;
25 import java.util.Collections JavaDoc;
26 import java.util.Enumeration JavaDoc;
27 import java.util.HashMap JavaDoc;
28 import java.util.Iterator JavaDoc;
29 import java.util.Map JavaDoc;
30
31 /**
32  * A ClassLoader which sees only specified classes, and which can be
33  * set to do parent-first or child-first path lookup.
34  * <p>
35  * Note that this classloader is not "industrial strength"; users
36  * looking for such a class may wish to look at the Tomcat sourcecode
37  * instead. In particular, this class may not be threadsafe.
38  * <p>
39  * Note that the ClassLoader.getResources method isn't overloaded here.
40  * It would be nice to ensure that when child-first lookup is set the
41  * resources from the child are returned earlier in the list than the
42  * resources from the parent. However overriding this method isn't possible
43  * as the java 1.4 version of ClassLoader declares this method final
44  * (though the java 1.5 version has removed the final qualifier). As the
45  * ClassLoader javadoc doesn't specify the order in which resources
46  * are returned, it's valid to return the resources in any order (just
47  * untidy) so the inherited implementation is technically ok.
48  */

49
50 public class PathableClassLoader extends URLClassLoader JavaDoc {
51     
52     private static final URL JavaDoc[] NO_URLS = new URL JavaDoc[0];
53     
54     /**
55      * A map of package-prefix to ClassLoader. Any class which is in
56      * this map is looked up via the specified classloader instead of
57      * the classpath associated with this classloader or its parents.
58      * <p>
59      * This is necessary in order for the rest of the world to communicate
60      * with classes loaded via a custom classloader. As an example, junit
61      * testcases which are loaded via a custom classloader needs to see
62      * the same junit classes as the code invoking the testcase, otherwise
63      * they can't pass result objects back.
64      * <p>
65      * Normally, only a classloader created with a null parent needs to
66      * have any lookasides defined.
67      */

68     private HashMap JavaDoc lookasides = null;
69
70     /**
71      * See setParentFirst.
72      */

73     private boolean parentFirst = true;
74     
75     /**
76      * Constructor.
77      */

78     public PathableClassLoader(ClassLoader JavaDoc parent) {
79         super(NO_URLS, parent);
80     }
81     
82     /**
83      * Allow caller to explicitly add paths. Generally this not a good idea;
84      * use addLogicalLib instead, then define the location for that logical
85      * library in the build.xml file.
86      */

87     public void addURL(URL JavaDoc url) {
88         super.addURL(url);
89     }
90
91     /**
92      * Specify whether this classloader should ask the parent classloader
93      * to resolve a class first, before trying to resolve it via its own
94      * classpath.
95      * <p>
96      * Checking with the parent first is the normal approach for java, but
97      * components within containers such as servlet engines can use
98      * child-first lookup instead, to allow the components to override libs
99      * which are visible in shared classloaders provided by the container.
100      * <p>
101      * Note that the method getResources always behaves as if parentFirst=true,
102      * because of limitations in java 1.4; see the javadoc for method
103      * getResourcesInOrder for details.
104      * <p>
105      * This value defaults to true.
106      */

107     public void setParentFirst(boolean state) {
108         parentFirst = state;
109     }
110
111     /**
112      * For classes with the specified prefix, get them from the system
113      * classpath <i>which is active at the point this method is called</i>.
114      * <p>
115      * This method is just a shortcut for
116      * <pre>
117      * useExplicitLoader(prefix, ClassLoader.getSystemClassLoader());
118      * </pre>
119      */

120     public void useSystemLoader(String JavaDoc prefix) {
121         useExplicitLoader(prefix, ClassLoader.getSystemClassLoader());
122         
123     }
124
125     /**
126      * Specify a classloader to use for specific java packages.
127      */

128     public void useExplicitLoader(String JavaDoc prefix, ClassLoader JavaDoc loader) {
129         if (lookasides == null) {
130             lookasides = new HashMap JavaDoc();
131         }
132         lookasides.put(prefix, loader);
133     }
134
135     /**
136      * Specify a collection of logical libraries. See addLogicalLib.
137      */

138     public void addLogicalLib(String JavaDoc[] logicalLibs) {
139         for(int i=0; i<logicalLibs.length; ++i) {
140             addLogicalLib(logicalLibs[i]);
141         }
142     }
143
144     /**
145      * Specify a logical library to be included in the classpath used to
146      * locate classes.
147      * <p>
148      * The specified lib name is used as a key into the system properties;
149      * there is expected to be a system property defined with that name
150      * whose value is a url that indicates where that logical library can
151      * be found. Typically this is the name of a jar file, or a directory
152      * containing class files.
153      * <p>
154      * Using logical library names allows the calling code to specify its
155      * desired classpath without knowing the exact location of the necessary
156      * classes.
157      */

158     public void addLogicalLib(String JavaDoc logicalLib) {
159         String JavaDoc filename = System.getProperty(logicalLib);
160         if (filename == null) {
161             throw new UnknownError JavaDoc(
162                 "Logical lib [" + logicalLib + "] is not defined"
163                 + " as a System property.");
164         }
165
166         try {
167             URL JavaDoc url = new File JavaDoc(filename).toURL();
168             addURL(url);
169         } catch(java.net.MalformedURLException JavaDoc e) {
170             throw new UnknownError JavaDoc(
171                 "Invalid file [" + filename + "] for logical lib [" + logicalLib + "]");
172         }
173     }
174     
175     /**
176      * Override ClassLoader method.
177      * <p>
178      * For each explicitly mapped package prefix, if the name matches the
179      * prefix associated with that entry then attempt to load the class via
180      * that entries' classloader.
181      */

182     protected Class JavaDoc loadClass(String JavaDoc name, boolean resolve)
183     throws ClassNotFoundException JavaDoc {
184         // just for performance, check java and javax
185
if (name.startsWith("java.") || name.startsWith("javax.")) {
186             return super.loadClass(name, resolve);
187         }
188
189         if (lookasides != null) {
190             for(Iterator JavaDoc i = lookasides.entrySet().iterator(); i.hasNext(); ) {
191                 Map.Entry JavaDoc entry = (Map.Entry JavaDoc) i.next();
192                 String JavaDoc prefix = (String JavaDoc) entry.getKey();
193                 if (name.startsWith(prefix) == true) {
194                     ClassLoader JavaDoc loader = (ClassLoader JavaDoc) entry.getValue();
195                     Class JavaDoc clazz = Class.forName(name, resolve, loader);
196                     return clazz;
197                 }
198             }
199         }
200         
201         if (parentFirst) {
202             return super.loadClass(name, resolve);
203         } else {
204             // Implement child-first.
205
//
206
// It appears that the findClass method doesn't check whether the
207
// class has already been loaded. This seems odd to me, but without
208
// first checking via findLoadedClass we can get java.lang.LinkageError
209
// with message "duplicate class definition" which isn't good.
210

211             try {
212                 Class JavaDoc clazz = findLoadedClass(name);
213                 if (clazz == null) {
214                     clazz = super.findClass(name);
215                 }
216                 if (resolve) {
217                     resolveClass(clazz);
218                 }
219                 return clazz;
220             } catch(ClassNotFoundException JavaDoc e) {
221                 return super.loadClass(name, resolve);
222             }
223         }
224     }
225     
226     /**
227      * Same as parent class method except that when parentFirst is false
228      * the resource is looked for in the local classpath before the parent
229      * loader is consulted.
230      */

231     public URL JavaDoc getResource(String JavaDoc name) {
232         if (parentFirst) {
233             return super.getResource(name);
234         } else {
235             URL JavaDoc local = super.findResource(name);
236             if (local != null) {
237                 return local;
238             }
239             return super.getResource(name);
240         }
241     }
242     
243     /**
244      * Emulate a proper implementation of getResources which respects the
245      * setting for parentFirst.
246      * <p>
247      * Note that it's not possible to override the inherited getResources, as
248      * it's declared final in java1.4 (thought that's been removed for 1.5).
249      * The inherited implementation always behaves as if parentFirst=true.
250      */

251     public Enumeration JavaDoc getResourcesInOrder(String JavaDoc name) throws IOException JavaDoc {
252         if (parentFirst) {
253             return super.getResources(name);
254         } else {
255             Enumeration JavaDoc localUrls = super.findResources(name);
256             
257             ClassLoader JavaDoc parent = getParent();
258             if (parent == null) {
259                 // Alas, there is no method to get matching resources
260
// from a null (BOOT) parent classloader. Calling
261
// ClassLoader.getSystemClassLoader isn't right. Maybe
262
// calling Class.class.getResources(name) would do?
263
//
264
// However for the purposes of unit tests, we can
265
// simply assume that no relevant resources are
266
// loadable from the parent; unit tests will never be
267
// putting any of their resources in a "boot" classloader
268
// path!
269
return localUrls;
270             }
271             Enumeration JavaDoc parentUrls = parent.getResources(name);
272
273             ArrayList JavaDoc localItems = toList(localUrls);
274             ArrayList JavaDoc parentItems = toList(parentUrls);
275             localItems.addAll(parentItems);
276             return Collections.enumeration(localItems);
277         }
278     }
279     
280     /**
281      *
282      * Clean implementation of list function of
283      * {@link java.utils.Collection} added in JDK 1.4
284      * @param en <code>Enumeration</code>, possibly null
285      * @return <code>ArrayList</code> containing the enumerated
286      * elements in the enumerated order, not null
287      */

288     private ArrayList JavaDoc toList(Enumeration JavaDoc en) {
289         ArrayList JavaDoc results = new ArrayList JavaDoc();
290         if (en != null) {
291             while (en.hasMoreElements()){
292                 Object JavaDoc element = en.nextElement();
293                 results.add(element);
294             }
295         }
296         return results;
297     }
298     
299     /**
300      * Same as parent class method except that when parentFirst is false
301      * the resource is looked for in the local classpath before the parent
302      * loader is consulted.
303      */

304     public InputStream JavaDoc getResourceAsStream(String JavaDoc name) {
305         if (parentFirst) {
306             return super.getResourceAsStream(name);
307         } else {
308             URL JavaDoc local = super.findResource(name);
309             if (local != null) {
310                 try {
311                     return local.openStream();
312                 } catch(IOException JavaDoc e) {
313                     // TODO: check if this is right or whether we should
314
// fall back to trying parent. The javadoc doesn't say...
315
return null;
316                 }
317             }
318             return super.getResourceAsStream(name);
319         }
320     }
321 }
322
Popular Tags