KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tomcat > util > loader > ModuleClassLoader


1 /*
2  * Copyright 1999,2004 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
18 package org.apache.tomcat.util.loader;
19
20 import java.io.File JavaDoc;
21 import java.io.IOException JavaDoc;
22 import java.net.MalformedURLException JavaDoc;
23 import java.net.URL JavaDoc;
24 import java.net.URLClassLoader JavaDoc;
25 import java.util.Enumeration JavaDoc;
26 import java.util.Vector JavaDoc;
27
28 /*
29  * Initially, I started with WebappClassLoader attempting to clean up and
30  * refactor it. Because of complexity and very weird ( and likely buggy )
31  * behavior, I moved the other way, starting with URLClassLoader and adding
32  * the functionality from WebappClassLoader.
33  *
34  * The current version has a lot of unimplemented WebappClassLoader features and
35  * TODOs - all of them are needed in order to support a single/consistent loader
36  * for webapps and server/modules.
37  *
38  * - all ordering options and tricks
39  * - local loading - in case it can be made more efficient than URLCL
40  * - hook to plugin JNDI finder
41  * - ability to add extra permissions to loaded classes
42  * - ability to use work dir for anti-jar locking and generated classes ( incl getURLs)
43  *
44  *
45  * Things better kept out:
46  * - negative cache - it'll waste space with little benefit, most not found classes
47  * will not be asked multiple times, and most will be in other loaders
48  * - binaryContent cache - it's unlikely same resource will be loaded multiple
49  * times, and some may be large
50  *
51  */

52
53 /**
54  * Simple module class loader. Will search the repository if the class is not
55  * found locally.
56  *
57  * TODO: findResources() - merge all responses from the repo and parent.
58  *
59  * Based on StandardClassLoader and WebappClassLoader.
60  *
61  * @author Costin Manolache
62  * @author Remy Maucherat
63  * @author Craig R. McClanahan
64  */

65 public class ModuleClassLoader
66     extends URLClassLoader JavaDoc
67 {
68     // Don't use commons logging or configs to debug loading - logging is dependent
69
// on loaders and drags a lot of stuff in the classpath
70
//
71
private static final boolean DEBUG=false; //LoaderProperties.getProperty("loader.ModuleClassLoader.debug") != null;
72
private static final boolean DEBUGNF=false;//LoaderProperties.getProperty("loader.ModuleClassLoader.debugNF") != null;
73

74     // ----------------------------------------------------------- Constructors
75

76
77     public ModuleClassLoader(URL JavaDoc repositories[], ClassLoader JavaDoc parent) {
78         super(repositories, parent);
79     }
80     
81
82     public ModuleClassLoader(URL JavaDoc repositories[]) {
83         super(repositories);
84     }
85
86
87     // ----------------------------------------------------- Instance Variables
88

89     protected Repository repository;
90
91     /**
92      * Should this class loader delegate to the parent class loader
93      * <strong>before</strong> searching its own repositories (i.e. the
94      * usual Java2 delegation model)? If set to <code>false</code>,
95      * this class loader will search its own repositories first, and
96      * delegate to the parent only if the class or resource is not
97      * found locally.
98      */

99     protected boolean delegate = false;
100
101     /**
102      * Last time a JAR was accessed.
103      * TODO: change to last time the loader was accessed
104      */

105     protected long lastJarAccessed = 0L;
106
107     /**
108      * Has this component been started?
109      */

110     protected boolean started = false;
111
112     protected Module module;
113
114     // ------------------------------------------------------------- Properties
115

116
117     /**
118      * Return the "delegate first" flag for this class loader.
119      */

120 // boolean getDelegate() {
121
//
122
// return (this.delegate);
123
//
124
// }
125

126
127     /**
128      * Set the "delegate first" flag for this class loader.
129      *
130      * @param delegate The new "delegate first" flag
131      */

132     void setDelegate(boolean delegate) {
133         this.delegate = delegate;
134     }
135
136     void setRepository(Repository lg ) {
137         this.repository=lg;
138     }
139
140     void setModule(Module webappLoader) {
141         this.module=webappLoader;
142     }
143
144     /** Not public - his can only be called from package.
145      * To get the module from a ClassLoader you need access to the Loader
146      * instance.
147      *
148      * @return
149      */

150     Module getModule() {
151         return module;
152     }
153
154     void setWorkDir(File JavaDoc s) {
155         // TODO
156
}
157
158     /**
159      * Add a new repository to the set of places this ClassLoader can look for
160      * classes to be loaded.
161      *
162      * @param repository Name of a source of classes to be loaded, such as a
163      * directory pathname, a JAR file pathname, or a ZIP file pathname
164      *
165      * @exception IllegalArgumentException if the specified repository is
166      * invalid or does not exist
167      */

168     void addRepository(String JavaDoc repository) {
169         // Add this repository to our underlying class loader
170
try {
171             URL JavaDoc url = new URL JavaDoc(repository);
172             super.addURL(url);
173         } catch (MalformedURLException JavaDoc e) {
174             IllegalArgumentException JavaDoc iae = new IllegalArgumentException JavaDoc
175                 ("Invalid repository: " + repository);
176             iae.initCause(e);
177             //jdkCompat.chainException(iae, e);
178
throw iae;
179         }
180     }
181
182     /**
183      * Have one or more classes or resources been modified so that a reload
184      * is appropriate?
185      *
186      * Not public - call it via Module
187      */

188     boolean modified() {
189         if (DEBUG)
190             log("modified() false");
191
192         // TODO - check at least the jars
193
return (false);
194     }
195
196     // ---------------------------------------------------- ClassLoader Methods
197

198
199     /**
200      * Find the specified class in our local repositories, if possible. If
201      * not found, throw <code>ClassNotFoundException</code>.
202      *
203      * @param name Name of the class to be loaded
204      *
205      * @exception ClassNotFoundException if the class was not found
206      */

207     public Class JavaDoc findClass(String JavaDoc name) throws ClassNotFoundException JavaDoc {
208
209         Class JavaDoc clazz = null;
210             
211         try {
212             clazz = super.findClass(name);
213         } catch (RuntimeException JavaDoc e) {
214             if (DEBUG)
215                 log("findClass() -->RuntimeException " + name, e);
216             throw e;
217         } catch( ClassNotFoundException JavaDoc ex ) {
218             if (DEBUGNF)
219                 log("findClass() NOTFOUND " + name);
220             throw ex;
221         }
222             
223         if (clazz == null) { // does it ever happen ?
224
if (DEBUGNF)
225                 log("findClass() NOTFOUND throw CNFE " + name);
226             throw new ClassNotFoundException JavaDoc(name);
227         }
228
229         // Return the class we have located
230
if (DEBUG) {
231             if( clazz.getClassLoader() != this )
232                 log("findClass() FOUND " + clazz + " Loaded by " + clazz.getClassLoader());
233             else
234                 log("findClass() FOUND " + clazz );
235         }
236         return (clazz);
237     }
238     
239     /** Same as findClass, but also checks if the class has been previously
240      * loaded.
241      *
242      * In most implementations, findClass() doesn't check with findLoadedClass().
243      * In order to implement repository, we need to ask each loader in the group
244      * to load only from it's local resources - however this will lead to errors
245      * ( duplicated definition ) if findClass() is used.
246      *
247      * @param name
248      * @return
249      * @throws ClassNotFoundException
250      */

251     public Class JavaDoc findLocalClass(String JavaDoc name) throws ClassNotFoundException JavaDoc
252     {
253         Class JavaDoc clazz = findLoadedClass(name);
254         if (clazz != null) {
255             if (DEBUG)
256                 log("findLocalClass() - FOUND " + name);
257             return (clazz);
258         }
259         
260         return findClass(name);
261     }
262
263
264
265     
266     /**
267      * Find the specified resource in our local repository, and return a
268      * <code>URL</code> refering to it, or <code>null</code> if this resource
269      * cannot be found.
270      *
271      * @param name Name of the resource to be found
272      */

273     public URL JavaDoc findResource(final String JavaDoc name) {
274
275         URL JavaDoc url = null;
276
277         url = super.findResource(name);
278         
279         if(url==null) {
280             // try the repository
281
// TODO
282
}
283         
284         if (url==null && DEBUG) {
285             if (DEBUGNF) log("findResource() NOTFOUND " + name );
286             return null;
287         }
288
289         if (DEBUG) log("findResource() found " + name + " " + url );
290         return (url);
291     }
292
293
294     /**
295      * Return an enumeration of <code>URLs</code> representing all of the
296      * resources with the given name. If no resources with this name are
297      * found, return an empty enumeration.
298      *
299      * @param name Name of the resources to be found
300      *
301      * @exception IOException if an input/output error occurs
302      */

303     public Enumeration JavaDoc findResources(String JavaDoc name) throws IOException JavaDoc {
304         Vector JavaDoc result=new Vector JavaDoc();
305         
306         Enumeration JavaDoc myRes=super.findResources(name);
307         if( myRes!=null ) {
308             while( myRes.hasMoreElements() ) {
309                 result.addElement(myRes.nextElement());
310             }
311         }
312         // TODO: same in repository ( parent ? )
313

314         return result.elements();
315
316     }
317
318     // Next methods implement the search alghoritm - parent, repo, delegation, etc
319

320     /** getResource() - modified to implement the search alghoritm
321      *
322      */

323     public URL JavaDoc getResource(String JavaDoc name) {
324
325         URL JavaDoc url = null;
326
327         // (1) Delegate to parent if requested
328
if (delegate) {
329             url=getResourceParentDelegate(name);
330             if(url!=null ) return url;
331         }
332
333         // (2) Search local repositories
334
url = findResource(name);
335         if (url != null) {
336             // TODO: antijar locking - WebappClassLoader is making a copy ( is it ??)
337
if (DEBUG)
338                 log("getResource() found locally " + delegate + " " + name + " " + url);
339             return (url);
340         }
341
342         // Finally, try the group loaders ( via super() in StandardClassLoader ).
343
// not found using normal loading mechanism. load from one of the classes in the group
344
if( repository!=null ) {
345             url=repository.findResource(this, name);
346             if(url!=null ) {
347                 if( DEBUG )
348                     log("getResource() FOUND from group " + repository.getName() + " " + name + " " + url);
349                 return url;
350             }
351         }
352
353         // (3) Delegate to parent unconditionally if not already attempted
354
if( !delegate ) {
355             url=getResourceParentDelegate(name);
356             if(url!=null ) return url;
357         }
358
359         
360         // (4) Resource was not found
361
if (DEBUGNF)
362             log("getResource() NOTFOUND " + delegate + " " + name + " " + url);
363         return (null);
364
365     }
366
367     // to avoid duplication
368
private URL JavaDoc getResourceParentDelegate(String JavaDoc name) {
369         URL JavaDoc url=null;
370         ClassLoader JavaDoc loader = getParent();
371         
372         if (loader == null) {
373             loader = getSystemClassLoader();
374             if (url != null) {
375                 if (DEBUG)
376                     log("getResource() found by system " + delegate + " " + name + " " + url);
377                 return (url);
378             }
379         } else {
380             url = loader.getResource(name);
381             if (url != null) {
382                 if (DEBUG)
383                     log("getResource() found by parent " + delegate + " " + name + " " + url);
384                 return (url);
385             }
386         }
387
388         return url;
389     }
390     
391     /**
392      * Load the class with the specified name, searching using the following
393      * algorithm until it finds and returns the class. If the class cannot
394      * be found, returns <code>ClassNotFoundException</code>.
395      * <ul>
396      * <li>Call <code>findLoadedClass(String)</code> to check if the
397      * class has already been loaded. If it has, the same
398      * <code>Class</code> object is returned.</li>
399      * <li>If the <code>delegate</code> property is set to <code>true</code>,
400      * call the <code>loadClass()</code> method of the parent class
401      * loader, if any.</li>
402      * <li>Call <code>findClass()</code> to find this class in our locally
403      * defined repositories.</li>
404      * <li>Call the <code>loadClass()</code> method of our parent
405      * class loader, if any.</li>
406      * </ul>
407      * If the class was found using the above steps, and the
408      * <code>resolve</code> flag is <code>true</code>, this method will then
409      * call <code>resolveClass(Class)</code> on the resulting Class object.
410      *
411      * @param name Name of the class to be loaded
412      * @param resolve If <code>true</code> then resolve the class
413      *
414      * @exception ClassNotFoundException if the class was not found
415      */

416     public Class JavaDoc loadClass(String JavaDoc name, boolean resolve)
417         throws ClassNotFoundException JavaDoc
418     {
419
420         Class JavaDoc clazz = null;
421
422         // Don't load classes if class loader is stopped
423
if (!started) {
424             //log("Not started " + this + " " + module);
425
throw new ThreadDeath JavaDoc();
426         }
427
428         // (0) Check our previously loaded local class cache
429
clazz = findLoadedClass(name);
430         if (clazz != null) {
431             if (DEBUG)
432                 log("loadClass() FOUND findLoadedClass " + name + " , " + resolve);
433             if (resolve) resolveClass(clazz);
434             return (clazz);
435         }
436
437         // (0.2) Try loading the class with the system class loader, to prevent
438
// the webapp from overriding J2SE classes
439
try {
440             clazz = getSystemClassLoader().loadClass(name);
441             if (clazz != null) {
442                 // enabling this can result in ClassCircularityException
443
// if (DEBUG)
444
// log("loadClass() FOUND system " + name + " , " + resolve);
445
if (resolve) resolveClass(clazz);
446                 return (clazz);
447             }
448         } catch (ClassNotFoundException JavaDoc e) {
449             // Ignore
450
}
451
452         // TODO: delegate based on filter
453
boolean delegateLoad = delegate;// || filter(name);
454

455         // (1) Delegate to our parent if requested
456
if (delegateLoad) {
457
458             ClassLoader JavaDoc loader = getParent();
459             if( loader != null ) {
460                 try {
461                     clazz = loader.loadClass(name);
462                     if (clazz != null) {
463                         if (DEBUG)
464                             log("loadClass() FOUND by parent " + delegate + " " + name + " , " + resolve);
465                         if (resolve)
466                             resolveClass(clazz);
467                         return (clazz);
468                     }
469                 } catch (ClassNotFoundException JavaDoc e) {
470                     ;
471                 }
472             }
473         }
474
475         // (2) Search local repositories
476
try {
477             clazz = findClass(name);
478             if (clazz != null) {
479 // if (DEBUG)
480
// log("loadClass - FOUND findClass " + delegate + " " + name + " , " + resolve);
481
if (resolve) resolveClass(clazz);
482                 return (clazz);
483             }
484         } catch (ClassNotFoundException JavaDoc e) {
485             ;
486         }
487
488         // Finally, try the group loaders ( via super() in StandardClassLoader ).
489
// not found using normal loading mechanism. load from one of the classes in the group
490
if( repository!=null ) {
491             Class JavaDoc cls=repository.findClass(this, name);
492             if(cls!=null ) {
493                 if( DEBUG )
494                     log("loadClass(): FOUND from group " + repository.getName() + " " + name);
495                 if (resolve) resolveClass(clazz);
496                 return cls;
497             }
498         }
499
500         // (3) Delegate to parent unconditionally
501
if (!delegateLoad) {
502             ClassLoader JavaDoc loader = getParent();
503             if( loader != null ) {
504                 try {
505                     clazz = loader.loadClass(name);
506                     if (clazz != null) {
507                         if (DEBUG)
508                             log("loadClass() FOUND parent " + delegate + " " + name + " , " + resolve);
509                         if (resolve) resolveClass(clazz);
510                         return (clazz);
511                     }
512                 } catch (ClassNotFoundException JavaDoc e) {
513                     ;
514                 }
515             }
516         }
517
518         if( DEBUGNF ) log("loadClass(): NOTFOUND " + name );
519         throw new ClassNotFoundException JavaDoc(name);
520     }
521
522
523     // ------------------------------------------------------ Lifecycle Methods
524

525
526
527     /**
528      * Start the class loader.
529      *
530      * @exception LifecycleException if a lifecycle error occurs
531      */

532     void start() {
533
534         started = true;
535
536     }
537
538     /** Support for "disabled" state.
539     *
540     * @return
541     */

542     boolean isStarted() {
543         return started;
544     }
545
546
547     /**
548      * Stop the class loader.
549      *
550      * @exception LifecycleException if a lifecycle error occurs
551      */

552     void stop() {
553
554         started = false;
555
556     }
557
558
559
560
561     /**
562      * Validate a classname. As per SRV.9.7.2, we must restict loading of
563      * classes from J2SE (java.*) and classes of the servlet API
564      * (javax.servlet.*). That should enhance robustness and prevent a number
565      * of user error (where an older version of servlet.jar would be present
566      * in /WEB-INF/lib).
567      *
568      * @param name class name
569      * @return true if the name is valid
570      */

571     protected boolean validate(String JavaDoc name) {
572
573         if (name == null)
574             return false;
575         if (name.startsWith("java."))
576             return false;
577
578         return true;
579
580     }
581
582
583     // ------------------ Local methods ------------------------
584

585     private void log(String JavaDoc s ) {
586         System.err.println("ModuleCL: " + s);
587     }
588     private void log(String JavaDoc s, Throwable JavaDoc t ) {
589         System.err.println("ModuleCL: " + s);
590         t.printStackTrace();
591     }
592     
593     Object JavaDoc debugObj=new Object JavaDoc();
594
595     /**
596      * Render a String representation of this object.
597      */

598     public String JavaDoc toString() {
599
600         StringBuffer JavaDoc sb = new StringBuffer JavaDoc("ModuleCL ");
601         sb.append(debugObj).append(" delegate: ");
602         sb.append(delegate);
603         //sb.append("\r\n");
604
sb.append(" cp: ");
605         URL JavaDoc cp[]=super.getURLs();
606         if (cp != null ) {
607             for (int i = 0; i <cp.length; i++) {
608                 sb.append(" ");
609                 sb.append(cp[i].getFile());
610             }
611         }
612         if (getParent() != null) {
613             sb.append("\r\n----------> Parent: ");
614             sb.append(getParent().toString());
615             sb.append("\r\n");
616         }
617         return (sb.toString());
618     }
619 }
620
621
Popular Tags