View Javadoc

1   /*
2    * $Header: /cvsroot/dspro/dspro/src/java/com/explosion/utilities/classes/StandardClassLoader.java,v 1.3 2005/07/06 10:13:42 uanda Exp $
3    * $Revision: 1.3 $
4    * $Date: 2005/07/06 10:13:42 $
5    *
6    * ====================================================================
7    *
8    * The Apache Software License, Version 1.1
9    *
10   * Copyright (c) 1999 The Apache Software Foundation.  All rights
11   * reserved.
12   *
13   * Redistribution and use in source and binary forms, with or without
14   * modification, are permitted provided that the following conditions
15   * are met:
16   *
17   * 1. Redistributions of source code must retain the above copyright
18   *    notice, this list of conditions and the following disclaimer.
19   *
20   * 2. Redistributions in binary form must reproduce the above copyright
21   *    notice, this list of conditions and the following disclaimer in
22   *    the documentation and/or other materials provided with the
23   *    distribution.
24   *
25   * 3. The end-user documentation included with the redistribution, if
26   *    any, must include the following acknowlegement:
27   *       "This product includes software developed by the
28   *        Apache Software Foundation (http://www.apache.org/)."
29   *    Alternately, this acknowlegement may appear in the software itself,
30   *    if and wherever such third-party acknowlegements normally appear.
31   *
32   * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
33   *    Foundation" must not be used to endorse or promote products derived
34   *    from this software without prior written permission. For written
35   *    permission, please contact apache@apache.org.
36   *
37   * 5. Products derived from this software may not be called "Apache"
38   *    nor may "Apache" appear in their names without prior written
39   *    permission of the Apache Group.
40   *
41   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
42   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
43   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
44   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
45   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
46   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
47   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
48   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
49   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
50   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
51   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52   * SUCH DAMAGE.
53   * ====================================================================
54   *
55   * This software consists of voluntary contributions made by many
56   * individuals on behalf of the Apache Software Foundation.  For more
57   * information on the Apache Software Foundation, please see
58   * <http://www.apache.org/>.
59   *
60   * [Additional notices, if required by prior licensing conditions]
61   *
62   */
63  
64  
65  package com.explosion.utilities.classes;
66  
67  import java.io.File;
68  import java.io.FilePermission;
69  import java.io.IOException;
70  import java.io.InputStream;
71  import java.net.JarURLConnection;
72  import java.net.MalformedURLException;
73  import java.net.URL;
74  import java.net.URLClassLoader;
75  import java.net.URLConnection;
76  import java.net.URLStreamHandler;
77  import java.net.URLStreamHandlerFactory;
78  import java.security.AccessControlException;
79  import java.security.CodeSource;
80  import java.security.Permission;
81  import java.security.PermissionCollection;
82  import java.security.Policy;
83  import java.util.ArrayList;
84  import java.util.Enumeration;
85  import java.util.HashMap;
86  import java.util.Iterator;
87  import java.util.jar.JarFile;
88  import java.util.jar.JarInputStream;
89  import java.util.jar.Manifest;
90  
91  
92  /***
93   * Subclass implementation of <b>java.net.URLClassLoader</b> that knows how
94   * to load classes from disk directories, as well as local and remote JAR
95   * files.  It also implements the <code>Reloader</code> interface, to provide
96   * automatic reloading support to the associated loader.
97   * <p>
98   * In all cases, URLs must conform to the contract specified by
99   * <code>URLClassLoader</code> - any URL that ends with a "/" character is
100  * assumed to represent a directory; all other URLs are assumed to be the
101  * address of a JAR file.
102  * <p>
103  * <strong>IMPLEMENTATION NOTE</strong> - Local repositories are searched in
104  * the order they are added via the initial constructor and/or any subsequent
105  * calls to <code>addRepository()</code>.
106  * <p>
107  * <strong>IMPLEMENTATION NOTE</strong> - At present, there are no dependencies
108  * from this class to any other Catalina class, so that it could be used
109  * independently.
110  *
111  * @author Craig R. McClanahan
112  * @author Remy Maucherat
113  * @version $Revision: 1.3 $ $Date: 2005/07/06 10:13:42 $
114  */
115 
116 public class StandardClassLoader
117     extends URLClassLoader {
118 
119 
120     // ----------------------------------------------------------- Constructors
121 
122 
123     /***
124      * Construct a new ClassLoader with no defined repositories and no
125      * parent ClassLoader.
126      */
127     public StandardClassLoader() {
128 
129         super(new URL[0]);
130         this.parent = getParent();
131         this.system = getSystemClassLoader();
132         securityManager = System.getSecurityManager();
133     }
134 
135 
136     /***
137      * Construct a new ClassLoader with no defined repositories and no
138      * parent ClassLoader, but with a stream handler factory.
139      *
140      * @param factory the URLStreamHandlerFactory to use when creating URLs
141      */
142     public StandardClassLoader(URLStreamHandlerFactory factory) {
143 
144         super(new URL[0], null, factory);
145         this.factory = factory;
146 
147     }
148 
149 
150     /***
151      * Construct a new ClassLoader with no defined repositories and the
152      * specified parent ClassLoader.
153      *
154      * @param parent The parent ClassLoader
155      */
156     public StandardClassLoader(ClassLoader parent) {
157 
158         super((new URL[0]), parent);
159         this.parent = parent;
160         this.system = getSystemClassLoader();
161         securityManager = System.getSecurityManager();
162 
163     }
164 
165 
166     /***
167      * Construct a new ClassLoader with no defined repositories and the
168      * specified parent ClassLoader.
169      *
170      * @param parent The parent ClassLoader
171      * @param factory the URLStreamHandlerFactory to use when creating URLs
172      */
173     public StandardClassLoader(ClassLoader parent,
174                                URLStreamHandlerFactory factory) {
175 
176         super((new URL[0]), parent, factory);
177         this.factory = factory;
178 
179     }
180 
181 
182     /***
183      * Construct a new ClassLoader with the specified repositories and
184      * no parent ClassLoader.
185      *
186      * @param repositories The initial set of repositories
187      */
188     public StandardClassLoader(String repositories[]) {
189 
190         super(convert(repositories));
191         this.parent = getParent();
192         this.system = getSystemClassLoader();
193         securityManager = System.getSecurityManager();
194         if (repositories != null) {
195             for (int i = 0; i < repositories.length; i++)
196                 addRepositoryInternal(repositories[i]);
197         }
198 
199     }
200 
201 
202     /***
203      * Construct a new ClassLoader with the specified repositories and
204      * parent ClassLoader.
205      *
206      * @param repositories The initial set of repositories
207      * @param parent The parent ClassLoader
208      */
209     public StandardClassLoader(String repositories[], ClassLoader parent) {
210 
211         super(convert(repositories), parent);
212         this.parent = parent;
213         this.system = getSystemClassLoader();
214         securityManager = System.getSecurityManager();
215         if (repositories != null) {
216             for (int i = 0; i < repositories.length; i++)
217                 addRepositoryInternal(repositories[i]);
218         }
219 
220     }
221 
222 
223     /***
224      * Construct a new ClassLoader with the specified repositories and
225      * parent ClassLoader.
226      *
227      * @param repositories The initial set of repositories
228      * @param parent The parent ClassLoader
229      */
230     public StandardClassLoader(URL repositories[], ClassLoader parent) {
231 
232         super(repositories, parent);
233         this.parent = parent;
234         this.system = getSystemClassLoader();
235         securityManager = System.getSecurityManager();
236         if (repositories != null) {
237             for (int i = 0; i < repositories.length; i++)
238                 addRepositoryInternal(repositories[i].toString());
239         }
240 
241     }
242 
243 
244     // ----------------------------------------------------- Instance Variables
245 
246     
247     /***
248      * The set of optional packages (formerly standard extensions) that
249      * are available in the repositories associated with this class loader.
250      * Each object in this list is of type
251      * <code>org.apache.catalina.loader.Extension</code>.
252      */
253     protected ArrayList available = new ArrayList();
254 
255 
256     /***
257      * The debugging detail level of this component.
258      */
259     protected int debug = 0;
260 
261 
262     /***
263      * Should this class loader delegate to the parent class loader
264      * <strong>before</strong> searching its own repositories (i.e. the
265      * usual Java2 delegation model)?  If set to <code>false</code>,
266      * this class loader will search its own repositories first, and
267      * delegate to the parent only if the class or resource is not
268      * found locally.
269      */
270     protected boolean delegate = false;
271 
272 
273     /***
274      * The list of local repositories, in the order they should be searched
275      * for locally loaded classes or resources.
276      */
277     protected String repositories[] = new String[0];
278 
279 
280     /***
281      * The set of optional packages (formerly standard extensions) that
282      * are required in the repositories associated with this class loader.
283      * Each object in this list is of type
284      * <code>org.apache.catalina.loader.Extension</code>.
285      */
286     protected ArrayList required = new ArrayList();
287 
288 
289     /***
290      * A list of read File and Jndi Permission's required if this loader
291      * is for a web application context.
292      */
293     private ArrayList permissionList = new ArrayList();
294 
295 
296     /***
297      * The PermissionCollection for each CodeSource for a web
298      * application context.
299      */
300     private HashMap loaderPC = new HashMap();
301 
302 
303     /***
304      * Instance of the SecurityManager installed.
305      */
306     private SecurityManager securityManager = null;
307 
308 
309     /***
310      * Flag that the security policy has been refreshed from file.
311      */
312     private boolean policy_refresh = false;
313 
314     /***
315      * The parent class loader.
316      */
317     private ClassLoader parent = null;
318 
319 
320     /***
321      * The system class loader.
322      */
323     private ClassLoader system = null;
324 
325 
326     /***
327      * URL stream handler for additional protocols.
328      */
329     protected URLStreamHandlerFactory factory = null;
330 
331 
332     // ------------------------------------------------------------- Properties
333 
334 
335     /***
336      * Return the debugging detail level for this component.
337      */
338     public int getDebug() {
339 
340         return (this.debug);
341 
342     }
343 
344 
345     /***
346      * Set the debugging detail level for this component.
347      *
348      * @param debug The new debugging detail level
349      */
350     public void setDebug(int debug) {
351 
352         this.debug = debug;
353 
354     }
355 
356 
357     /***
358      * Return the "delegate first" flag for this class loader.
359      */
360     public boolean getDelegate() {
361 
362         return (this.delegate);
363 
364     }
365 
366 
367     /***
368      * Set the "delegate first" flag for this class loader.
369      *
370      * @param delegate The new "delegate first" flag
371      */
372     public void setDelegate(boolean delegate) {
373 
374         this.delegate = delegate;
375 
376     }
377 
378 
379     /***
380      * If there is a Java SecurityManager create a read FilePermission
381      * or JndiPermission for the file directory path.
382      *
383      * @param path file directory path
384      */
385     public void setPermissions(String path) {
386         if( securityManager != null ) {
387             if( path.startsWith("jndi:") || path.startsWith("jar:jndi:") ) {
388                 permissionList.add(new JndiPermission(path + "*"));
389             } else {
390                 permissionList.add(new FilePermission(path + "-","read"));
391             }
392         }
393     }
394 
395 
396     /***
397      * If there is a Java SecurityManager add a read FilePermission
398      * or JndiPermission for URL.
399      *
400      * @param url URL for a file or directory on local system
401      */
402     public void setPermissions(URL url) {
403         setPermissions(url.toString());
404     }
405 
406 
407     // ------------------------------------------------------- Reloader Methods
408 
409 
410     /***
411      * Add a new repository to the set of places this ClassLoader can look for
412      * classes to be loaded.
413      *
414      * @param repository Name of a source of classes to be loaded, such as a
415      *  directory pathname, a JAR file pathname, or a ZIP file pathname
416      *
417      * @exception IllegalArgumentException if the specified repository is
418      *  invalid or does not exist
419      */
420     public void addRepository(String repository) {
421 
422         if (debug >= 1)
423             log("addRepository(" + repository + ")");
424 
425         // Add this repository to our underlying class loader
426         try {
427             URLStreamHandler streamHandler = null;
428             String protocol = parseProtocol(repository);
429             if (factory != null)
430                 streamHandler = factory.createURLStreamHandler(protocol);
431             URL url = new URL(null, repository, streamHandler);
432             super.addURL(url);
433         } catch (MalformedURLException e) {
434             throw new IllegalArgumentException(e.toString());
435         }
436 
437         // Add this repository to our internal list
438         addRepositoryInternal(repository);
439 
440     }
441 
442 
443     /***
444      * Return a list of "optional packages" (formerly "standard extensions")
445      * that have been declared to be available in the repositories associated
446      * with this class loader, plus any parent class loader implemented with
447      * the same class.
448      */
449     public Extension[] findAvailable() {
450 
451         // Initialize the results with our local available extensions
452         ArrayList results = new ArrayList();
453         Iterator available = this.available.iterator();
454         while (available.hasNext())
455             results.add(available.next());
456 
457         // Trace our parentage tree and add declared extensions when possible
458         ClassLoader loader = this;
459         while (true) {
460             loader = loader.getParent();
461             if (loader == null)
462                 break;
463             if (!(loader instanceof StandardClassLoader))
464                 continue;
465             Extension extensions[] =
466                 ((StandardClassLoader) loader).findAvailable();
467             for (int i = 0; i < extensions.length; i++)
468                 results.add(extensions[i]);
469         }
470 
471         // Return the results as an array
472         Extension extensions[] = new Extension[results.size()];
473         return ((Extension[]) results.toArray(extensions));
474 
475     }
476 
477 
478     /***
479      * Return a String array of the current repositories for this class
480      * loader.  If there are no repositories, a zero-length array is
481      * returned.
482      */
483     public String[] findRepositories() {
484 
485         return (repositories);
486 
487     }
488 
489 
490     /***
491      * Return a list of "optional packages" (formerly "standard extensions")
492      * that have been declared to be required in the repositories associated
493      * with this class loader, plus any parent class loader implemented with
494      * the same class.
495      */
496     public Extension[] findRequired() {
497 
498         // Initialize the results with our local required extensions
499         ArrayList results = new ArrayList();
500         Iterator required = this.required.iterator();
501         while (required.hasNext())
502             results.add(required.next());
503 
504         // Trace our parentage tree and add declared extensions when possible
505         ClassLoader loader = this;
506         while (true) {
507             loader = loader.getParent();
508             if (loader == null)
509                 break;
510             if (!(loader instanceof StandardClassLoader))
511                 continue;
512             Extension extensions[] =
513                 ((StandardClassLoader) loader).findRequired();
514             for (int i = 0; i < extensions.length; i++)
515                 results.add(extensions[i]);
516         }
517 
518         // Return the results as an array
519         Extension extensions[] = new Extension[results.size()];
520         return ((Extension[]) results.toArray(extensions));
521 
522     }
523 
524 
525     /***
526      * This class loader doesn't check for reloading.
527      */
528     public boolean modified() {
529 
530         return (false);
531 
532     }
533 
534 
535     /***
536      * Render a String representation of this object.
537      */
538     public String toString() {
539 
540         StringBuffer sb = new StringBuffer("StandardClassLoader\r\n");
541         sb.append("  available:\r\n");
542         Iterator available = this.available.iterator();
543         while (available.hasNext()) {
544             sb.append("    ");
545             sb.append(available.next().toString());
546             sb.append("\r\n");
547         }
548         sb.append("  delegate: ");
549         sb.append(delegate);
550         sb.append("\r\n");
551         sb.append("  repositories:\r\n");
552         for (int i = 0; i < repositories.length; i++) {
553             sb.append("    ");
554             sb.append(repositories[i]);
555             sb.append("\r\n");
556         }
557         sb.append("  required:\r\n");
558         Iterator required = this.required.iterator();
559         while (required.hasNext()) {
560             sb.append("    ");
561             sb.append(required.next().toString());
562             sb.append("\r\n");
563         }
564         if (this.parent != null) {
565             sb.append("----------> Parent Classloader:\r\n");
566             sb.append(this.parent.toString());
567             sb.append("\r\n");
568         }
569         return (sb.toString());
570 
571     }
572 
573 
574     // ---------------------------------------------------- ClassLoader Methods
575 
576 
577     /***
578      * Find the specified class in our local repositories, if possible.  If
579      * not found, throw <code>ClassNotFoundException</code>.
580      *
581      * @param name Name of the class to be loaded
582      *
583      * @exception ClassNotFoundException if the class was not found
584      */
585     public Class findClass(String name) throws ClassNotFoundException {
586 
587         if (debug >= 3)
588             log("    findClass(" + name + ")");
589 
590         // (1) Permission to define this class when using a SecurityManager
591         if (securityManager != null) {
592             int i = name.lastIndexOf('.');
593             if (i >= 0) {
594                 try {
595                     if (debug >= 4)
596                         log("      securityManager.checkPackageDefinition");
597                     securityManager.checkPackageDefinition(name.substring(0,i));
598                 } catch (Exception se) {
599                     if (debug >= 4)
600                         log("      -->Exception-->ClassNotFoundException", se);
601                     throw new ClassNotFoundException(name);
602                 }
603             }
604         }
605 
606         // Ask our superclass to locate this class, if possible
607         // (throws ClassNotFoundException if it is not found)
608         Class clazz = null;
609         try {
610             if (debug >= 4)
611                 log("      super.findClass(" + name + ")");
612             try {
613                 synchronized (this) {
614                     clazz = findLoadedClass(name);
615                     if (clazz != null)
616                         return clazz;
617                     clazz = super.findClass(name);
618                 }
619             } catch(AccessControlException ace) {
620                 throw new ClassNotFoundException(name);
621             } catch (RuntimeException e) {
622                 if (debug >= 4)
623                     log("      -->RuntimeException Rethrown", e);
624                 throw e;
625             }
626             if (clazz == null) {
627                 if (debug >= 3)
628                     log("    --> Returning ClassNotFoundException");
629                 throw new ClassNotFoundException(name);
630             }
631         } catch (ClassNotFoundException e) {
632             if (debug >= 3)
633                 log("    --> Passing on ClassNotFoundException", e);
634             throw e;
635         }
636 
637         // Return the class we have located
638         if (debug >= 4)
639             log("      Returning class " + clazz);
640         if ((debug >= 4) && (clazz != null))
641             log("      Loaded by " + clazz.getClassLoader());
642         return (clazz);
643 
644     }
645 
646 
647     /***
648      * Find the specified resource in our local repository, and return a
649      * <code>URL</code> refering to it, or <code>null</code> if this resource
650      * cannot be found.
651      *
652      * @param name Name of the resource to be found
653      */
654     public URL findResource(String name) {
655 
656         if (debug >= 3)
657             log("    findResource(" + name + ")");
658 
659         URL url = super.findResource(name);
660         if (debug >= 3) {
661             if (url != null)
662                 log("    --> Returning '" + url.toString() + "'");
663             else
664                 log("    --> Resource not found, returning null");
665         }
666         return (url);
667 
668     }
669 
670 
671     /***
672      * Return an eneration of <code>URLs</code> representing all of the
673      * resources with the given name.  If no resources with this name are
674      * found, return an empty eneration.
675      *
676      * @param name Name of the resources to be found
677      *
678      * @exception IOException if an input/output error occurs
679      */
680     public Enumeration findResources(String name) throws IOException {
681 
682         if (debug >= 3)
683             log("    findResources(" + name + ")");
684         return (super.findResources(name));
685 
686     }
687 
688 
689     /***
690      * Find the resource with the given name.  A resource is some data
691      * (images, audio, text, etc.) that can be accessed by class code in a
692      * way that is independent of the location of the code.  The name of a
693      * resource is a "/"-separated path name that identifies the resource.
694      * If the resource cannot be found, return <code>null</code>.
695      * <p>
696      * This method searches according to the following algorithm, returning
697      * as soon as it finds the appropriate URL.  If the resource cannot be
698      * found, returns <code>null</code>.
699      * <ul>
700      * <li>If the <code>delegate</code> property is set to <code>true</code>,
701      *     call the <code>getResource()</code> method of the parent class
702      *     loader, if any.</li>
703      * <li>Call <code>findResource()</code> to find this resource in our
704      *     locally defined repositories.</li>
705      * <li>Call the <code>getResource()</code> method of the parent class
706      *     loader, if any.</li>
707      * </ul>
708      *
709      * @param name Name of the resource to return a URL for
710      */
711     public URL getResource(String name) {
712 
713         if (debug >= 2)
714             log("getResource(" + name + ")");
715         URL url = null;
716 
717         // (1) Delegate to parent if requested
718         if (delegate) {
719             if (debug >= 3)
720                 log("  Delegating to parent classloader");
721             ClassLoader loader = parent;
722             if (loader == null)
723                 loader = system;
724             url = loader.getResource(name);
725             if (url != null) {
726                 if (debug >= 2)
727                     log("  --> Returning '" + url.toString() + "'");
728                 return (url);
729             }
730         }
731 
732         // (2) Search local repositories
733         if (debug >= 3)
734             log("  Searching local repositories");
735         url = findResource(name);
736         if (url != null) {
737             if (debug >= 2)
738                 log("  --> Returning '" + url.toString() + "'");
739             return (url);
740         }
741 
742         // (3) Delegate to parent unconditionally if not already attempted
743         if( !delegate ) {
744             ClassLoader loader = parent;
745             if (loader == null)
746                 loader = system;
747             url = loader.getResource(name);
748             if (url != null) {
749                 if (debug >= 2)
750                     log("  --> Returning '" + url.toString() + "'");
751                 return (url);
752             }
753         }
754 
755         // (4) Resource was not found
756         if (debug >= 2)
757             log("  --> Resource not found, returning null");
758         return (null);
759 
760     }
761 
762 
763     /***
764      * Find the resource with the given name, and return an input stream
765      * that can be used for reading it.  The search order is as described
766      * for <code>getResource()</code>, after checking to see if the resource
767      * data has been previously cached.  If the resource cannot be found,
768      * return <code>null</code>.
769      *
770      * @param name Name of the resource to return an input stream for
771      */
772     public InputStream getResourceAsStream(String name) {
773 
774         if (debug >= 2)
775             log("getResourceAsStream(" + name + ")");
776         InputStream stream = null;
777 
778         // (0) Check for a cached copy of this resource
779         stream = findLoadedResource(name);
780         if (stream != null) {
781             if (debug >= 2)
782                 log("  --> Returning stream from cache");
783             return (stream);
784         }
785 
786         // (1) Delegate to parent if requested
787         if (delegate) {
788             if (debug >= 3)
789                 log("  Delegating to parent classloader");
790             ClassLoader loader = parent;
791             if (loader == null)
792                 loader = system;
793             stream = loader.getResourceAsStream(name);
794             if (stream != null) {
795                 // FIXME - cache???
796                 if (debug >= 2)
797                     log("  --> Returning stream from parent");
798                 return (stream);
799             }
800         }
801 
802         // (2) Search local repositories
803         if (debug >= 3)
804             log("  Searching local repositories");
805         URL url = findResource(name);
806         if (url != null) {
807             // FIXME - cache???
808             if (debug >= 2)
809                 log("  --> Returning stream from local");
810             try {
811                return (url.openStream());
812             } catch (IOException e) {
813                log("url.openStream(" + url.toString() + ")", e);
814                return (null);
815             }
816         }
817 
818         // (3) Delegate to parent unconditionally
819         if (!delegate) {
820             if (debug >= 3)
821                 log("  Delegating to parent classloader");
822             ClassLoader loader = parent;
823             if (loader == null)
824                 loader = system;
825             stream = loader.getResourceAsStream(name);
826             if (stream != null) {
827                 // FIXME - cache???
828                 if (debug >= 2)
829                     log("  --> Returning stream from parent");
830                 return (stream);
831             }
832         }
833 
834         // (4) Resource was not found
835         if (debug >= 2)
836             log("  --> Resource not found, returning null");
837         return (null);
838 
839     }
840 
841 
842     /***
843      * Load the class with the specified name.  This method searches for
844      * classes in the same manner as <code>loadClass(String, boolean)</code>
845      * with <code>false</code> as the second argument.
846      *
847      * @param name Name of the class to be loaded
848      *
849      * @exception ClassNotFoundException if the class was not found
850      */
851     public Class loadClass(String name) throws ClassNotFoundException {
852 
853         return (loadClass(name, false));
854 
855     }
856 
857 
858     /***
859      * Load the class with the specified name, searching using the following
860      * algorithm until it finds and returns the class.  If the class cannot
861      * be found, returns <code>ClassNotFoundException</code>.
862      * <ul>
863      * <li>Call <code>findLoadedClass(String)</code> to check if the
864      *     class has already been loaded.  If it has, the same
865      *     <code>Class</code> object is returned.</li>
866      * <li>If the <code>delegate</code> property is set to <code>true</code>,
867      *     call the <code>loadClass()</code> method of the parent class
868      *     loader, if any.</li>
869      * <li>Call <code>findClass()</code> to find this class in our locally
870      *     defined repositories.</li>
871      * <li>Call the <code>loadClass()</code> method of our parent
872      *     class loader, if any.</li>
873      * </ul>
874      * If the class was found using the above steps, and the
875      * <code>resolve</code> flag is <code>true</code>, this method will then
876      * call <code>resolveClass(Class)</code> on the resulting Class object.
877      *
878      * @param name Name of the class to be loaded
879      * @param resolve If <code>true</code> then resolve the class
880      *
881      * @exception ClassNotFoundException if the class was not found
882      */
883     public Class loadClass(String name, boolean resolve)
884         throws ClassNotFoundException {
885 
886         if (debug >= 2)
887             log("loadClass(" + name + ", " + resolve + ")");
888         Class clazz = null;
889 
890         // (0) Check our previously loaded class cache
891         clazz = findLoadedClass(name);
892         if (clazz != null) {
893             if (debug >= 3)
894                 log("  Returning class from cache");
895             if (resolve)
896                 resolveClass(clazz);
897             return (clazz);
898         }
899 
900         // If a system class, use system class loader
901         if( name.startsWith("java.") ) {
902             ClassLoader loader = system;
903             clazz = loader.loadClass(name);
904             if (clazz != null) {
905                 if (resolve)
906                     resolveClass(clazz);
907                 return (clazz);
908             }
909             throw new ClassNotFoundException(name);
910         }
911 
912         // (.5) Permission to access this class when using a SecurityManager
913         if (securityManager != null) {
914             int i = name.lastIndexOf('.');
915             if (i >= 0) {
916                 try {
917                     securityManager.checkPackageAccess(name.substring(0,i));
918                 } catch (SecurityException se) {
919                     String error = "Security Violation, attempt to use " +
920                         "Restricted Class: " + name;
921                     System.out.println(error);
922                     se.printStackTrace();
923                     log(error);
924                     throw new ClassNotFoundException(error);
925                 }
926             }
927         }
928 
929         // (1) Delegate to our parent if requested
930         if (delegate) {
931             if (debug >= 3)
932                 log("  Delegating to parent classloader");
933             ClassLoader loader = parent;
934             if (loader == null)
935                 loader = system;
936             try {
937                 clazz = loader.loadClass(name);
938                 if (clazz != null) {
939                     if (debug >= 3)
940                         log("  Loading class from parent");
941                     if (resolve)
942                         resolveClass(clazz);
943                     return (clazz);
944                 }
945             } catch (ClassNotFoundException e) {
946                 ;
947             }
948         }
949 
950         // (2) Search local repositories
951         if (debug >= 3)
952             log("  Searching local repositories");
953         try {
954             clazz = findClass(name);
955             if (clazz != null) {
956                 if (debug >= 3)
957                     log("  Loading class from local repository");
958                 if (resolve)
959                     resolveClass(clazz);
960                 return (clazz);
961             }
962         } catch (ClassNotFoundException e) {
963             ;
964         }
965 
966         // (3) Delegate to parent unconditionally
967         if (!delegate) {
968             if (debug >= 3)
969                 log("  Delegating to parent classloader");
970             ClassLoader loader = parent;
971             if (loader == null)
972                 loader = system;
973             try {
974                 clazz = loader.loadClass(name);
975                 if (clazz != null) {
976                     if (debug >= 3)
977                         log("  Loading class from parent");
978                     if (resolve)
979                         resolveClass(clazz);
980                     return (clazz);
981                 }
982             } catch (ClassNotFoundException e) {
983                 ;
984             }
985         }
986 
987         // This class was not found
988         throw new ClassNotFoundException(name);
989 
990     }
991 
992 
993     /***
994      * Get the Permissions for a CodeSource.  If this instance
995      * of StandardClassLoader is for a web application context,
996      * add read FilePermissions for the base directory (if unpacked),
997      * the context URL, and jar file resources.
998      *
999      * @param CodeSource where the code was loaded from
1000      * @return PermissionCollection for CodeSource
1001      */
1002     protected final PermissionCollection getPermissions(CodeSource codeSource) {
1003         if (!policy_refresh) {
1004             // Refresh the security policies
1005             Policy policy = Policy.getPolicy();
1006             policy.refresh();
1007             policy_refresh = true;
1008         }
1009         String codeUrl = codeSource.getLocation().toString();
1010         PermissionCollection pc;
1011         if ((pc = (PermissionCollection)loaderPC.get(codeUrl)) == null) {
1012             pc = super.getPermissions(codeSource);
1013             if (pc != null) {
1014                 Iterator perms = permissionList.iterator();
1015                 while (perms.hasNext()) {
1016                     Permission p = (Permission)perms.next();
1017                     pc.add(p);
1018                 }
1019                 loaderPC.put(codeUrl,pc);
1020             }
1021         }
1022         return (pc);
1023 
1024     }
1025 
1026 
1027     // ------------------------------------------------------ Protected Methods
1028 
1029 
1030     /***
1031      * Parse URL protocol.
1032      *
1033      * @return String protocol
1034      */
1035     protected static String parseProtocol(String spec) {
1036         if (spec == null)
1037             return "";
1038         int pos = spec.indexOf(':');
1039         if (pos <= 0)
1040             return "";
1041         return spec.substring(0, pos).trim();
1042     }
1043 
1044 
1045     /***
1046      * Add a repository to our internal array only.
1047      *
1048      * @param repository The new repository
1049      *
1050      * @exception IllegalArgumentException if the manifest of a JAR file
1051      *  cannot be processed correctly
1052      */
1053     protected void addRepositoryInternal(String repository) {
1054 
1055         URLStreamHandler streamHandler = null;
1056         String protocol = parseProtocol(repository);
1057         if (factory != null)
1058             streamHandler = factory.createURLStreamHandler(protocol);
1059 
1060         // Validate the manifest of a JAR file repository
1061         if (!repository.endsWith(File.separator) &&
1062             !repository.endsWith("/")) {
1063             JarFile jarFile = null;
1064             try {
1065                 Manifest manifest = null;
1066                 if (repository.startsWith("jar:")) {
1067                     URL url = new URL(null, repository, streamHandler);
1068                     JarURLConnection conn =
1069                         (JarURLConnection) url.openConnection();
1070                     conn.setAllowUserInteraction(false);
1071                     conn.setDoInput(true);
1072                     conn.setDoOutput(false);
1073                     conn.connect();
1074                     jarFile = conn.getJarFile();
1075                 } else if (repository.startsWith("file://")) {
1076                     jarFile = new JarFile(repository.substring(7));
1077                 } else if (repository.startsWith("file:")) {
1078                     jarFile = new JarFile(repository.substring(5));
1079                 } else if (repository.endsWith(".jar")) {
1080                     URL url = new URL(null, repository, streamHandler);
1081                     URLConnection conn = url.openConnection();
1082                     JarInputStream jis =
1083                         new JarInputStream(conn.getInputStream());
1084                     manifest = jis.getManifest();
1085                 } else {
1086                     throw new IllegalArgumentException
1087                         ("addRepositoryInternal:  Invalid URL '" +
1088                          repository + "'");
1089                 }
1090                 if (!((manifest == null) && (jarFile == null))) {
1091                     if ((manifest == null) && (jarFile != null))
1092                         manifest = jarFile.getManifest();
1093                     if (manifest != null) {
1094                         Iterator extensions =
1095                             Extension.getAvailable(manifest).iterator();
1096                         while (extensions.hasNext())
1097                             available.add(extensions.next());
1098                         extensions =
1099                             Extension.getRequired(manifest).iterator();
1100                         while (extensions.hasNext())
1101                             required.add(extensions.next());
1102                     }
1103                 }
1104             } catch (Throwable t) {
1105                 t.printStackTrace();
1106                 throw new IllegalArgumentException
1107                     ("addRepositoryInternal: " + t);
1108             } finally {
1109                 if (jarFile != null) {
1110                     try {
1111                         jarFile.close();
1112                     } catch (Throwable t) {}
1113                 }
1114             }
1115         }
1116 
1117         // Add this repository to our internal list
1118         synchronized (repositories) {
1119             String results[] = new String[repositories.length + 1];
1120             System.arraycopy(repositories, 0, results, 0, repositories.length);
1121             results[repositories.length] = repository;
1122             repositories = results;
1123         }
1124 
1125     }
1126 
1127 
1128     /***
1129      * Convert an array of String to an array of URL and return it.
1130      *
1131      * @param input The array of String to be converted
1132      */
1133     protected static URL[] convert(String input[]) {
1134         return convert(input, null);
1135     }
1136 
1137 
1138     /***
1139      * Convert an array of String to an array of URL and return it.
1140      *
1141      * @param input The array of String to be converted
1142      * @param factory Handler factory to use to generate the URLs
1143      */
1144     protected static URL[] convert(String input[],
1145                                    URLStreamHandlerFactory factory) {
1146 
1147         URLStreamHandler streamHandler = null;
1148 
1149         URL url[] = new URL[input.length];
1150         for (int i = 0; i < url.length; i++) {
1151             try {
1152                 String protocol = parseProtocol(input[i]);
1153                 if (factory != null)
1154                     streamHandler = factory.createURLStreamHandler(protocol);
1155                 else
1156                     streamHandler = null;
1157                 url[i] = new URL(null, input[i], streamHandler);
1158             } catch (MalformedURLException e) {
1159                 url[i] = null;
1160             }
1161         }
1162         return (url);
1163 
1164     }
1165 
1166 
1167     /***
1168      * Finds the resource with the given name if it has previously been
1169      * loaded and cached by this class loader, and return an input stream
1170      * to the resource data.  If this resource has not been cached, return
1171      * <code>null</code>.
1172      *
1173      * @param name Name of the resource to return
1174      */
1175     protected InputStream findLoadedResource(String name) {
1176 
1177         return (null);  // FIXME - findLoadedResource()
1178 
1179     }
1180 
1181 
1182     /***
1183      * Log a debugging output message.
1184      *
1185      * @param message Message to be logged
1186      */
1187     private void log(String message) {
1188 
1189         //log.debug("StandardClassLoader: " + message);
1190         System.out.println(message);
1191     }
1192 
1193 
1194     /***
1195      * Log a debugging output message with an exception.
1196      *
1197      * @param message Message to be logged
1198      * @param throwable Exception to be logged
1199      */
1200     private void log(String message, Throwable throwable) {
1201 
1202         System.out.println("StandardClassLoader: " + message);
1203         throwable.printStackTrace(System.out);
1204 
1205     }
1206 
1207 
1208 }
1209