KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > lutris > classloader > MultiClassLoader


1 /*
2  * Enhydra Java Application Server Project
3  *
4  * The contents of this file are subject to the Enhydra Public License
5  * Version 1.1 (the "License"); you may not use this file except in
6  * compliance with the License. You may obtain a copy of the License on
7  * the Enhydra web site ( http://www.enhydra.org/ ).
8  *
9  * Software distributed under the License is distributed on an "AS IS"
10  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
11  * the License for the specific terms governing rights and limitations
12  * under the License.
13  *
14  * The Initial Developer of the Enhydra Application Server is Lutris
15  * Technologies, Inc. The Enhydra Application Server and portions created
16  * by Lutris Technologies, Inc. are Copyright Lutris Technologies, Inc.
17  * All Rights Reserved.
18  *
19  * Contributor(s):
20  *
21  * $Id: MultiClassLoader.java,v 1.3 2005/03/24 10:51:25 slobodan Exp $
22  *
23  * formatted with JxBeauty (c) johann.langhofer@nextra.at
24  */

25
26
27 package com.lutris.classloader;
28
29 import java.io.ByteArrayOutputStream JavaDoc;
30 import java.io.File JavaDoc;
31 import java.io.FileNotFoundException JavaDoc;
32 import java.io.IOException JavaDoc;
33 import java.io.InputStream JavaDoc;
34 import java.net.MalformedURLException JavaDoc;
35 import java.net.URL JavaDoc;
36 import java.util.Enumeration JavaDoc;
37 import java.util.Hashtable JavaDoc;
38 import java.util.StringTokenizer JavaDoc;
39 import java.util.Vector JavaDoc;
40
41 import com.lutris.logging.LogChannel;
42
43 /**
44  * <P><B>Summary:</B>
45  * <P>A class loader that can load classes from
46  * class files and zip files residing in a specified class path.
47  *
48  * <P>This class loader can also load resources that reside on the
49  * class path and return them as is, as input streams, or as byte arrays.
50  *
51  * <P><B>Features:</B>
52  * <UL>
53  * <LI>Loads classes from class files and zip files.
54  * <LI>Finds resources and can return them as is, as input streams and as byte arrays.
55  * <LI>Loads classes and resources using the specified class path. Class
56  * path entries can be URLs, directories, zip files or jar files.
57  * <P> A secondary classloader can be specified which is used to locate
58  * the class if its not found by this classloader.
59  * <LI> A filter object can be supplied to determine if the current class
60  * loader should load the class or it should be passed to the parent or
61  * secondary class loader.
62  * </UL>
63  *
64  * <H3>Class Path</H3>
65  *
66  * <P>If classes are to be loaded from a class path, it must be set by the
67  * <CODE>setClassPath</CODE> or <CODE>addClassPath</CODE> methods or by
68  * the constructor prior to calling <CODE>loadClass</CODE>. If the class
69  * path is not set, the class will be loaded with the system class loader.
70  *
71  * <P>The class path can consist of directories, files, and/or URLs.
72  * <P>Example valid class path entries are:
73  * <PRE>
74  * <EM>Files and directories on the local file system</EM>
75  * ../../java/classes
76  * /users/kristen/java/classes
77  * /users/kristen/java/classes/
78  * /users/kristen/java/zipfiles/MyClasses.zip
79  * /users/kristen/java/jarfiles/MyClasses.jar
80  * file:///users/kristen/java/classes
81  * file://localhost/users/kristen/java/classes
82  * <BR>
83  * <EM>Files and directories on a remote file system
84  * (must be in URL format)</EM>
85  * ftp://www.foo.com/pub/java/classes
86  * file://www.foo.com/pub/java/classes/
87  * http://www.foo.com/web/java/classes/
88  * file://www.foo.com:8080/pub/java/zipfiles/MyClasses.zip
89  * http://www.foo.com:8080/web/java/jarfiles/MyClasses.jar
90  * </PRE>
91  *
92  * <P>Note that the location of the entry includes the protocol, the host name,
93  * and the port while the file name is everything else. For example,
94  *
95  * <PRE>
96  * http://www.foo.com:8080/web/java/jarfiles/MyClasses.jar
97  * </PRE>
98  * has the form [location][name] or
99  * <PRE>
100  * [http://www.foo.com:8080/][/web/java/jarfiles/MyClasses.jar]
101  * </PRE>
102  * so the location is "http://www.foo.com:8080/" and the name is
103  * "/web/java/jarfiles/MyClasses.jar".
104  *
105  * <P>Note that the two references
106  * <PRE>
107  * /users/kristen/java/classes/
108  * file:///users/kristen/java/classes/
109  * </PRE>
110  * represent the same directory on a Unix machine, and
111  * <PRE>
112  * C|/windows/java/classes/
113  * file:///C|/windows/java/classes/
114  * </PRE>
115  * are equivalent directories on a Windows box.
116  *
117  * <P>But the two references
118  * <PRE>
119  * /users/kristen/java/classes/
120  * file://monet.lutris.com/users/kristen/java/classes/
121  * </PRE>
122  * are not equivalent even if the directory
123  * <EM>/users/kristen/java/classes/</EM> lives
124  * on the machine named <EM>monet.lutris.com</EM> and all development
125  * is on this machine. Why? Because the web (browser?) protocol is different
126  * for URLs with host information and those without. If no host is
127  * specified, the file is assumed to be on the local machine and the
128  * path is determined from the <EM>ROOT</EM> of the machine. If the
129  * host is specified, then the <EM>ftp protocol</EM> is used and the path
130  * is determined from the <EM>ftp ROOT</EM> (e.g. /users/ftp/) rather
131  * than the machine's ROOT. Thus, on a machine that support's anonymous
132  * ftp, the following two directories are the same:
133  * <PRE>
134  * /users/ftp/pub/classes/
135  * file://picasso.lutris.com/pub/classes/
136  * </PRE>
137  * assuming the development is being done on <EM>picasso.lutris.com</EM>.
138  *
139  * <H3>System Class Path</H3>
140  *
141  * <P>The system class path is the system-dependent path of directories
142  * and files (e.g. CLASSPATH on Unix and Windows) used by the system
143  * class loader to load classes. This class path is usually configured
144  * prior to executing a Java program but can be dynamically configured
145  * during runtime if desired. If you want to use the system class path
146  * for this class loader, the convenience method <CODE>getSystemClassPath</CODE>
147  * has been provided.
148  *
149  * <P>Valid system class path entries are
150  * directories and zip files, specified by absolute path or relative path
151  * on the system. Any valid system class path entry is also valid
152  * for this class loader.
153  *
154  * <H3>Example</H3>
155  *
156  * <P>Here is an example of how to use this class loader:
157  * <PRE>
158  * MultiClassLoader loader = new MultiClassLoader();
159  * loader.setClassPath("/web/java/lutris.jar");
160  * loader.addClassPath("/users/kristen/java/");
161  * loader.addClassPath("/usr/local/lib/graphics.zip");
162  * try {
163  * Class c = loader.loadClass("com.lutris.util.MyClass");
164  * System.out.println("My loader is " + c.getClassLoader());
165  * Object o = (Object) c.newInstance();
166  * System.out.println("My class is " + o.getClass());
167  * } catch (ClassNotFoundException e) {
168  * Throwable t = new Throwable();
169  * t.printStackTrace();
170  * }
171  * </PRE>
172  *
173  * <P>
174  * <B>Warning:</B> This class loader is not yet fully compliant with Java 1.2. It
175  * maybe used on 1.2, but not all features are available. The <em>parent</em> loader,
176  * <em>secondary</em> loader, and <em>filter</em> may change in a future release without
177  * maintaining compatibility.
178  *
179  * <!--
180  * FIXME:
181  * This class needs revisited for JDK 1.2, including adding security
182  * managers. Should implement the find* methods, but probably
183  * must have its own loadClass. Need to have resource loading
184  * follow same paradigm as class loading.
185  *
186  * -->
187  *
188  * @author Kristen Pol, Lutris Technologies
189  * @version $Revision : 1.0 $
190  * @see java.lang.ClassLoader
191  * @see com.lutris.classloader.Resource
192  * @see java.net.URL
193  */

194 public class MultiClassLoader extends ClassLoader JavaDoc {
195
196   /**
197    * A filter interface, used in deciding if a class should be loaded
198    * by this class loader.
199    */

200   public interface ClassFilter {
201     /**
202      * Value returned to indicate that we don't care, other filters
203      * maybe checked.
204      */

205     public static final int NORMAL_LOAD = 1;
206     /**
207      * Value returned to indicate that the class should not be loaded
208      * by this class loader. Normal delation/secondary class loader
209      * checks will be done.
210      */

211     public static final int DONT_LOAD = 2;
212     /**
213      * Value returned to indicate that the class can be loaded
214      * by this class loader. If it is not loaded, it will be
215      * passed to the secondary class loader. The delegate
216      * is not check, since it is check before this class loader.
217      */

218     public static final int CAN_LOAD = 3;
219     /**
220      * Value returned to indicate that the class must loaded
221      * by this class loader. If it is not loaded, not other
222      * class loader will be checked.
223      */

224     public static final int MUST_LOAD = 4;
225
226     /**
227      * Check if a class should be loaded by this class loader.
228      * @param className the class being loaded.
229      * @return One of NORMAL_LOAD, DONT_LOAD, CAN_LOAD, or MUST_LOAD.
230      */

231     public int loadCheck(String JavaDoc className);
232   }
233
234   /**
235    * Information kept about a loaded class.
236    */

237   public class ClassResource {
238
239     /**
240      * Class object
241      */

242     private Class JavaDoc classObj;
243
244     /**
245      * Resource were the class was obtained.
246      */

247     private Resource resource;
248
249     /**
250      * Constructor.
251      */

252     public ClassResource(Class JavaDoc classObj, Resource resource) {
253       this.classObj = classObj;
254       this.resource = resource;
255     }
256
257     /**
258      * Get the class object.
259      */

260     public Class JavaDoc getClassObj() {
261       return classObj;
262     }
263
264     /**
265      * Get the resource. Maybe null if the class was not loaded by
266      * a MultiClassLoader.
267      */

268     public Resource getResource() {
269       return resource;
270     }
271   }
272
273   /**
274    * The ClassResource objects for classes loaded by this classloader
275    * (but not those handled by the parent).
276    */

277   private Hashtable JavaDoc loadedClasses = new Hashtable JavaDoc();
278
279   /**
280    * The class path to check when loading classes.
281    */

282   private ClassPath classPath;
283
284   /**
285    * Log level symbolic name
286    */

287   public static final String JavaDoc LOG_LEVEL = "CLASSLOAD";
288
289   /**
290    * Is logging enabled?
291    */

292   private boolean loggingEnabled = false;
293
294   /**
295    * Log channel to write messages to; maybe null.
296    */

297   private LogChannel logChannel;
298
299   /**
300    * Numeric log level number for LOG_LEVEL string
301    */

302   private int logLevel;
303
304   /**
305    * If any class filters are supplied, this is a non-null list of
306    * filters to check.
307    */

308   private Vector JavaDoc filters = null;
309
310   /**
311    * Parent class loader, or null if there isn't one.
312    */

313   private ClassLoader JavaDoc parentClassLoader;
314
315   /**
316    * Secondary class loader, or null if there isn't one.
317    */

318   private ClassLoader JavaDoc secondaryClassLoader;
319
320   /**
321    * Flag which indicates that using of secondary ClassLoader will be forced.
322    * If this flag is true the MultiClassLoader is used as utility wrapper class
323    * for passed secondary ClassLoader.
324    */

325   private boolean forceSecondaryLoader = false; //vr 28.06.2004
326

327   /**
328    * Flag which indicates using of MultiClassLoader with AutoReload option.
329    */

330   private boolean isAutoReload = false; //vr 30.06.2004
331

332   /**
333    * Constructs class loader with no initial class path and a
334    * specified parent class loader.
335    * @param parent The parent class loader for delegation,
336    * or null if no parent is defined.
337    * @param secondary The secondary class loader.
338    * Use <code>getSysClassLoader</code> to get the system class loader
339    * to specify as the secondary.
340    * @param loadLogChannel The log channel, maybe null.
341    * @see getSysClassLoader
342    */

343   public MultiClassLoader(ClassLoader JavaDoc parent, ClassLoader JavaDoc secondary, LogChannel loadLogChannel) {
344     classPath = new ClassPath(loadLogChannel);
345 // v. puskas, 10.03.2003 native compile changes;
346
// parentClassLoader = parent;
347
parentClassLoader = (null != parent) ? parent : getSystemClassLoader();
348     secondaryClassLoader = secondary;
349     logChannel = loadLogChannel;
350     if (logChannel != null) {
351       logLevel = logChannel.getLevel(LOG_LEVEL);
352       loggingEnabled = logChannel.isEnabled(logLevel);
353     }
354
355   }
356
357   /**
358    * Constructs class loader with no initial class path and the
359    * system class loader as the secondary.
360    * @param loadLogChannel The log channel, maybe null.
361    */

362   public MultiClassLoader(LogChannel loadLogChannel) {
363     this(null, getSystemClassLoader(), loadLogChannel);
364   }
365
366   /**
367    * Constructs class loader with specified class path. The parameter is
368    * assumed to be either a directory, URL, or zip file.
369    * @param path The class path represented by a String.
370    * @param loadLogChannel The log channel, maybe null.
371    */

372   public MultiClassLoader(String JavaDoc path, LogChannel loadLogChannel) {
373     this(new String JavaDoc[] {path}, loadLogChannel);
374   }
375
376   /**
377    * Constructs class loader with specified class path. The parameter is
378    * assumed to be an array of directories, URLs, and/or zip files.
379    * @param path The class path represented by a String array.
380    * @param loadLogChannel The log channel, maybe null.
381    */

382   public MultiClassLoader(String JavaDoc[] path, LogChannel loadLogChannel) {
383     this(loadLogChannel);
384     setClassPath(path);
385   }
386
387   /**
388    * Constructs class loader with specified class path. The parameter is
389    * assumed to be either a zip file or directory.
390    * @param path The class path represented by a File.
391    * @param loadLogChannel The log channel, maybe null.
392    */

393   public MultiClassLoader(File JavaDoc path, LogChannel loadLogChannel) {
394     this(new File JavaDoc[] {path}
395          , loadLogChannel);
396   }
397
398   /**
399    * Constructs class loader with specified class path. The parameter is
400    * assumed to be an array of zip files and/or directories.
401    * @param path The class path represented by a File array.
402    * @param loadLogChannel The log channel, maybe null.
403    */

404   public MultiClassLoader(File JavaDoc[] path, LogChannel loadLogChannel) {
405     this(loadLogChannel);
406     setClassPath(path);
407   }
408
409   /**
410    * Constructs class loader with specified class path. The parameter is
411    * represent a directory or zip file on the local machine or a
412    * remote machine.
413    * @param path The class path represented by a URL.
414    * @param loadLogChannel The log channel, maybe null.
415    */

416   public MultiClassLoader(URL JavaDoc path, LogChannel loadLogChannel) {
417     this(new URL JavaDoc[] {path}
418          , loadLogChannel);
419   }
420
421   /**
422    * Constructs class loader with specified class path. The parameter is
423    * represent directories and/or zip files on the local machine and/or
424    * on remote machines.
425    * @param path The class path represented by a URL array.
426    * @param loadLogChannel The log channel, maybe null.
427    */

428   public MultiClassLoader(URL JavaDoc[] path, LogChannel loadLogChannel) {
429     this(loadLogChannel);
430     setClassPath(path);
431   }
432
433   /**
434    * Sets flag to indicate forsing of Secondary ClassLoader usage or not.
435    * @param force true when the MultiClassLoader is used as utility wrapper
436    * class for passed secondary ClassLoader. All resource loads will be forced
437    * via passed Secondary ClassLoader. If parameter is set to false, then the
438    * MultyClassLoader will work in standard maner.
439    */

440   public void forceSecondaryLoader(boolean force) { //vr 28.06.2004
441
this.forceSecondaryLoader = force;
442   }
443
444   /**
445    * Sets flag to enable Auto Reloading possibility when Secondary ClassLoader
446    * usage is forced (refer to forceSecondaryLoader() method for more help).
447    * This flag has no efect when MultiClassLoader is used in standard maner (as
448    * realy ClassLoader, not only as wrapper class for external ClassLoader)
449    * @param enable true when the AutoReloading should be used.
450    */

451   public void enableAutoReloadForSecLoader(boolean enable) { //vr 30.06.2004
452
this.isAutoReload = enable;
453   }
454
455   /**
456    * Removes stored data about already loaded classes
457    */

458   public void removeLoadedClass() { //vr 28.06.2004
459
this.loadedClasses = new Hashtable JavaDoc();
460   }
461
462   /**
463    * Sets class loader with specified class path. The parameter is
464    * assumed to be either a directory, URL, or zip file.
465    * @param path The class path to be used when loading classes.
466    */

467   public void setClassPath(String JavaDoc path) {
468     setClassPath(new String JavaDoc[] {path});
469   }
470
471   /**
472    * Sets class loader with specified class path. The parameter is
473    * assumed to be an array of directories, URLs, and/or zip files.
474    * @param path The class path to be used when loading classes.
475    */

476   public synchronized void setClassPath(String JavaDoc[] path) {
477     classPath.set(path);
478   }
479
480   /**
481    * Sets class loader with specified class path. The parameter is
482    * assumed to be either a zip file or directory.
483    * @param path The class path to be used when loading classes.
484    */

485   public void setClassPath(File JavaDoc path) {
486     setClassPath(new File JavaDoc[] {path});
487   }
488
489   /**
490    * Sets class loader with specified class path. The parameter is
491    * assumed to be an array of zip files and/or directories.
492    * @param path The class path to be used when loading classes.
493    */

494   public synchronized void setClassPath(File JavaDoc[] path) {
495     classPath.set(path);
496   }
497
498   /**
499    * Sets class loader with specified class path. The parameter is
500    * represent a directory or zip file on the local machine or a
501    * remote machine.
502    * @param path The class path to be used when loading classes.
503    */

504   public void setClassPath(URL JavaDoc path) {
505     setClassPath(new URL JavaDoc[] {path});
506   }
507
508   /**
509    * Sets class loader with specified class path. The parameter is
510    * represent directories and/or zip files on the local machine and/or
511    * on remote machines.
512    * @param path The class path to be used when loading classes.
513    */

514   public synchronized void setClassPath(URL JavaDoc[] path) {
515     classPath.set(path);
516   }
517
518   /**
519    * Adds specified class path to beginning of existing path.
520    * The parameter is
521    * assumed to be either a directory, URL, or zip file.
522    * @param path The class path to be added to current class path.
523    */

524   public void addClassPath(String JavaDoc path) {
525     addClassPath(new String JavaDoc[] {path});
526   }
527
528   /**
529    * Adds specified class path to beginning of existing path.
530    * The parameter is
531    * assumed to be an array of directories, URLs, and/or zip files.
532    * @param path The class path to be added to current class path.
533    */

534   public synchronized void addClassPath(String JavaDoc[] path) {
535     classPath.add(path);
536   }
537
538   /**
539    * Adds specified class path to beginning of existing path.
540    * The parameter is assumed to be either a zip file or directory.
541    * @param path The class path to be added to current class path.
542    */

543   public void addClassPath(File JavaDoc path) {
544     addClassPath(new File JavaDoc[] {path});
545   }
546
547   /**
548    * Adds specified class path to beginning of existing path.
549    * The parameter is
550    * assumed to be an array of zip files and/or directories.
551    * @param path The class path to be added to current class path.
552    */

553   public synchronized void addClassPath(File JavaDoc[] path) {
554     classPath.add(path);
555   }
556
557   /**
558    * Adds specified class path to beginning of existing path.
559    * The parameter is
560    * represent a directory or zip file on the local machine or a
561    * remote machine.
562    * @param path The class path to be added to current class path.
563    */

564   public void addClassPath(URL JavaDoc path) {
565     addClassPath(new URL JavaDoc[] {path});
566   }
567
568   /**
569    * Adds specified class path to beginning of existing path.
570    * The parameter is
571    * represent directories and/or zip files on the local machine and/or
572    * on remote machines.
573    * @param path The class path to be added to current class path.
574    */

575   public synchronized void addClassPath(URL JavaDoc[] path) {
576     classPath.add(path);
577   }
578
579   /**
580    * Clears class path entries.
581    * @see #setClassPath
582    */

583   public synchronized void clearClassPath() {
584     classPath.clear();
585   }
586
587   /**
588    * Gets class path for class loader defined previously by constructor and
589    * <CODE>setClassPath</CODE>/<CODE>addClassPath</CODE> methods.
590    * @return the class path represented by an <CODE>Enumeration</CODE>
591    * of URL objects.
592    * @see #setClassPath
593    * @see #addClassPath
594    * <!-- FIXME: should really return a format that can be passed
595    * to another class loader, like an array -->
596    */

597   public URL JavaDoc[] getClassPath() {
598     int len = classPath.getLength();
599     URL JavaDoc[] urlPath = new URL JavaDoc[len];
600     Enumeration JavaDoc cpeEnum = classPath.getPath();
601     for (int i = 0; i < len; i++) {
602       ClassPathEntry cpe = (ClassPathEntry) cpeEnum.nextElement();
603       urlPath[i] = cpe.getURL();
604     }
605     return urlPath;
606   }
607
608   /**
609    * Parse a class-path string using the system path separator.
610    */

611   public static String JavaDoc[] parseClassPath(String JavaDoc path) {
612     String JavaDoc systemSeparator = System.getProperty("path.separator");
613     if (systemSeparator == null) {
614       throw new NullPointerException JavaDoc("path.separator property not defined");
615     }
616     StringTokenizer JavaDoc tokenizer = new StringTokenizer JavaDoc(path, systemSeparator);
617     String JavaDoc[] parsed = new String JavaDoc[tokenizer.countTokens()];
618     for (int i = 0; tokenizer.hasMoreTokens(); i++) {
619       parsed[i] = tokenizer.nextToken();
620     }
621     return parsed;
622   }
623
624   /**
625    * Gets class path from system.
626    *
627    * @return the system class path represented by an
628    * array of URL objects.
629    */

630   public static URL JavaDoc[] getSystemClassPath() {
631     // Parse system class path into its components
632
String JavaDoc systemClassPath = System.getProperty("java.class.path");
633     if (systemClassPath == null) {
634       systemClassPath = "";
635     }
636     String JavaDoc[] parsedPath = parseClassPath(systemClassPath);
637     // Convert to URLs, dropping invalid entries
638
Vector JavaDoc urlVector = new Vector JavaDoc(parsedPath.length);
639     for (int i = 0; i < parsedPath.length; i++) {
640       try {
641         urlVector.addElement(new URL JavaDoc("file", "", parsedPath[i]));
642       }
643       catch (MalformedURLException JavaDoc mue) {
644         // Do not add this entry
645
}
646     }
647     URL JavaDoc[] urlArray = new URL JavaDoc[urlVector.size()];
648     urlVector.copyInto(urlArray);
649     return urlArray;
650   }
651
652   /**
653    * Set the parent class loader for delegation.
654    */

655   public void setParent(ClassLoader JavaDoc parent) {
656     parentClassLoader = parent;
657   }
658
659   /**
660    * Get the secondary class loader.
661    */

662   public ClassLoader JavaDoc getSecondary() {
663     return secondaryClassLoader;
664   }
665
666   /**
667    * Set the secondary class loader.
668    */

669   public void setSecondary(ClassLoader JavaDoc secondary) {
670     secondaryClassLoader = secondary;
671   }
672
673   /**
674    * Get the log channel associated this class loader.
675    */

676   public LogChannel getLogChannel() {
677     return logChannel;
678   }
679
680   /**
681    * Add a filter to the list of filters that check if a class maybe
682    * loaded by this class loader.
683    */

684   public synchronized void addClassFilter(ClassFilter filter) {
685     if (filters == null) {
686       filters = new Vector JavaDoc();
687     }
688     filters.addElement(filter);
689   }
690
691   /**
692    * Check class filters to determine if a class should be loaded by
693    * this class loader. This also handles check an implicit filter
694    * for java.lang.* classes, which must go to the main class loader.
695    * @return One of NORMAL_LOAD, DONT_LOAD, CAN_LOAD, or MUST_LOAD.
696    */

697   private int checkFilters(String JavaDoc className) {
698     int restrict = ClassFilter.NORMAL_LOAD;
699     if (className.startsWith("java.lang.")) {
700       restrict = ClassFilter.DONT_LOAD;
701     }
702     else if (filters != null) {
703       int numFilters = filters.size();
704       for (int idx = 0; idx < numFilters; idx++) {
705         restrict = ( (ClassFilter) filters.elementAt(idx)).loadCheck(className);
706         if (restrict != ClassFilter.NORMAL_LOAD) {
707           break;
708         }
709       }
710     }
711     // Log restriction, if any.
712
if ( (restrict != ClassFilter.NORMAL_LOAD) && loggingEnabled) {
713       String JavaDoc msg = "";
714       switch (restrict) {
715         case ClassFilter.DONT_LOAD:
716           msg = "Filter disallows loading by this classloader: ";
717           break;
718         case ClassFilter.CAN_LOAD:
719           msg = "Filter allows loading by this classloader: ";
720           break;
721         case ClassFilter.MUST_LOAD:
722           msg = "Filter requires loading by this classloader: ";
723           break;
724       }
725       if (loggingEnabled) {
726         logChannel.write(logLevel, msg + className);
727       }
728     }
729     return restrict;
730   }
731
732   /**
733    * Check the table of loaded classes to determine if a class is already
734    * loaded. Purposely not synchronized, allowing a quick check for a class
735    * being loaded. A second check, which higher level synchronization is
736    * required if the class is not found. This function does logs a message
737    * when a class is found.
738    * @param className The class to look up.
739    * @return The ClassResource object for the class, or null if not found.
740    */

741   private ClassResource checkForLoadedClass(String JavaDoc className) {
742     ClassResource cr = (ClassResource) loadedClasses.get(className);
743     if ( (cr != null) && loggingEnabled) {
744       logChannel.write(logLevel, "loadClass already loaded: " + className);
745     }
746     return cr;
747   }
748
749   //FIXME: method with resolve parameter should be protected (see base class).
750
/**
751    * Loads and, optionally, resolves the specified class. If the class
752    * needs to be instantiated, the class must be resolved. If only the
753    * existence of the class needs verification, class resolution is
754    * unnecessary.
755    *
756    * Calling <CODE>loadClass(String className)</CODE> is equivalent to calling
757    * this method with <CODE>resolve</CODE> set to <CODE>true</CODE>.
758    *
759    * Purposely not synchronized. If class is not found in loadedClasses table, then
760    * doLoadClass will do the operation in a synchronized manner.
761    *
762    * @param className The name of the class to be loaded, e.g.
763    * "com.lutris.util.Table".
764    * @param resolve Set to <CODE>true</CODE> for class resolution,
765    * <CODE>false</CODE> for no resolution.
766    * @return the loaded Class.
767    * @exception ClassNotFoundException if the class could not be load.
768    * @see #setClassPath
769    * @see #addClassPath
770    */

771   public Class JavaDoc loadClass(String JavaDoc className, boolean resolve) throws ClassNotFoundException JavaDoc {
772     return loadClassResource(className, resolve).getClassObj();
773   }
774
775   public Class JavaDoc loadClass(String JavaDoc className) throws ClassNotFoundException JavaDoc {
776     if (this.forceSecondaryLoader)
777       return loadClassResource(className, false).getClassObj();
778     else
779       return super.loadClass(className);
780   }
781
782   /**
783    * Log a class load failer.
784    */

785   private void logClassLoadFailure(String JavaDoc className, Throwable JavaDoc except) {
786     if (loggingEnabled) {
787       logChannel.write(logLevel, "load of class failed: " + className, except);
788     }
789   }
790
791   /**
792    * Log a resource read failer.
793    */

794   private void logReadFailure(String JavaDoc name, Throwable JavaDoc except) {
795     if (loggingEnabled) {
796       logChannel.write(logLevel, "read of resource failed: " + name, except);
797     }
798   }
799
800   /**
801    * Loads and, optionally, resolves the specified class, returning the
802    * <CODE>ClassResource</CODE> object.
803    *
804    * @param className The name of the class to load.
805    * @param resolve Set to <CODE>true</CODE> for class resolution,
806    * <CODE>false</CODE> for no resolution.
807    * @return Object containing class and resource.
808    * @exception ClassNotFoundException if this loader and the system loader
809    * can not find or successfully load the class.
810    * @see #setClassPath
811    * @see #addClassPath
812    * @see #loadClass
813    */

814   private ClassResource loadClassResource(String JavaDoc className, boolean resolve) throws ClassNotFoundException JavaDoc {
815     if (loggingEnabled) {
816       logChannel.write(logLevel, "loadClass: " + className);
817     }
818     try {
819       ClassResource cr = checkForLoadedClass(className);
820       if (cr == null) {
821         cr = doLoadClass(className);
822         if (resolve) {
823           resolveClass(cr.getClassObj());
824         }
825       }
826       return cr;
827     }
828     catch (ClassNotFoundException JavaDoc except) {
829       logClassLoadFailure(className, except);
830       throw except;
831     }
832     catch (IOException JavaDoc except) {
833       logClassLoadFailure(className, except);
834       throw new ClassNotFoundException JavaDoc(except.getClass().getName() + ": " +
835                                        except.getMessage());
836     }
837     catch (RuntimeException JavaDoc except) {
838       logClassLoadFailure(className, except);
839       throw except;
840     }
841     catch (Error JavaDoc except) {
842       logClassLoadFailure(className, except);
843       throw except;
844     }
845   }
846
847   /**
848    * Get the resource for a class loaded by this class loader.
849    */

850   public Resource getClassResource(String JavaDoc className) throws ClassNotFoundException JavaDoc {
851     ClassResource cr = checkForLoadedClass(className);
852     if (cr == null) {
853       throw new ClassNotFoundException JavaDoc("Class \"" + className + "\" is not loaded by this class loader");
854     }
855     return cr.getResource();
856   }
857
858   /**
859    * Load a class with the parent or secondary class loader.
860    * Must already be synchronized
861    */

862   private ClassResource loadClassOther(String JavaDoc className, ClassLoader JavaDoc loader,
863                                        String JavaDoc loaderName) {
864     ClassResource cr = null;
865
866     if (loggingEnabled) {
867       logChannel.write(logLevel, "checking " + loaderName + " class loader: " + className);
868     }
869     try {
870       if (loader instanceof MultiClassLoader) {
871         cr = ( (MultiClassLoader) loader).loadClassResource(className, false);
872       }
873       else {
874         Class JavaDoc c = loader.loadClass(className);
875         cr = new ClassResource(c, null);
876       }
877       if (loggingEnabled) {
878         logChannel.write(logLevel, "class loaded by: " + loaderName);
879       }
880     }
881     catch (ClassNotFoundException JavaDoc except) {
882       // Just log and return null
883
if (loggingEnabled) {
884         logChannel.write(logLevel, "class not loaded by " + loaderName
885                          + ": " + except.getClass().getName() + ": " + except.getMessage());
886       }
887     }
888     return cr;
889   }
890
891   /**
892    * Load a class with this class loader. Must already be
893    * synchronized
894    */

895   private ClassResource loadClassHere(String JavaDoc className) throws IOException JavaDoc {
896     ClassResource cr = null;
897
898     if (loggingEnabled) {
899       logChannel.write(logLevel, "checking MultiClassLoader: " + className);
900     }
901     String JavaDoc fileName = className.replace('.', '/').concat(".class");
902     Resource resource = getResourceObject(fileName);
903
904     if (resource != null) {
905
906       Class JavaDoc c = null;
907       try {
908         // AutoReload with secondary loader
909
if (isAutoReload && forceSecondaryLoader) {
910           InputStream JavaDoc is = null;
911           try {
912             is = this.secondaryClassLoader.getResource(fileName).openConnection().getInputStream();
913             c = getClassFromStream(className, is);
914           }
915           catch (Throwable JavaDoc th) {
916             try {
917               is = this.secondaryClassLoader.getResourceAsStream(fileName);
918               c = getClassFromStream(className, is);
919             }
920             catch (Throwable JavaDoc th1) {
921               c = Class.forName(className, true, this.secondaryClassLoader);
922             }
923           }
924
925         }
926         // loading with secondary loader
927
else if (forceSecondaryLoader) {
928           c = Class.forName(className, true, this.secondaryClassLoader);
929         }
930         // standard use of MultiClassLoader
931
else
932           c = Class.forName(className);
933       }
934       catch (ClassNotFoundException JavaDoc e) {
935         try {
936           // second try to search the class among Dll-mapped classes
937
// this requires the call Class.forName with system class
938
// loader
939
c = Class.forName(className, true, ClassLoader.getSystemClassLoader());
940         }
941         catch (ClassNotFoundException JavaDoc e1) {
942           // if there is no precompiled class, try to compile it with JET JIT
943
// NOTE! this will success only if -Djet.jit property is defined
944
byte[] classBytes = resource.getBytes();
945           c = defineClass(className, classBytes, 0, classBytes.length);
946         }
947       }
948
949       cr = new ClassResource(c, resource);
950       loadedClasses.put(className, cr);
951
952       if (loggingEnabled) {
953         logChannel.write(logLevel, "class loaded by MultiClassLoader: " + className);
954       }
955     }
956     if ( (cr == null) && loggingEnabled) {
957       logChannel.write(logLevel, "class not loaded by MultiClassLoader: " + className);
958     }
959     return cr;
960   }
961
962   /**
963    * Returns Class object for defined class name created by bytes obtained from
964    * InputStream. Note that InputStrim will be closed in this method after it
965    * is read.
966    * @param className full name of class which has to be created
967    * @param is InputStream which is used as origin of bytes used for class
968    * generation.
969    * @return created Class object
970    * @throws IOException
971    */

972   private Class JavaDoc getClassFromStream(String JavaDoc className, InputStream JavaDoc is) throws IOException JavaDoc {
973     ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc();
974     byte[] buf = new byte[1024];
975     int i = is.read(buf);
976     while (i == 1024) {
977       baos.write(buf);
978       i = is.read(buf);
979     }
980     if (i != -1)
981       baos.write(buf, 0, i);
982
983     buf = baos.toByteArray();
984     baos.close();
985     is.close();
986
987     return defineClass(className, buf, 0, buf.length);
988   }
989   /**
990    * Loads specified class. If possible, the class will loaded from the
991    * class path defined previously with <CODE>setClassPath</CODE>,
992    * <CODE>addClassPath</CODE>, and/or the constructor. Otherwise, the
993    * class will be passed off to the parent class loader. Classes from
994    * the java.* packages cannot be loaded by this class loader so will
995    * be passed off to the parent class loader.
996    * @param className The name of the class to be loaded, e.g.
997    * "com.lutris.util.Table".
998    * @return Object containing class and resource.
999    * @exception ClassNotFoundException if this loader and the system loader
1000   * can not find or successfully load the class.
1001   * @see #setClassPath
1002   * @see #addClassPath
1003   */

1004  private synchronized ClassResource doLoadClass(String JavaDoc className) throws ClassNotFoundException JavaDoc,
1005      IOException JavaDoc {
1006    // Must check loaded class table again, as we are now synchronized.
1007
ClassResource cr = checkForLoadedClass(className);
1008    if (cr != null) {
1009      return cr;
1010    }
1011    // Get filter restriction to use (includes check for java.lang.*).
1012
int filterRestrict = checkFilters(className);
1013
1014    // Delegate if parent exists and filter allows.
1015
if ( (parentClassLoader != null) && (filterRestrict != ClassFilter.CAN_LOAD)
1016        && (filterRestrict != ClassFilter.MUST_LOAD)) {
1017      cr = loadClassOther(className, parentClassLoader, "parent");
1018      if (cr != null) {
1019        return cr;
1020      }
1021    }
1022
1023    // Try loading with this class loader if filter allows.
1024
if ( (filterRestrict == ClassFilter.NORMAL_LOAD) || (filterRestrict == ClassFilter.CAN_LOAD)
1025        || (filterRestrict == ClassFilter.MUST_LOAD)) {
1026      cr = loadClassHere(className);
1027      if (cr != null) {
1028        return cr;
1029      }
1030    }
1031
1032    // Try secondary class loader.
1033
if ( (secondaryClassLoader != null) && (filterRestrict != ClassFilter.MUST_LOAD)) {
1034      cr = loadClassOther(className, secondaryClassLoader, "secondary");
1035      if (cr != null) {
1036        return cr;
1037      }
1038    }
1039    throw new ClassNotFoundException JavaDoc(className);
1040  }
1041
1042  /**
1043   * Gets specified resource as URL. Doing a getContent() on the URL may
1044   * return an Image, an AudioClip, or an InputStream.
1045   * @param name The name of the resource.
1046   * @return the resource represented by a URL, or null if not found.
1047   */

1048  public URL JavaDoc getResource(String JavaDoc name) {
1049    Resource resource = getResourceObject(name);
1050    if (resource != null) {
1051      ClassPathEntry location = resource.getLocation();
1052      try {
1053        return new URL JavaDoc(location.getURL() + resource.getName());
1054      }
1055      catch (MalformedURLException JavaDoc mue) {
1056        if (loggingEnabled) {
1057          logChannel.write(logLevel, "getResource not returned due to exception: " + name, mue);
1058        }
1059        return null;
1060      }
1061    }
1062    return null;
1063  }
1064
1065  /**
1066   * Gets specified resource object.
1067   * @param name The name of the resource.
1068   * @return the resource if found, null if not.
1069   * @see Resource
1070   */

1071  public Resource getResourceObject(String JavaDoc name) {
1072    Resource resource;
1073    if (loggingEnabled) {
1074      logChannel.write(logLevel, "getResource loading: " + name);
1075    }
1076    try {
1077      resource = classPath.getResource(name);
1078      if (loggingEnabled) {
1079        if (resource == null) {
1080          logChannel.write(logLevel, "getResource not found: " + name);
1081        }
1082        else {
1083          logChannel.write(logLevel, "getResource finished: " + name);
1084        }
1085      }
1086    }
1087    catch (RuntimeException JavaDoc except) {
1088      logReadFailure(name, except);
1089      throw except;
1090    }
1091    catch (Error JavaDoc except) {
1092      logReadFailure(name, except);
1093      throw except;
1094    }
1095    return resource;
1096  }
1097
1098  /**
1099   * Gets specified resource object.
1100   * @see deprecated Use getResourceObject()
1101   * @see getResourceObject
1102   */

1103  public Resource getResourceAsIs(String JavaDoc name) {
1104    return getResourceObject(name);
1105  }
1106
1107  /**
1108   * Gets specified resource as input stream.
1109   * @param name The name of the resource.
1110   * @return an input stream representing the specified resource or
1111   * null if the resource is not found.
1112   */

1113  public InputStream JavaDoc getResourceAsStream(String JavaDoc name) {
1114    Resource resource = getResourceObject(name);
1115    if (resource != null) {
1116      try {
1117        return resource.getInputStream();
1118      }
1119      catch (IOException JavaDoc except) {
1120        logReadFailure(name, except);
1121        return null; // This is what getResourceAsStream does..
1122
}
1123    }
1124    return null;
1125  }
1126
1127  /**
1128   * Gets specified resource as array of bytes.
1129   * @param name The name of the resource.
1130   * @return an array of bytes representing the specified resource or
1131   * null if the resource is not found.
1132   */

1133  public byte[] getResourceAsByteArray(String JavaDoc name) {
1134    Resource resource = getResourceObject(name);
1135    if (resource != null) {
1136      try {
1137        return resource.getBytes();
1138      }
1139      catch (IOException JavaDoc except) {
1140        logReadFailure(name, except);
1141        return null; // This is what getResourceAsStream does..
1142
}
1143    }
1144    return null;
1145  }
1146
1147  /**
1148   * Determine if the classes loaded by this class loader have been modified.
1149   * If any file associated with loaded in the class loader's class path has
1150   * changed, the classes should be reloaded. If the classes need to be
1151   * reloaded, a new instance of this class loader must be created
1152   * because a particular class loader instance can only load classes
1153   * once.
1154   * @return <CODE>true</CODE> if the classes should be reloaded,
1155   * <CODE>false</CODE> if not.
1156   */

1157  public boolean shouldReload() {
1158    Enumeration JavaDoc classes = loadedClasses.elements();
1159    if (loggingEnabled) {
1160      logChannel.write(logLevel, "Checking for out-of-date classes");
1161    }
1162    while (classes.hasMoreElements()) {
1163      boolean isModified;
1164      try {
1165        isModified = ( (ClassResource) classes.nextElement()).getResource().hasBeenModified();
1166      }
1167      catch (FileNotFoundException JavaDoc except) {
1168        if (loggingEnabled) {
1169          logChannel.write(logLevel, "File for loaded class can no longer be accessed", except);
1170        }
1171        isModified = true;
1172      }
1173      if (isModified) {
1174        if (loggingEnabled) {
1175          logChannel.write(logLevel, "Loaded classes have been modified");
1176        }
1177        return true;
1178      }
1179    }
1180    if (loggingEnabled) {
1181      logChannel.write(logLevel, "Loaded classes have not been modified");
1182    }
1183    return false;
1184  }
1185}
1186
Popular Tags