KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > oddjob > arooa > ArooaClassLoader


1 /*
2  * This source code is heavily based on source code from the Apache
3  * Ant project. As such the following is included:
4  * ------------------------------------------------------------------
5  *
6  * The Apache Software License, Version 1.1
7  *
8  * Copyright (c) 2000-2003 The Apache Software Foundation. All rights
9  * reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  *
15  * 1. Redistributions of source code must retain the above copyright
16  * notice, this list of conditions and the following disclaimer.
17  *
18  * 2. Redistributions in binary form must reproduce the above copyright
19  * notice, this list of conditions and the following disclaimer in
20  * the documentation and/or other materials provided with the
21  * distribution.
22  *
23  * 3. The end-user documentation included with the redistribution, if
24  * any, must include the following acknowlegement:
25  * "This product includes software developed by the
26  * Apache Software Foundation (http://www.apache.org/)."
27  * Alternately, this acknowlegement may appear in the software itself,
28  * if and wherever such third-party acknowlegements normally appear.
29  *
30  * 4. The names "Ant" and "Apache Software
31  * Foundation" must not be used to endorse or promote products derived
32  * from this software without prior written permission. For written
33  * permission, please contact apache@apache.org.
34  *
35  * 5. Products derived from this software may not be called "Apache"
36  * nor may "Apache" appear in their names without prior written
37  * permission of the Apache Group.
38  *
39  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
40  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
41  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
42  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
43  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
44  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
45  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
46  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
47  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
48  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
49  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
50  * SUCH DAMAGE.
51  * ====================================================================
52  *
53  * This software consists of voluntary contributions made by many
54  * individuals on behalf of the Apache Software Foundation. For more
55  * information on the Apache Software Foundation, please see
56  * <http://www.apache.org/>.
57  */

58 package org.oddjob.arooa;
59
60 import java.io.ByteArrayOutputStream JavaDoc;
61 import java.io.File JavaDoc;
62 import java.io.FileInputStream JavaDoc;
63 import java.io.IOException JavaDoc;
64 import java.io.InputStream JavaDoc;
65 import java.lang.reflect.Constructor JavaDoc;
66 import java.net.MalformedURLException JavaDoc;
67 import java.net.URL JavaDoc;
68 import java.util.Enumeration JavaDoc;
69 import java.util.Hashtable JavaDoc;
70 import java.util.Vector JavaDoc;
71 import java.util.zip.ZipEntry JavaDoc;
72 import java.util.zip.ZipFile JavaDoc;
73
74 import org.apache.log4j.Logger;
75 import org.apache.tools.ant.AntClassLoader;
76
77 /**
78  * Used to load classes within ant with a different classpath from
79  * that used to start ant. Note that it is possible to force a class
80  * into this loader even when that class is on the system classpath by
81  * using the forceLoadClass method. Any subsequent classes loaded by that
82  * class will then use this loader rather than the system class loader.
83  * <p>
84  * @author Based on the orginal from Ant.
85  */

86 public class ArooaClassLoader extends ClassLoader JavaDoc {
87     private static final Logger logger = Logger.getLogger(ArooaClassLoader.class);
88     
89     /**
90      * An enumeration of all resources of a given name found within the
91      * classpath of this class loader. This enumeration is used by the
92      * ClassLoader.findResources method, which is in
93      * turn used by the ClassLoader.getResources method.
94      *
95      * @see AntClassLoader#findResources(String)
96      * @see java.lang.ClassLoader#getResources(String)
97      * @author <a HREF="mailto:hermand@alumni.grinnell.edu">David A. Herman</a>
98      */

99     private class ResourceEnumeration implements Enumeration JavaDoc {
100         /**
101          * The name of the resource being searched for.
102          */

103         private String JavaDoc resourceName;
104
105         /**
106          * The index of the next classpath element to search.
107          */

108         private int pathElementsIndex;
109
110         /**
111          * The URL of the next resource to return in the enumeration. If this
112          * field is <code>null</code> then the enumeration has been completed,
113          * i.e., there are no more elements to return.
114          */

115         private URL JavaDoc nextResource;
116
117         /**
118          * Constructs a new enumeration of resources of the given name found
119          * within this class loader's classpath.
120          *
121          * @param name the name of the resource to search for.
122          */

123         ResourceEnumeration(String JavaDoc name) {
124             this.resourceName = name;
125             this.pathElementsIndex = 0;
126             findNextResource();
127         }
128
129         /**
130          * Indicates whether there are more elements in the enumeration to
131          * return.
132          *
133          * @return <code>true</code> if there are more elements in the
134          * enumeration; <code>false</code> otherwise.
135          */

136         public boolean hasMoreElements() {
137             return (this.nextResource != null);
138         }
139
140         /**
141          * Returns the next resource in the enumeration.
142          *
143          * @return the next resource in the enumeration
144          */

145         public Object JavaDoc nextElement() {
146             URL JavaDoc ret = this.nextResource;
147             findNextResource();
148             return ret;
149         }
150
151         /**
152          * Locates the next resource of the correct name in the classpath and
153          * sets <code>nextResource</code> to the URL of that resource. If no
154          * more resources can be found, <code>nextResource</code> is set to
155          * <code>null</code>.
156          */

157         private void findNextResource() {
158             URL JavaDoc url = null;
159             while ((pathElementsIndex < pathComponents.size())
160                     && (url == null)) {
161                 File JavaDoc pathComponent
162                     = (File JavaDoc) pathComponents.elementAt(pathElementsIndex);
163                 url = getResourceURL(pathComponent, this.resourceName);
164                 pathElementsIndex++;
165             }
166             this.nextResource = url;
167         }
168     }
169
170     /**
171      * The size of buffers to be used in this classloader.
172      */

173     private static final int BUFFER_SIZE = 8192;
174     /**
175      * Number of array elements in a test array of strings
176      */

177     private static final int NUMBER_OF_STRINGS = 256;
178
179     /**
180      * The components of the classpath that the classloader searches
181      * for classes.
182      */

183     private Vector JavaDoc pathComponents = new Vector JavaDoc();
184
185     /**
186      * Indicates whether the parent class loader should be
187      * consulted before trying to load with this class loader.
188      */

189     private boolean parentFirst = true;
190
191     /**
192      * These are the package roots that are to be loaded by the parent class
193      * loader regardless of whether the parent class loader is being searched
194      * first or not.
195      */

196     private Vector JavaDoc systemPackages = new Vector JavaDoc();
197
198     /**
199      * These are the package roots that are to be loaded by this class loader
200      * regardless of whether the parent class loader is being searched first
201      * or not.
202      */

203     private Vector JavaDoc loaderPackages = new Vector JavaDoc();
204
205     /**
206      * Whether or not this classloader will ignore the base
207      * classloader if it can't find a class.
208      *
209      * @see #setIsolated(boolean)
210      */

211     private boolean ignoreBase = false;
212
213     /**
214      * The parent class loader, if one is given or can be determined.
215      */

216     private ClassLoader JavaDoc parent = null;
217
218     /**
219      * A hashtable of zip files opened by the classloader (File to ZipFile).
220      */

221     private Hashtable JavaDoc zipFiles = new Hashtable JavaDoc();
222
223     
224     /**
225      * Create an Ant Class Loader
226      */

227     public ArooaClassLoader() {
228         setParent(null);
229     }
230
231     /**
232      * Creates a classloader for the given project using the classpath given.
233      *
234      * @param project The project to which this classloader is to belong.
235      * Must not be <code>null</code>.
236      * @param classpath The classpath to use to load the classes. This
237      * is combined with the system classpath in a manner
238      * determined by the value of ${build.sysclasspath}.
239      * May be <code>null</code>, in which case no path
240      * elements are set up to start with.
241      */

242     public ArooaClassLoader(File JavaDoc[] classpath) {
243         setParent(null);
244         setClassPath(classpath);
245     }
246
247     /**
248      * Creates a classloader for the given project using the classpath given.
249      *
250      * @param parent The parent classloader to which unsatisfied loading
251      * attempts are delegated. May be <code>null</code>,
252      * in which case the classloader which loaded this
253      * class is used as the parent.
254      * @param project The project to which this classloader is to belong.
255      * Must not be <code>null</code>.
256      * @param classpath the classpath to use to load the classes.
257      * May be <code>null</code>, in which case no path
258      * elements are set up to start with.
259      * @param parentFirst If <code>true</code>, indicates that the parent
260      * classloader should be consulted before trying to
261      * load the a class through this loader.
262      */

263     public ArooaClassLoader(ClassLoader JavaDoc parent, File JavaDoc[] classpath,
264                           boolean parentFirst) {
265         this(classpath);
266         if (parent != null) {
267             setParent(parent);
268         }
269         setParentFirst(parentFirst);
270     }
271
272
273     /**
274      * Creates a classloader for the given project using the classpath given.
275      *
276      * @param project The project to which this classloader is to belong.
277      * Must not be <code>null</code>.
278      * @param classpath The classpath to use to load the classes. May be
279      * <code>null</code>, in which case no path
280      * elements are set up to start with.
281      * @param parentFirst If <code>true</code>, indicates that the parent
282      * classloader should be consulted before trying to
283      * load the a class through this loader.
284      */

285     public ArooaClassLoader(File JavaDoc[] classpath,
286                           boolean parentFirst) {
287         this(null, classpath, parentFirst);
288     }
289
290     /**
291      * Creates an empty class loader. The classloader should be configured
292      * with path elements to specify where the loader is to look for
293      * classes.
294      *
295      * @param parent The parent classloader to which unsatisfied loading
296      * attempts are delegated. May be <code>null</code>,
297      * in which case the classloader which loaded this
298      * class is used as the parent.
299      * @param parentFirst If <code>true</code>, indicates that the parent
300      * classloader should be consulted before trying to
301      * load the a class through this loader.
302      */

303     public ArooaClassLoader(ClassLoader JavaDoc parent, boolean parentFirst) {
304         setParent(parent);
305         this.parentFirst = parentFirst;
306     }
307
308     /**
309      * Set the classpath to search for classes to load. This should not be
310      * changed once the classloader starts to server classes
311      *
312      * @param classpath the search classpath consisting of directories and
313      * jar/zip files.
314      */

315     public void setClassPath(File JavaDoc[] classpath) {
316         pathComponents.removeAllElements();
317         if (classpath != null) {
318             logger.debug("Setting classloader classpath with ["
319                     + classpath.length + "] files...");
320             for (int i = 0; i < classpath.length; ++i) {
321                 try {
322                     addPathFile(classpath[i]);
323                     logger.debug("" + classpath[i]);
324                 } catch (IOException JavaDoc e) {
325                     throw new ArooaException(e);
326                 }
327             }
328         }
329         else {
330             logger.debug("Setting classloader classpath null");
331         }
332     }
333
334     /**
335      * Set the parent for this class loader. This is the class loader to which
336      * this class loader will delegate to load classes
337      *
338      * @param parent the parent class loader.
339      */

340     public void setParent(ClassLoader JavaDoc parent) {
341         if (parent == null) {
342             this.parent = ArooaClassLoader.class.getClassLoader();
343         } else {
344             this.parent = parent;
345         }
346     }
347
348     /**
349      * Control whether class lookup is delegated to the parent loader first
350      * or after this loader. Use with extreme caution. Setting this to
351      * false violates the class loader hierarchy and can lead to Linkage errors
352      *
353      * @param parentFirst if true, delegate initial class search to the parent
354      * classloader.
355      */

356     public void setParentFirst(boolean parentFirst) {
357         this.parentFirst = parentFirst;
358     }
359
360     /**
361      * Add a file to the path
362      *
363      * @param pathComponent the file which is to be added to the path for
364      * this class loader
365      *
366      * @throws IOException if data needed from the file cannot be read.
367      */

368     protected void addPathFile(File JavaDoc pathComponent) throws IOException JavaDoc {
369         pathComponents.addElement(pathComponent);
370     }
371
372     /**
373      * Returns the classpath this classloader will consult.
374      *
375      * @return the classpath used for this classloader, with elements
376      * separated by the path separator for the system.
377      */

378     public String JavaDoc getClasspath() {
379         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
380         boolean firstPass = true;
381         Enumeration JavaDoc componentEnum = pathComponents.elements();
382         while (componentEnum.hasMoreElements()) {
383             if (!firstPass) {
384                 sb.append(System.getProperty("path.separator"));
385             } else {
386                 firstPass = false;
387             }
388             sb.append(((File JavaDoc) componentEnum.nextElement()).getAbsolutePath());
389         }
390         return sb.toString();
391     }
392
393     public String JavaDoc toString() {
394         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
395         boolean firstPass = true;
396         Enumeration JavaDoc componentEnum = pathComponents.elements();
397         sb.append("ArooaClassLoader (");
398         while (componentEnum.hasMoreElements()) {
399             if (!firstPass) {
400                 sb.append(',');
401             } else {
402                 firstPass = false;
403             }
404             sb.append(((File JavaDoc) componentEnum.nextElement()).getName());
405         }
406         sb.append(')');
407         return sb.toString();
408     }
409     
410     /**
411      * Sets whether this classloader should run in isolated mode. In
412      * isolated mode, classes not found on the given classpath will
413      * not be referred to the parent class loader but will cause a
414      * ClassNotFoundException.
415      *
416      * @param isolated Whether or not this classloader should run in
417      * isolated mode.
418      */

419     public synchronized void setIsolated(boolean isolated) {
420         ignoreBase = isolated;
421     }
422
423     /**
424      * Forces initialization of a class in a JDK 1.1 compatible, albeit hacky
425      * way.
426      *
427      * @param theClass The class to initialize.
428      * Must not be <code>null</code>.
429      *
430      * @deprecated use Class.forName with initialize=true instead.
431      */

432     public static void initializeClass(Class JavaDoc theClass) {
433         // ***HACK*** We ask the VM to create an instance
434
// by voluntarily providing illegal arguments to force
435
// the VM to run the class' static initializer, while
436
// at the same time not running a valid constructor.
437

438         final Constructor JavaDoc[] cons = theClass.getDeclaredConstructors();
439         //At least one constructor is guaranteed to be there, but check anyway.
440
if (cons != null) {
441             if (cons.length > 0 && cons[0] != null) {
442                 final String JavaDoc[] strs = new String JavaDoc[NUMBER_OF_STRINGS];
443                 try {
444                     cons[0].newInstance(strs);
445                     // Expecting an exception to be thrown by this call:
446
// IllegalArgumentException: wrong number of Arguments
447
} catch (Throwable JavaDoc t) {
448                     // Ignore - we are interested only in the side
449
// effect - that of getting the static initializers
450
// invoked. As we do not want to call a valid
451
// constructor to get this side effect, an
452
// attempt is made to call a hopefully
453
// invalid constructor - come on, nobody
454
// would have a constructor that takes in
455
// 256 String arguments ;-)
456
// (In fact, they can't - according to JVM spec
457
// section 4.10, the number of method parameters is limited
458
// to 255 by the definition of a method descriptor.
459
// Constructors count as methods here.)
460
}
461             }
462         }
463     }
464
465     /**
466      * Adds a package root to the list of packages which must be loaded on the
467      * parent loader.
468      *
469      * All subpackages are also included.
470      *
471      * @param packageRoot The root of all packages to be included.
472      * Should not be <code>null</code>.
473      */

474     public void addSystemPackageRoot(String JavaDoc packageRoot) {
475         systemPackages.addElement(packageRoot
476                                   + (packageRoot.endsWith(".") ? "" : "."));
477     }
478
479     /**
480      * Adds a package root to the list of packages which must be loaded using
481      * this loader.
482      *
483      * All subpackages are also included.
484      *
485      * @param packageRoot The root of all packages to be included.
486      * Should not be <code>null</code>.
487      */

488     public void addLoaderPackageRoot(String JavaDoc packageRoot) {
489         loaderPackages.addElement(packageRoot
490                                   + (packageRoot.endsWith(".") ? "" : "."));
491     }
492
493     /**
494      * Loads a class through this class loader even if that class is available
495      * on the parent classpath.
496      *
497      * This ensures that any classes which are loaded by the returned class
498      * will use this classloader.
499      *
500      * @param classname The name of the class to be loaded.
501      * Must not be <code>null</code>.
502      *
503      * @return the required Class object
504      *
505      * @exception ClassNotFoundException if the requested class does not exist
506      * on this loader's classpath.
507      */

508     public Class JavaDoc forceLoadClass(String JavaDoc classname)
509          throws ClassNotFoundException JavaDoc {
510
511         Class JavaDoc theClass = findLoadedClass(classname);
512
513         if (theClass == null) {
514             theClass = findClass(classname);
515         }
516
517         return theClass;
518     }
519
520     /**
521      * Loads a class through this class loader but defer to the parent class
522      * loader.
523      *
524      * This ensures that instances of the returned class will be compatible
525      * with instances which have already been loaded on the parent
526      * loader.
527      *
528      * @param classname The name of the class to be loaded.
529      * Must not be <code>null</code>.
530      *
531      * @return the required Class object
532      *
533      * @exception ClassNotFoundException if the requested class does not exist
534      * on this loader's classpath.
535      */

536     public Class JavaDoc forceLoadSystemClass(String JavaDoc classname)
537          throws ClassNotFoundException JavaDoc {
538
539         Class JavaDoc theClass = findLoadedClass(classname);
540
541         if (theClass == null) {
542             theClass = findBaseClass(classname);
543         }
544
545         return theClass;
546     }
547
548     /**
549      * Returns a stream to read the requested resource name.
550      *
551      * @param name The name of the resource for which a stream is required.
552      * Must not be <code>null</code>.
553      *
554      * @return a stream to the required resource or <code>null</code> if the
555      * resource cannot be found on the loader's classpath.
556      */

557     public InputStream JavaDoc getResourceAsStream(String JavaDoc name) {
558
559         InputStream JavaDoc resourceStream = null;
560         if (isParentFirst(name)) {
561             resourceStream = loadBaseResource(name);
562             if (resourceStream != null) {
563                 // ResourceStream loaded from parent loader
564

565             } else {
566                 resourceStream = loadResource(name);
567                 if (resourceStream != null) {
568                     // ResourceStream loaded from this loader
569
}
570             }
571         } else {
572             resourceStream = loadResource(name);
573             if (resourceStream != null) {
574                 // ResourceStream loaded from this loader
575
} else {
576                 resourceStream = loadBaseResource(name);
577                 if (resourceStream != null) {
578                     // ResourceStream loaded from parent loader
579
}
580             }
581         }
582
583         if (resourceStream == null) {
584             // Couldn't load ResourceStream
585
}
586
587         return resourceStream;
588     }
589
590     /**
591      * Returns a stream to read the requested resource name from this loader.
592      *
593      * @param name The name of the resource for which a stream is required.
594      * Must not be <code>null</code>.
595      *
596      * @return a stream to the required resource or <code>null</code> if
597      * the resource cannot be found on the loader's classpath.
598      */

599     private InputStream JavaDoc loadResource(String JavaDoc name) {
600         // we need to search the components of the path to see if we can
601
// find the class we want.
602
InputStream JavaDoc stream = null;
603
604         Enumeration JavaDoc e = pathComponents.elements();
605         while (e.hasMoreElements() && stream == null) {
606             File JavaDoc pathComponent = (File JavaDoc) e.nextElement();
607             stream = getResourceStream(pathComponent, name);
608         }
609         return stream;
610     }
611
612     /**
613      * Finds a system resource (which should be loaded from the parent
614      * classloader).
615      *
616      * @param name The name of the system resource to load.
617      * Must not be <code>null</code>.
618      *
619      * @return a stream to the named resource, or <code>null</code> if
620      * the resource cannot be found.
621      */

622     private InputStream JavaDoc loadBaseResource(String JavaDoc name) {
623         if (parent == null) {
624             return getSystemResourceAsStream(name);
625         } else {
626             return parent.getResourceAsStream(name);
627         }
628     }
629
630     /**
631      * Returns an inputstream to a given resource in the given file which may
632      * either be a directory or a zip file.
633      *
634      * @param file the file (directory or jar) in which to search for the
635      * resource. Must not be <code>null</code>.
636      * @param resourceName The name of the resource for which a stream is
637      * required. Must not be <code>null</code>.
638      *
639      * @return a stream to the required resource or <code>null</code> if
640      * the resource cannot be found in the given file.
641      */

642     private InputStream JavaDoc getResourceStream(File JavaDoc file, String JavaDoc resourceName) {
643         try {
644             if (!file.exists()) {
645                 return null;
646             }
647
648             if (file.isDirectory()) {
649                 File JavaDoc resource = new File JavaDoc(file, resourceName);
650
651                 if (resource.exists()) {
652                     return new FileInputStream JavaDoc(resource);
653                 }
654             } else {
655                 // is the zip file in the cache
656
ZipFile JavaDoc zipFile = (ZipFile JavaDoc) zipFiles.get(file);
657                 if (zipFile == null) {
658                     zipFile = new ZipFile JavaDoc(file);
659                     zipFiles.put(file, zipFile);
660                 }
661                 ZipEntry JavaDoc entry = zipFile.getEntry(resourceName);
662                 if (entry != null) {
663                     return zipFile.getInputStream(entry);
664                 }
665             }
666         } catch (Exception JavaDoc e) {
667             // ignore
668
}
669
670         return null;
671     }
672
673     /**
674      * Tests whether or not the parent classloader should be checked for
675      * a resource before this one. If the resource matches both the
676      * "use parent classloader first" and the "use this classloader first"
677      * lists, the latter takes priority.
678      *
679      * @param resourceName The name of the resource to check.
680      * Must not be <code>null</code>.
681      *
682      * @return whether or not the parent classloader should be checked for a
683      * resource before this one is.
684      */

685     private boolean isParentFirst(String JavaDoc resourceName) {
686         // default to the global setting and then see
687
// if this class belongs to a package which has been
688
// designated to use a specific loader first
689
// (this one or the parent one)
690

691         // XXX - shouldn't this always return false in isolated mode?
692

693         boolean useParentFirst = parentFirst;
694
695         for (Enumeration JavaDoc e = systemPackages.elements(); e.hasMoreElements();) {
696             String JavaDoc packageName = (String JavaDoc) e.nextElement();
697             if (resourceName.startsWith(packageName)) {
698                 useParentFirst = true;
699                 break;
700             }
701         }
702
703         for (Enumeration JavaDoc e = loaderPackages.elements(); e.hasMoreElements();) {
704             String JavaDoc packageName = (String JavaDoc) e.nextElement();
705             if (resourceName.startsWith(packageName)) {
706                 useParentFirst = false;
707                 break;
708             }
709         }
710
711         return useParentFirst;
712     }
713
714     /**
715      * Finds the resource with the given name. A resource is
716      * some data (images, audio, text, etc) that can be accessed by class
717      * code in a way that is independent of the location of the code.
718      *
719      * @param name The name of the resource for which a stream is required.
720      * Must not be <code>null</code>.
721      *
722      * @return a URL for reading the resource, or <code>null</code> if the
723      * resource could not be found or the caller doesn't have
724      * adequate privileges to get the resource.
725      */

726     public URL JavaDoc getResource(String JavaDoc name) {
727         // we need to search the components of the path to see if
728
// we can find the class we want.
729
URL JavaDoc url = null;
730         if (isParentFirst(name)) {
731             url = (parent == null) ? super.getResource(name)
732                                    : parent.getResource(name);
733         }
734
735         if (url != null) {
736             // Resource loaded from parent loader"
737
} else {
738             // try and load from this loader if the parent either didn't find
739
// it or wasn't consulted.
740
Enumeration JavaDoc e = pathComponents.elements();
741             while (e.hasMoreElements() && url == null) {
742                 File JavaDoc pathComponent = (File JavaDoc) e.nextElement();
743                 url = getResourceURL(pathComponent, name);
744                 if (url != null) {
745                     // Resource loaded from this loader
746
}
747             }
748         }
749
750         if (url == null && !isParentFirst(name)) {
751             // this loader was first but it didn't find it - try the parent
752

753             url = (parent == null) ? super.getResource(name)
754                 : parent.getResource(name);
755             if (url != null) {
756                 // Resource loaded from parent loader
757
}
758         }
759
760         if (url == null) {
761             // Couldn't load Resource
762
}
763
764         return url;
765     }
766
767     /**
768      * Returns an enumeration of URLs representing all the resources with the
769      * given name by searching the class loader's classpath.
770      *
771      * @param name The resource name to search for.
772      * Must not be <code>null</code>.
773      * @return an enumeration of URLs for the resources
774      * @exception IOException if I/O errors occurs (can't happen)
775      */

776     protected Enumeration JavaDoc findResources(String JavaDoc name) throws IOException JavaDoc {
777         return new ResourceEnumeration(name);
778     }
779
780     /**
781      * Returns the URL of a given resource in the given file which may
782      * either be a directory or a zip file.
783      *
784      * @param file The file (directory or jar) in which to search for
785      * the resource. Must not be <code>null</code>.
786      * @param resourceName The name of the resource for which a stream
787      * is required. Must not be <code>null</code>.
788      *
789      * @return a stream to the required resource or <code>null</code> if the
790      * resource cannot be found in the given file object.
791      */

792     protected URL JavaDoc getResourceURL(File JavaDoc file, String JavaDoc resourceName) {
793         try {
794             if (!file.exists()) {
795                 return null;
796             }
797
798             if (file.isDirectory()) {
799                 File JavaDoc resource = new File JavaDoc(file, resourceName);
800
801                 if (resource.exists()) {
802                     try {
803                         return new URL JavaDoc("file:" + resource.toString());
804                     } catch (MalformedURLException JavaDoc ex) {
805                         return null;
806                     }
807                 }
808             } else {
809                 ZipFile JavaDoc zipFile = (ZipFile JavaDoc) zipFiles.get(file);
810                 if (zipFile == null) {
811                     zipFile = new ZipFile JavaDoc(file);
812                     zipFiles.put(file, zipFile);
813                 }
814
815                 ZipEntry JavaDoc entry = zipFile.getEntry(resourceName);
816                 if (entry != null) {
817                     try {
818                         return new URL JavaDoc("jar:file:" + file.toString()
819                             + "!/" + entry);
820                     } catch (MalformedURLException JavaDoc ex) {
821                         return null;
822                     }
823                 }
824             }
825         } catch (Exception JavaDoc e) {
826             e.printStackTrace();
827         }
828
829         return null;
830     }
831
832     /**
833      * Loads a class with this class loader.
834      *
835      * This class attempts to load the class in an order determined by whether
836      * or not the class matches the system/loader package lists, with the
837      * loader package list taking priority. If the classloader is in isolated
838      * mode, failure to load the class in this loader will result in a
839      * ClassNotFoundException.
840      *
841      * @param classname The name of the class to be loaded.
842      * Must not be <code>null</code>.
843      * @param resolve <code>true</code> if all classes upon which this class
844      * depends are to be loaded.
845      *
846      * @return the required Class object
847      *
848      * @exception ClassNotFoundException if the requested class does not exist
849      * on the system classpath (when not in isolated mode) or this loader's
850      * classpath.
851      */

852     protected synchronized Class JavaDoc loadClass(String JavaDoc classname, boolean resolve)
853          throws ClassNotFoundException JavaDoc {
854         // 'sync' is needed - otherwise 2 threads can load the same class
855
// twice, resulting in LinkageError: duplicated class definition.
856
// findLoadedClass avoids that, but without sync it won't work.
857

858         Class JavaDoc theClass = findLoadedClass(classname);
859         if (theClass != null) {
860             return theClass;
861         }
862
863         if (isParentFirst(classname)) {
864             try {
865                 theClass = findBaseClass(classname);
866                 // Class loaded from parent loader
867
} catch (ClassNotFoundException JavaDoc cnfe) {
868                 theClass = findClass(classname);
869                // Class loaded from this loader
870
}
871         } else {
872             try {
873                 theClass = findClass(classname);
874                 // Class loaded from this loader
875
} catch (ClassNotFoundException JavaDoc cnfe) {
876                 if (ignoreBase) {
877                     throw cnfe;
878                 }
879                 theClass = findBaseClass(classname);
880                 // Class loaded from parent loader",
881
}
882         }
883
884         if (resolve) {
885             resolveClass(theClass);
886         }
887
888         return theClass;
889     }
890
891     /**
892      * Converts the class dot notation to a filesystem equivalent for
893      * searching purposes.
894      *
895      * @param classname The class name in dot format (eg java.lang.Integer).
896      * Must not be <code>null</code>.
897      *
898      * @return the classname in filesystem format (eg java/lang/Integer.class)
899      */

900     private String JavaDoc getClassFilename(String JavaDoc classname) {
901         return classname.replace('.', '/') + ".class";
902     }
903
904
905     /**
906      * Reads a class definition from a stream.
907      *
908      * @param stream The stream from which the class is to be read.
909      * Must not be <code>null</code>.
910      * @param classname The name of the class in the stream.
911      * Must not be <code>null</code>.
912      * @param container the file or directory containing the class.
913      *
914      * @return the Class object read from the stream.
915      *
916      * @exception IOException if there is a problem reading the class from the
917      * stream.
918      * @exception SecurityException if there is a security problem while
919      * reading the class from the stream.
920      */

921     private Class JavaDoc getClassFromStream(InputStream JavaDoc stream, String JavaDoc classname,
922                                        File JavaDoc container)
923                 throws IOException JavaDoc, SecurityException JavaDoc {
924         ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc();
925         int bytesRead = -1;
926         byte[] buffer = new byte[BUFFER_SIZE];
927
928         while ((bytesRead = stream.read(buffer, 0, BUFFER_SIZE)) != -1) {
929             baos.write(buffer, 0, bytesRead);
930         }
931
932         byte[] classData = baos.toByteArray();
933         return defineClass(classname, classData, 0, classData.length);
934     }
935
936     /**
937      * Searches for and load a class on the classpath of this class loader.
938      *
939      * @param name The name of the class to be loaded. Must not be
940      * <code>null</code>.
941      *
942      * @return the required Class object
943      *
944      * @exception ClassNotFoundException if the requested class does not exist
945      * on this loader's classpath.
946      */

947     public Class JavaDoc findClass(String JavaDoc name) throws ClassNotFoundException JavaDoc {
948         Class JavaDoc c = findClassInComponents(name);
949        
950         return c;
951     }
952
953     /**
954      * Indicate if the given file is in this loader's path
955      *
956      * @param component the file which is to be checked
957      *
958      * @return true if the file is in the class path
959      */

960     protected boolean isInPath(File JavaDoc component) {
961         for (Enumeration JavaDoc e = pathComponents.elements(); e.hasMoreElements();) {
962             File JavaDoc pathComponent = (File JavaDoc) e.nextElement();
963             if (pathComponent.equals(component)) {
964                 return true;
965             }
966         }
967         return false;
968     }
969
970
971     /**
972      * Finds a class on the given classpath.
973      *
974      * @param name The name of the class to be loaded. Must not be
975      * <code>null</code>.
976      *
977      * @return the required Class object
978      *
979      * @exception ClassNotFoundException if the requested class does not exist
980      * on this loader's classpath.
981      */

982     private Class JavaDoc findClassInComponents(String JavaDoc name)
983          throws ClassNotFoundException JavaDoc {
984         // we need to search the components of the path to see if
985
// we can find the class we want.
986
InputStream JavaDoc stream = null;
987         String JavaDoc classFilename = getClassFilename(name);
988         try {
989             Enumeration JavaDoc e = pathComponents.elements();
990             while (e.hasMoreElements()) {
991                 File JavaDoc pathComponent = (File JavaDoc) e.nextElement();
992                 try {
993                     stream = getResourceStream(pathComponent, classFilename);
994                     if (stream != null) {
995                         return getClassFromStream(stream, name, pathComponent);
996                     }
997                 } catch (SecurityException JavaDoc se) {
998                     throw se;
999                 } catch (IOException JavaDoc ioe) {
1000                    throw new ClassNotFoundException JavaDoc("Failed to read file", ioe);
1001                }
1002            }
1003
1004            throw new ClassNotFoundException JavaDoc(name);
1005        } finally {
1006            try {
1007                if (stream != null) {
1008                    stream.close();
1009                }
1010            } catch (IOException JavaDoc e) {
1011                //ignore
1012
}
1013        }
1014    }
1015
1016    /**
1017     * Finds a system class (which should be loaded from the same classloader
1018     * as the Ant core).
1019     *
1020     * For JDK 1.1 compatibility, this uses the findSystemClass method if
1021     * no parent classloader has been specified.
1022     *
1023     * @param name The name of the class to be loaded.
1024     * Must not be <code>null</code>.
1025     *
1026     * @return the required Class object
1027     *
1028     * @exception ClassNotFoundException if the requested class does not exist
1029     * on this loader's classpath.
1030     */

1031    private Class JavaDoc findBaseClass(String JavaDoc name) throws ClassNotFoundException JavaDoc {
1032        if (parent == null) {
1033            return findSystemClass(name);
1034        } else {
1035            return parent.loadClass(name);
1036        }
1037    }
1038
1039    /**
1040     * Cleans up any resources held by this classloader. Any open archive
1041     * files are closed.
1042     */

1043    public synchronized void cleanup() {
1044        for (Enumeration JavaDoc e = zipFiles.elements(); e.hasMoreElements();) {
1045            ZipFile JavaDoc zipFile = (ZipFile JavaDoc) e.nextElement();
1046            try {
1047                zipFile.close();
1048            } catch (IOException JavaDoc ioe) {
1049                // ignore
1050
}
1051        }
1052        zipFiles = new Hashtable JavaDoc();
1053    }
1054
1055    /**
1056     * Get the classpath as a string. useful for debugging.
1057     *
1058     * @return A string classpath.
1059     */

1060    public String JavaDoc getPath() {
1061        String JavaDoc pathSeparator = System.getProperty("path.separator");
1062        StringBuffer JavaDoc classpath = new StringBuffer JavaDoc();
1063        if (parent != null && parent instanceof ArooaClassLoader) {
1064            classpath.append(((ArooaClassLoader) parent).getPath());
1065        }
1066        Enumeration JavaDoc e = pathComponents.elements();
1067        while (e.hasMoreElements()) {
1068            if (classpath.length() > 0) {
1069                classpath.append(pathSeparator);
1070            }
1071            File JavaDoc pathComponent = (File JavaDoc) e.nextElement();
1072            classpath.append(pathComponent.getPath());
1073        }
1074        return classpath.toString();
1075    }
1076}
1077
Popular Tags