KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > ungoverned > moduleloader > ModuleClassLoader


1 /*
2  * ModuleLoader - A generic, policy-driven class loader.
3  * Copyright (c) 2004, Richard S. Hall
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * * Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  * * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in
14  * the documentation and/or other materials provided with the
15  * distribution.
16  * * Neither the name of the ungoverned.org nor the names of its
17  * contributors may be used to endorse or promote products derived
18  * from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  * Contact: Richard S. Hall (heavy@ungoverned.org)
33  * Contributor(s):
34  *
35 **/

36 package org.ungoverned.moduleloader;
37
38 import java.net.URL JavaDoc;
39 import java.security.CodeSource JavaDoc;
40 import java.security.SecureClassLoader JavaDoc;
41 import java.security.cert.Certificate JavaDoc;
42 import java.util.Enumeration JavaDoc;
43 import java.util.Vector JavaDoc;
44
45 /**
46  * <p>
47  * Each module that is managed by a <tt>ModuleManager</tt> has a
48  * <tt>ModuleClassLoader</tt> associated with it. The <tt>ModuleClassLoader</tt>
49  * is responsible for loading all classes, resources, and native libraries
50  * for its module. The <tt>ModuleClassLoader</tt> of a module
51  * is accessed using the <tt>Module.getClassLoader()</tt> method. The
52  * <tt>ModuleClassLoader</tt> uses its module's
53  * <a HREF="ResourceSource.html"><tt>ResourceSource</tt></a>s
54  * and <a HREF="LibrarySource.html"><tt>LibrarySource</tt></a>s
55  * to perform its function.
56  * </p>
57  * <p>
58  * When loading a class or resource, the <tt>ModuleClassLoader</tt> does
59  * not immediately search its module's <tt>ResourceSource</tt>s, instead
60  * it first delegates the request to the
61  * <a HREF="SearchPolicy.html"><tt>SearchPolicy</tt></a> of the
62  * <tt>ModuleManager</tt>; this allows applications to inject specific
63  * class/resource loading policies. When the <tt>ModuleClassLoader</tt> delegates
64  * to the search policy, the search policy uses application-specific behavior
65  * to typically service the request from the <tt>ResourceSource</tt>s of
66  * other modules. If the search policy returns a result, then this result is
67  * returned immediately by the <tt>ModuleClassLoader</tt>; otherwise, it searches
68  * the <tt>ResourceSource</tt>s its module in an attempt to satisfy the
69  * original request.
70  * </p>
71  * <p>
72  * <b><i>Important:</i></b> The search policy <i>searches</i> modules in
73  * some application-specific manner in order to find a class or resource.
74  * This <i>search</i> is instigated, either directly or indirectly, by calls
75  * to <tt>ModuleClassLoader.loadClass()</tt> and <tt>ModuleClassLoader.getResource()</tt>,
76  * respectively. In order for the search policy to load a class or resource,
77  * it must <b>not</b> use <tt>ModuleClassLoader.loadClass()</tt> or
78  * <tt>ModuleClassLoader.getResource()</tt> again, because this would result
79  * in an infinite loop. Instead, the <tt>ModuleClassLoader</tt> offers the
80  * the methods <tt>ModuleClassLoader.searchForClass()</tt> and
81  * <tt>ModuleClassLoader.searchForResource()</tt> to search a given module
82  * and to avoid an infinite loop. As an example, consider the following
83  * snippet of code that implements an "exhaustive" search policy:
84  * </p>
85  * <pre>
86  * ...
87  * public Class findClass(Module module, String name)
88  * {
89  * Module[] modules = m_mgr.getModules();
90  * for (int i = 0; i < modules.length; i++)
91  * {
92  * try {
93  * Class clazz = modules[i].getClassLoader().searchForClass(name);
94  * if (clazz != null)
95  * {
96  * return clazz;
97  * }
98  * } catch (Throwable th) {
99  * }
100  * }
101  *
102  * return null;
103  * }
104  * ...
105  * </pre>
106  * <p>
107  * In the above code, the search policy "exhaustively" searches every module in the
108  * <tt>ModuleManager</tt> to find the requested resource. Note that this policy
109  * will also search the module that originated the request, which is not totally
110  * necessary since returning <tt>null</tt> will cause the <tt>ModuleClassLoader</tt>
111  * to search the originating module's <tt>ResourceSource</tt>s.
112  * </p>
113 **/

114 public class ModuleClassLoader extends SecureClassLoader JavaDoc
115 {
116     private ModuleManager m_mgr = null;
117     private Module m_module = null;
118
119     /**
120      * <p>
121      * Constructs an instance using the specified <tt>ModuleManager</tt>, for
122      * the specified <tt>Module</tt>. This constructor is protected so that
123      * it cannot be created publicly.
124      * </p>
125      * @param mgr the <tt>ModuleManager</tt> of the <tt>Module</tt>.
126      * @param module the <tt>Module</tt> instance associated with the class loader.
127     **/

128     protected ModuleClassLoader(ModuleManager mgr, Module module)
129     {
130         super(ModuleClassLoader.class.getClassLoader());
131         m_mgr = mgr;
132         m_module = module;
133     }
134
135     /**
136      * <p>
137      * This method is nearly an exact copy of the ClassLoader.loadClass()
138      * method. The main difference is that it delegates to its associated
139      * <tt>ModuleManager</tt>'s search policy before calling the
140      * <tt>ClassLoader.findClass()</tt> method. Additionally, the synchronized
141      * modifier was removed from the superclass method; this change was necessary
142      * because ClassLoader class assumes a tree of class loaders, but the class
143      * loading structure in the <tt>ModuleManager</tt> might actually be a graph
144      * of class loaders; thus, it was necessary to loosen the concurrency locking
145      * to allow for cycles.
146      * </p>
147      * @param name the class to be loaded.
148      * @param resolve flag indicating whether the class should be resolved or not.
149      * @return the loaded class.
150      * @throws java.lang.ClassNotFoundException if the class could not be loaded.
151     **/

152     protected Class JavaDoc loadClass(String JavaDoc name, boolean resolve)
153         throws ClassNotFoundException JavaDoc
154     {
155         // Make sure the class was not already loaded.
156
Class JavaDoc c = findLoadedClass(name);
157         if (c == null)
158         {
159             try
160             {
161                 // First, try to load from parent or the
162
// bootstrap loader if no parent.
163
if (getParent() != null)
164                 {
165                     c = getParent().loadClass(name);
166                 }
167                 // HACK ALERT: The following lines are commented out,
168
// because the findBootstrapClass0() method is private
169
// in ClassLoader. As a result, the parent of all
170
// ModuleClassLoaders is set to the ClassLoader.getSystemClassLoader()
171
// class loader, so it will always be called above, instead
172
// of needing to call the below method.
173
/*
174                 else
175                 {
176                     c = findBootstrapClass0(name);
177                 }
178 */

179
180             }
181             catch (ClassNotFoundException JavaDoc ex)
182             {
183                 // Try the search policy and then local findClass() if no class
184
// found in the parent or bootstrap loader.
185
if (m_mgr.getSearchPolicy() != null)
186                 {
187                     c = m_mgr.getSearchPolicy().findClass(m_module, name);
188                 }
189
190                 if (c == null)
191                 {
192                     c = findClass(name);
193                 }
194             }
195         }
196
197         if (resolve)
198         {
199             resolveClass(c);
200         }
201
202         return c;
203     }
204
205     /**
206      * <p>
207      * This method overriden from from <tt>ClassLoader</tt>.
208      * It is implemented such that it loads classes from the set of
209      * <tt>ResourceSource</tt>s from its associated module.
210      * </p>
211      * @param name the name of the resource to load.
212      * @return the loaded <tt>Class</tt> object.
213      * @throws java.lang.ClassNotFoundException if the class could not be loaded.
214     **/

215 // protected synchronized Class findClass(String name) throws ClassNotFoundException
216
protected Class JavaDoc findClass(String JavaDoc name) throws ClassNotFoundException JavaDoc
217     {
218         Class JavaDoc clazz = findLoadedClass(name);
219
220         if (clazz == null)
221         {
222             String JavaDoc actual = name.replace('.', '/') + ".class";
223             ResourceSource[] sources = m_module.getResourceSources();
224             for (int i = 0;
225                 (clazz == null) && (sources != null) && (i < sources.length);
226                 i++)
227             {
228                 byte[] bytes = sources[i].getBytes(actual);
229                 if (bytes != null)
230                 {
231                     // Get the code source URL for this class. For concurrency
232
// purposes, we are performing this call outside of the
233
// synchronized block below since we call out to application
234
// code, which might in turn need to call back into the
235
// module loader code. Because of this, it is better to
236
// not be holding any locks before making the call.
237
URL JavaDoc url = m_mgr.getURLPolicy().createCodeSourceURL(
238                         m_mgr, m_module);
239
240                     // If we have a valid code source URL, then use it to
241
// define the class for security purposes, otherwise
242
// define the class without a code source.
243
if (url != null)
244                     {
245                         CodeSource JavaDoc cs = new CodeSource JavaDoc(url, (Certificate JavaDoc[]) null);
246                         clazz = defineClass(name, bytes, 0, bytes.length, cs);
247                     }
248                     else
249                     {
250                         clazz = defineClass(name, bytes, 0, bytes.length);
251                     }
252                 }
253             }
254         }
255
256         if (clazz != null)
257         {
258             return clazz;
259         }
260
261         throw new ClassNotFoundException JavaDoc(name);
262     }
263
264     /**
265      * <p>
266      * This method is used by <tt>SearchPolicy</tt> instances when they want
267      * to load a class from a module. The search policy is initially invoked when
268      * <tt>ModuleClassLoader.loadClass()</tt> delegates a class loading
269      * request to it. In general, the ultimate goal of the search policy is to
270      * return a class from another module if possible. Unfortunately, if a search
271      * policy tries to directly load a class from another module's class loader, an
272      * infinite loop will result because the module's class loader will delegate the
273      * request back to the search policy. To avoid this situation, search policies
274      * must use this method when trying to load a class from a module.
275      * </p>
276      * @param name the name of the class to load.
277      * @return the loaded class or <tt>null</tt>.
278     **/

279     public Class JavaDoc searchForClass(String JavaDoc name)
280     {
281         try
282         {
283             return findClass(name);
284         } catch (Throwable JavaDoc th) {
285             // Not much we can do.
286
// TODO: Do something with this error message.
287
// System.err.println("ModuleClassLoader: " + th.getMessage());
288
}
289         return null;
290     }
291
292     /**
293      * <p>
294      * This method is nearly an exact copy of the ClassLoader.getResource()
295      * method. The main difference is that it delegates to its associated
296      * <tt>ModuleManager</tt>'s search policy before calling the
297      * <tt>ClassLoader.findResource()</tt> method.
298      * </p>
299      * @param name the class to be loaded.
300      * @return a URL to the resource or <tt>null</tt> if the resource was not found.
301     **/

302     public URL JavaDoc getResource(String JavaDoc name)
303     {
304         URL JavaDoc url = null;
305
306         // First try the parent class loader.
307
if (getParent() != null)
308         {
309             url = getParent().getResource(name);
310         }
311 // else
312
// {
313
// url = getBootstrapResource(name);
314
// }
315

316         // Then try the search policy.
317
if (url == null)
318         {
319             // Try the search policy if no resource is
320
// found in the parent or bootstrap loader.
321
if (m_mgr.getSearchPolicy() != null)
322             {
323                 try
324                 {
325                     url = m_mgr.getSearchPolicy().findResource(m_module, name);
326                 }
327                 catch (ResourceNotFoundException ex)
328                 {
329                     // We return null here because if SearchPolicy.findResource()
330
// throws an exception we interpret that to mean that the
331
// search should be stopped.
332
return null;
333                 }
334             }
335         }
336
337         // Finally, look locally.
338
if (url == null)
339         {
340             url = findResource(name);
341         }
342
343         return url;
344     }
345
346     /**
347      * <p>
348      * This method overriden from from <tt>ClassLoader</tt>.
349      * It is implemented such that it loads resources from the set of
350      * <tt>ResourceSource</tt>s from its associated module.
351      * </p>
352      * @param name the name of the resource to load.
353      * @return the <tt>URL</tt> associated with the resource or <tt>null</tt>.
354     **/

355     protected URL JavaDoc findResource(String JavaDoc name)
356     {
357         // Remove leading slash, if present.
358
if (name.startsWith("/"))
359         {
360             name = name.substring(1);
361         }
362
363         URL JavaDoc url = null;
364
365         // Try to load the resource from the module's resource
366
// sources.
367
if (url == null)
368         {
369             ResourceSource[] sources = m_module.getResourceSources();
370             for (int i = 0;
371                 (url == null) && (sources != null) && (i < sources.length);
372                 i++)
373             {
374                 if (sources[i].hasResource(name))
375                 {
376                     url = m_mgr.getURLPolicy().createResourceURL(m_mgr, m_module, i, name);
377                 }
378             }
379         }
380
381         return url;
382     }
383
384     /**
385      * <p>
386      * This method is used by <tt>SearchPolicy</tt> instances when they want
387      * to load a resource from a module. The search policy is initially invoked when
388      * <tt>ModuleClassLoader.loadClass()</tt> delegates a resource loading
389      * request to it. In general, the ultimate goal of the search policy is to
390      * return a resource from another module if possible. Unfortunately, if a search
391      * policy tries to directly load a resource from another module's class loader, an
392      * infinite loop will result because the module's class loader will delegate the
393      * request back to the search policy. To avoid this situation, search policies
394      * must use this method when trying to load a resource from a module.
395      * </p>
396      * @param name the name of the resource to load.
397      * @return a URL to the resource or <tt>null</tt>.
398     **/

399     public URL JavaDoc searchForResource(String JavaDoc name)
400     {
401         try
402         {
403             URL JavaDoc url = findResource(name);
404             return url;
405         }
406         catch (Throwable JavaDoc th)
407         {
408             // Ignore and just return null.
409
}
410         return null;
411     }
412
413     protected Enumeration JavaDoc findResources(String JavaDoc name)
414     {
415         Vector JavaDoc v = new Vector JavaDoc();
416
417         // Remove leading slash, if present.
418
if (name.startsWith("/"))
419         {
420             name = name.substring(1);
421         }
422
423         // Try to load the resource from the module's resource
424
// sources.
425

426         ResourceSource[] sources = m_module.getResourceSources();
427         for (int i = 0; (sources != null) && (i < sources.length); i++)
428         {
429             if (sources[i].hasResource(name))
430             {
431                 v.addElement(m_mgr.getURLPolicy().createResourceURL(m_mgr, m_module, i, name));
432             }
433         }
434
435         return v.elements();
436     }
437
438     /**
439      * <p>
440      * This method overriden from from <tt>ClassLoader</tt>. It maps a library
441      * name to a library path by consulting the <tt>LibrarySource</tt>s of the
442      * class loader's module.
443      * </p>
444      * @param name the name of the library to find.
445      * @return the file system path of library or <tt>null</tt>
446     **/

447     protected String JavaDoc findLibrary(String JavaDoc name)
448     {
449         // Remove leading slash, if present.
450
if (name.startsWith("/"))
451         {
452             name = name.substring(1);
453         }
454
455         LibrarySource[] sources = m_module.getLibrarySources();
456         for (int i = 0;
457             (sources != null) && (i < sources.length);
458             i++)
459         {
460             String JavaDoc path = sources[i].getPath(name);
461             if (path != null)
462             {
463                 return path;
464             }
465         }
466
467         return null;
468     }
469 }
Popular Tags