Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: java/net/URLClassLoader.java


1   /* URLClassLoader.java --  ClassLoader that loads classes from one or more URLs
2      Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005
3      Free Software Foundation, Inc.
4   
5   This file is part of GNU Classpath.
6   
7   GNU Classpath is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2, or (at your option)
10  any later version.
11  
12  GNU Classpath is distributed in the hope that it will be useful, but
13  WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  General Public License for more details.
16  
17  You should have received a copy of the GNU General Public License
18  along with GNU Classpath; see the file COPYING.  If not, write to the
19  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20  02110-1301 USA.
21  
22  Linking this library statically or dynamically with other modules is
23  making a combined work based on this library.  Thus, the terms and
24  conditions of the GNU General Public License cover the whole
25  combination.
26  
27  As a special exception, the copyright holders of this library give you
28  permission to link this library with independent modules to produce an
29  executable, regardless of the license terms of these independent
30  modules, and to copy and distribute the resulting executable under
31  terms of your choice, provided that you also meet, for each linked
32  independent module, the terms and conditions of the license of that
33  module.  An independent module is a module which is not derived from
34  or based on this library.  If you modify this library, you may extend
35  this exception to your version of the library, but you are not
36  obligated to do so.  If you do not wish to do so, delete this
37  exception statement from your version. */
38  
39  
40  package java.net;
41  
42  import java.io.ByteArrayOutputStream;
43  import java.io.EOFException;
44  import java.io.File;
45  import java.io.FileInputStream;
46  import java.io.FilePermission;
47  import java.io.IOException;
48  import java.io.InputStream;
49  import java.security.AccessControlContext;
50  import java.security.AccessController;
51  import java.security.CodeSource;
52  import java.security.PermissionCollection;
53  import java.security.PrivilegedAction;
54  import java.security.SecureClassLoader;
55  import java.security.cert.Certificate;
56  import java.util.Enumeration;
57  import java.util.HashMap;
58  import java.util.Iterator;
59  import java.util.StringTokenizer;
60  import java.util.Vector;
61  import java.util.jar.Attributes;
62  import java.util.jar.JarEntry;
63  import java.util.jar.JarFile;
64  import java.util.jar.Manifest;
65  
66  
67  /**
68   * A secure class loader that can load classes and resources from
69   * multiple locations.  Given an array of <code>URL</code>s this class
70   * loader will retrieve classes and resources by fetching them from
71   * possible remote locations.  Each <code>URL</code> is searched in
72   * order in which it was added.  If the file portion of the
73   * <code>URL</code> ends with a '/' character then it is interpreted
74   * as a base directory, otherwise it is interpreted as a jar file from
75   * which the classes/resources are resolved.
76   *
77   * <p>New instances can be created by two static
78   * <code>newInstance()</code> methods or by three public
79   * contructors. Both ways give the option to supply an initial array
80   * of <code>URL</code>s and (optionally) a parent classloader (that is
81   * different from the standard system class loader).</p>
82   *
83   * <p>Normally creating a <code>URLClassLoader</code> throws a
84   * <code>SecurityException</code> if a <code>SecurityManager</code> is
85   * installed and the <code>checkCreateClassLoader()</code> method does
86   * not return true.  But the <code>newInstance()</code> methods may be
87   * used by any code as long as it has permission to acces the given
88   * <code>URL</code>s.  <code>URLClassLoaders</code> created by the
89   * <code>newInstance()</code> methods also explicitly call the
90   * <code>checkPackageAccess()</code> method of
91   * <code>SecurityManager</code> if one is installed before trying to
92   * load a class.  Note that only subclasses of
93   * <code>URLClassLoader</code> can add new URLs after the
94   * URLClassLoader had been created. But it is always possible to get
95   * an array of all URLs that the class loader uses to resolve classes
96   * and resources by way of the <code>getURLs()</code> method.</p>
97   *
98   * <p>Open issues:
99   * <ul>
100  *
101  * <li>Should the URLClassLoader actually add the locations found in
102  * the manifest or is this the responsibility of some other
103  * loader/(sub)class?  (see <a
104  * href="http://java.sun.com/products/jdk/1.4/docs/guide/extensions/spec.html">
105  * Extension Mechanism Architecture - Bundles Extensions</a>)</li>
106  *
107  * <li>How does <code>definePackage()</code> and sealing work
108  * precisely?</li>
109  *
110  * <li>We save and use the security context (when a created by
111  * <code>newInstance()</code> but do we have to use it in more
112  * places?</li>
113  *
114  * <li>The use of <code>URLStreamHandler</code>s has not been tested.</li>
115  *
116  * </ul>
117  * </p>
118  *
119  * @since 1.2
120  *
121  * @author Mark Wielaard (mark@klomp.org)
122  * @author Wu Gansha (gansha.wu@intel.com)
123  */
124 public class URLClassLoader extends SecureClassLoader
125 {
126   // Class Variables
127 
128   /**
129    * A global cache to store mappings between URLLoader and URL,
130    * so we can avoid do all the homework each time the same URL
131    * comes.
132    * XXX - Keeps these loaders forever which prevents garbage collection.
133    */
134   private static HashMap urlloaders = new HashMap();
135 
136   /**
137    * A cache to store mappings between handler factory and its
138    * private protocol handler cache (also a HashMap), so we can avoid
139    * create handlers each time the same protocol comes.
140    */
141   private static HashMap factoryCache = new HashMap(5);
142 
143   // Instance variables
144 
145   /** Locations to load classes from */
146   private final Vector urls = new Vector();
147 
148   /**
149    * Store pre-parsed information for each url into this vector: each
150    * element is a URL loader.  A jar file has its own class-path
151    * attribute which adds to the URLs that will be searched, but this
152    * does not add to the list of urls.
153    */
154   private final Vector urlinfos = new Vector();
155 
156   /** Factory used to get the protocol handlers of the URLs */
157   private final URLStreamHandlerFactory factory;
158 
159   /**
160    * The security context when created from <code>newInstance()</code>
161    * or null when created through a normal constructor or when no
162    * <code>SecurityManager</code> was installed.
163    */
164   private final AccessControlContext securityContext;
165 
166   // Helper classes
167 
168   /**
169    * A <code>URLLoader</code> contains all logic to load resources from a
170    * given base <code>URL</code>.
171    */
172   abstract static class URLLoader
173   {
174     /**
175      * Our classloader to get info from if needed.
176      */
177     final URLClassLoader classloader;
178 
179     /**
180      * The base URL from which all resources are loaded.
181      */
182     final URL baseURL;
183 
184     /**
185      * A <code>CodeSource</code> without any associated certificates.
186      * It is common for classes to not have certificates associated
187      * with them.  If they come from the same <code>URLLoader</code>
188      * then it is safe to share the associated <code>CodeSource</code>
189      * between them since <code>CodeSource</code> is immutable.
190      */
191     final CodeSource noCertCodeSource;
192 
193     URLLoader(URLClassLoader classloader, URL baseURL)
194     {
195       this(classloader, baseURL, baseURL);
196     }
197 
198     URLLoader(URLClassLoader classloader, URL baseURL, URL overrideURL)
199     {
200       this.classloader = classloader;
201       this.baseURL = baseURL;
202       this.noCertCodeSource = new CodeSource(overrideURL, null);
203     }
204 
205     /**
206      * Returns a <code>Resource</code> loaded by this
207      * <code>URLLoader</code>, or <code>null</code> when no
208      * <code>Resource</code> with the given name exists.
209      */
210     abstract Resource getResource(String s);
211 
212     /**
213      * Returns the <code>Manifest</code> associated with the
214      * <code>Resource</code>s loaded by this <code>URLLoader</code> or
215      * <code>null</code> there is no such <code>Manifest</code>.
216      */
217     Manifest getManifest()
218     {
219       return null;
220     }
221 
222     Vector getClassPath()
223     {
224       return null;
225     }
226   }
227 
228   /**
229    * A <code>Resource</code> represents a resource in some
230    * <code>URLLoader</code>. It also contains all information (e.g.,
231    * <code>URL</code>, <code>CodeSource</code>, <code>Manifest</code> and
232    * <code>InputStream</code>) that is necessary for loading resources
233    * and creating classes from a <code>URL</code>.
234    */
235   abstract static class Resource
236   {
237     final URLLoader loader;
238     final String name;
239 
240     Resource(URLLoader loader, String name)
241     {
242       this.loader = loader;
243       this.name = name;
244     }
245 
246     /**
247      * Returns the non-null <code>CodeSource</code> associated with
248      * this resource.
249      */
250     CodeSource getCodeSource()
251     {
252       Certificate[] certs = getCertificates();
253       if (certs == null)
254         return loader.noCertCodeSource;
255       else
256         return new CodeSource(loader.baseURL, certs);
257     }
258 
259     /**
260      * Returns <code>Certificates</code> associated with this
261      * resource, or null when there are none.
262      */
263     Certificate[] getCertificates()
264     {
265       return null;
266     }
267 
268     /**
269      * Return a <code>URL</code> that can be used to access this resource.
270      */
271     abstract URL getURL();
272 
273     /**
274      * Returns the size of this <code>Resource</code> in bytes or
275      * <code>-1</code> when unknown.
276      */
277     abstract int getLength();
278 
279     /**
280      * Returns the non-null <code>InputStream</code> through which
281      * this resource can be loaded.
282      */
283     abstract InputStream getInputStream() throws IOException;
284   }
285 
286   /**
287    * A <code>JarURLLoader</code> is a type of <code>URLLoader</code>
288    * only loading from jar url.
289    */
290   static final class JarURLLoader extends URLLoader
291   {
292     final JarFile jarfile; // The jar file for this url
293     final URL baseJarURL; // Base jar: url for all resources loaded from jar
294 
295     Vector classPath;  // The "Class-Path" attribute of this Jar's manifest
296 
297     public JarURLLoader(URLClassLoader classloader, URL baseURL)
298     {
299       super(classloader, baseURL);
300 
301       // Cache url prefix for all resources in this jar url.
302       String external = baseURL.toExternalForm();
303       StringBuffer sb = new StringBuffer(external.length() + 6);
304       sb.append("jar:");
305       sb.append(external);
306       sb.append("!/");
307       String jarURL = sb.toString();
308 
309       this.classPath = null;
310       URL baseJarURL = null;
311       JarFile jarfile = null;
312       try
313   {
314     baseJarURL =
315       new URL(null, jarURL, classloader.getURLStreamHandler("jar"));
316     
317     jarfile =
318       ((JarURLConnection) baseJarURL.openConnection()).getJarFile();
319     
320     Manifest manifest;
321     Attributes attributes;
322     String classPathString;
323 
324     if ((manifest = jarfile.getManifest()) != null
325         && (attributes = manifest.getMainAttributes()) != null
326         && ((classPathString 
327        = attributes.getValue(Attributes.Name.CLASS_PATH)) 
328       != null))
329       {
330         this.classPath = new Vector();
331         
332         StringTokenizer st = new StringTokenizer(classPathString, " ");
333         while (st.hasMoreElements ()) 
334     {  
335       String e = st.nextToken ();
336       try
337         {
338           URL url = new URL(baseURL, e);
339           this.classPath.add(url);
340         } 
341       catch (java.net.MalformedURLException xx)
342         {
343           // Give up
344         }
345     }
346       }
347   }
348       catch (IOException ioe)
349         {
350     /* ignored */
351         }
352 
353       this.baseJarURL = baseJarURL;
354       this.jarfile = jarfile;
355     }
356 
357     /** get resource with the name "name" in the jar url */
358     Resource getResource(String name)
359     {
360       if (jarfile == null)
361         return null;
362 
363       if (name.startsWith("/"))
364         name = name.substring(1);
365 
366       JarEntry je = jarfile.getJarEntry(name);
367       if (je != null)
368         return new JarURLResource(this, name, je);
369       else
370         return null;
371     }
372 
373     Manifest getManifest()
374     {
375       try
376         {
377           return (jarfile == null) ? null : jarfile.getManifest();
378         }
379       catch (IOException ioe)
380         {
381           return null;
382         }
383     }
384 
385     Vector getClassPath()
386     {
387       return classPath;
388     }
389   }
390 
391   static final class JarURLResource extends Resource
392   {
393     private final JarEntry entry;
394 
395     JarURLResource(JarURLLoader loader, String name, JarEntry entry)
396     {
397       super(loader, name);
398       this.entry = entry;
399     }
400 
401     InputStream getInputStream() throws IOException
402     {
403       return ((JarURLLoader) loader).jarfile.getInputStream(entry);
404     }
405 
406     int getLength()
407     {
408       return (int) entry.getSize();
409     }
410 
411     Certificate[] getCertificates()
412     {
413       // We have to get the entry from the jar file again, because the
414       // certificates will not be available until the entire entry has
415       // been read.
416       return ((JarEntry) ((JarURLLoader) loader).jarfile.getEntry(name))
417         .getCertificates();
418     }
419 
420     URL getURL()
421     {
422       try
423         {
424           return new URL(((JarURLLoader) loader).baseJarURL, name,
425                          loader.classloader.getURLStreamHandler("jar"));
426         }
427       catch (MalformedURLException e)
428         {
429           InternalError ie = new InternalError();
430           ie.initCause(e);
431           throw ie;
432         }
433     }
434   }
435 
436   /**
437    * Loader for remote directories.
438    */
439   static final class RemoteURLLoader extends URLLoader
440   {
441     private final String protocol;
442 
443     RemoteURLLoader(URLClassLoader classloader, URL url)
444     {
445       super(classloader, url);
446       protocol = url.getProtocol();
447     }
448 
449     /**
450      * Get a remote resource.
451      * Returns null if no such resource exists.
452      */
453     Resource getResource(String name)
454     {
455       try
456         {
457           URL url =
458             new URL(baseURL, name, classloader.getURLStreamHandler(protocol));
459           URLConnection connection = url.openConnection();
460 
461           // Open the connection and check the stream
462           // just to be sure it exists.
463           int length = connection.getContentLength();
464           InputStream stream = connection.getInputStream();
465 
466           // We can do some extra checking if it is a http request
467           if (connection instanceof HttpURLConnection)
468             {
469               int response =
470                 ((HttpURLConnection) connection).getResponseCode();
471               if (response / 100 != 2)
472                 return null;
473             }
474 
475           if (stream != null)
476             return new RemoteResource(this, name, url, stream, length);
477           else
478             return null;
479         }
480       catch (IOException ioe)
481         {
482           return null;
483         }
484     }
485   }
486 
487   /**
488    * A resource from some remote location.
489    */
490   static final class RemoteResource extends Resource
491   {
492     private final URL url;
493     private final InputStream stream;
494     private final int length;
495 
496     RemoteResource(RemoteURLLoader loader, String name, URL url,
497                    InputStream stream, int length)
498     {
499       super(loader, name);
500       this.url = url;
501       this.stream = stream;
502       this.length = length;
503     }
504 
505     InputStream getInputStream() throws IOException
506     {
507       return stream;
508     }
509 
510     public int getLength()
511     {
512       return length;
513     }
514 
515     public URL getURL()
516     {
517       return url;
518     }
519   }
520 
521   /**
522    * A <code>FileURLLoader</code> is a type of <code>URLLoader</code>
523    * only loading from file url.
524    */
525   static final class FileURLLoader extends URLLoader
526   {
527     File dir; //the file for this file url
528 
529     FileURLLoader(URLClassLoader classloader, URL url)
530     {
531       super(classloader, url);
532       dir = new File(baseURL.getFile());
533     }
534 
535     /** get resource with the name "name" in the file url */
536     Resource getResource(String name)
537     {
538       File file = new File(dir, name);
539       if (file.exists() && !file.isDirectory())
540         return new FileResource(this, name, file);
541       return null;
542     }
543   }
544 
545   static final class FileResource extends Resource
546   {
547     final File file;
548 
549     FileResource(FileURLLoader loader, String name, File file)
550     {
551       super(loader, name);
552       this.file = file;
553     }
554 
555     InputStream getInputStream() throws IOException
556     {
557       return new FileInputStream(file);
558     }
559 
560     public int getLength()
561     {
562       return (int) file.length();
563     }
564 
565     public URL getURL()
566     {
567       try
568         {
569           return new URL(loader.baseURL, name,
570                          loader.classloader.getURLStreamHandler("file"));
571         }
572       catch (MalformedURLException e)
573         {
574           InternalError ie = new InternalError();
575           ie.initCause(e);
576           throw ie;
577         }
578     }
579   }
580 
581   // Constructors
582 
583   /**
584    * Creates a URLClassLoader that gets classes from the supplied URLs.
585    * To determine if this classloader may be created the constructor of
586    * the super class (<code>SecureClassLoader</code>) is called first, which
587    * can throw a SecurityException. Then the supplied URLs are added
588    * in the order given to the URLClassLoader which uses these URLs to
589    * load classes and resources (after using the default parent ClassLoader).
590    *
591    * @param urls Locations that should be searched by this ClassLoader when
592    * resolving Classes or Resources.
593    * @exception SecurityException if the SecurityManager disallows the
594    * creation of a ClassLoader.
595    * @see SecureClassLoader
596    */
597   public URLClassLoader(URL[] urls) throws SecurityException
598   {
599     super();
600     this.factory = null;
601     this.securityContext = null;
602     addURLs(urls);
603   }
604 
605   /**
606    * Creates a <code>URLClassLoader</code> that gets classes from the supplied
607    * <code>URL</code>s.
608    * To determine if this classloader may be created the constructor of
609    * the super class (<code>SecureClassLoader</code>) is called first, which
610    * can throw a SecurityException. Then the supplied URLs are added
611    * in the order given to the URLClassLoader which uses these URLs to
612    * load classes and resources (after using the supplied parent ClassLoader).
613    * @param urls Locations that should be searched by this ClassLoader when
614    * resolving Classes or Resources.
615    * @param parent The parent class loader used before trying this class
616    * loader.
617    * @exception SecurityException if the SecurityManager disallows the
618    * creation of a ClassLoader.
619    * @exception SecurityException
620    * @see SecureClassLoader
621    */
622   public URLClassLoader(URL[] urls, ClassLoader parent)
623     throws SecurityException
624   {
625     super(parent);
626     this.factory = null;
627     this.securityContext = null;
628     addURLs(urls);
629   }
630 
631   // Package-private to avoid a trampoline constructor.
632   /**
633    * Package-private constructor used by the static
634    * <code>newInstance(URL[])</code> method.  Creates an
635    * <code>URLClassLoader</code> with the given parent but without any
636    * <code>URL</code>s yet. This is used to bypass the normal security
637    * check for creating classloaders, but remembers the security
638    * context which will be used when defining classes.  The
639    * <code>URL</code>s to load from must be added by the
640    * <code>newInstance()</code> method in the security context of the
641    * caller.
642    *
643    * @param securityContext the security context of the unprivileged code.
644    */
645   URLClassLoader(ClassLoader parent, AccessControlContext securityContext)
646   {
647     super(parent);
648     this.factory = null;
649     this.securityContext = securityContext;
650   }
651 
652   /**
653    * Creates a URLClassLoader that gets classes from the supplied URLs.
654    * To determine if this classloader may be created the constructor of
655    * the super class (<CODE>SecureClassLoader</CODE>) is called first, which
656    * can throw a SecurityException. Then the supplied URLs are added
657    * in the order given to the URLClassLoader which uses these URLs to
658    * load classes and resources (after using the supplied parent ClassLoader).
659    * It will use the supplied <CODE>URLStreamHandlerFactory</CODE> to get the
660    * protocol handlers of the supplied URLs.
661    * @param urls Locations that should be searched by this ClassLoader when
662    * resolving Classes or Resources.
663    * @param parent The parent class loader used before trying this class
664    * loader.
665    * @param factory Used to get the protocol handler for the URLs.
666    * @exception SecurityException if the SecurityManager disallows the
667    * creation of a ClassLoader.
668    * @exception SecurityException
669    * @see SecureClassLoader
670    */
671   public URLClassLoader(URL[] urls, ClassLoader parent,
672                         URLStreamHandlerFactory factory)
673     throws SecurityException
674   {
675     super(parent);
676     this.securityContext = null;
677     this.factory = factory;
678     addURLs(urls);
679 
680     // If this factory is still not in factoryCache, add it,
681     //   since we only support three protocols so far, 5 is enough
682     //   for cache initial size
683     synchronized (factoryCache)
684       {
685         if (factory != null && factoryCache.get(factory) == null)
686           factoryCache.put(factory, new HashMap(5));
687       }
688   }
689 
690   // Methods
691 
692   /**
693    * Adds a new location to the end of the internal URL store.
694    * @param newUrl the location to add
695    */
696   protected void addURL(URL newUrl)
697   {
698     urls.add(newUrl);
699     addURLImpl(newUrl);
700   }
701 
702   private void addURLImpl(URL newUrl)
703   {
704     synchronized (urlloaders)
705       {
706         if (newUrl == null)
707           return; // Silently ignore...
708 
709   // Reset the toString() value.
710   thisString = null;
711 
712         // Check global cache to see if there're already url loader
713         // for this url.
714         URLLoader loader = (URLLoader) urlloaders.get(newUrl);
715         if (loader == null)
716           {
717             String file = newUrl.getFile();
718             String protocol = newUrl.getProtocol();
719 
720             // Check that it is not a directory
721             if (! (file.endsWith("/") || file.endsWith(File.separator)))
722               loader = new JarURLLoader(this, newUrl);
723             else if ("file".equals(protocol))
724               loader = new FileURLLoader(this, newUrl);
725             else
726               loader = new RemoteURLLoader(this, newUrl);
727 
728             // Cache it.
729             urlloaders.put(newUrl, loader);
730           }
731 
732   urlinfos.add(loader);
733 
734   Vector extraUrls = loader.getClassPath();
735   if (extraUrls != null)
736     {
737       Iterator it = extraUrls.iterator();
738       while (it.hasNext())
739         {
740     URL url = (URL)it.next();
741     URLLoader extraLoader = (URLLoader) urlloaders.get(url);
742     if (! urlinfos.contains (extraLoader))
743       addURLImpl(url);
744         }
745     }
746 
747       }
748   }
749 
750   /**
751    * Adds an array of new locations to the end of the internal URL store.
752    * @param newUrls the locations to add
753    */
754   private void addURLs(URL[] newUrls)
755   {
756     for (int i = 0; i < newUrls.length; i++)
757       addURL(newUrls[i]);
758   }
759 
760   /**
761    * Defines a Package based on the given name and the supplied manifest
762    * information. The manifest indicates the tile, version and
763    * vendor information of the specification and implementation and wheter the
764    * package is sealed. If the Manifest indicates that the package is sealed
765    * then the Package will be sealed with respect to the supplied URL.
766    *
767    * @param name The name of the package
768    * @param manifest The manifest describing the specification,
769    * implementation and sealing details of the package
770    * @param url the code source url to seal the package
771    * @exception IllegalArgumentException If this package name already exists
772    * in this class loader
773    * @return the defined Package
774    */
775   protected Package definePackage(String name, Manifest manifest, URL url)
776     throws IllegalArgumentException
777   {
778     Attributes attr = manifest.getMainAttributes();
779     String specTitle = attr.getValue(Attributes.Name.SPECIFICATION_TITLE);
780     String specVersion = attr.getValue(Attributes.Name.SPECIFICATION_VERSION);
781     String specVendor = attr.getValue(Attributes.Name.SPECIFICATION_VENDOR);
782     String implTitle = attr.getValue(Attributes.Name.IMPLEMENTATION_TITLE);
783     String implVersion = attr.getValue(Attributes.Name.IMPLEMENTATION_VERSION);
784     String implVendor = attr.getValue(Attributes.Name.IMPLEMENTATION_VENDOR);
785 
786     // Look if the Manifest indicates that this package is sealed
787     // XXX - most likely not completely correct!
788     // Shouldn't we also check the sealed attribute of the complete jar?
789     // http://java.sun.com/products/jdk/1.4/docs/guide/extensions/spec.html#bundled
790     // But how do we get that jar manifest here?
791     String sealed = attr.getValue(Attributes.Name.SEALED);
792     if ("false".equals(sealed))
793       // make sure that the URL is null so the package is not sealed
794       url = null;
795 
796     return definePackage(name, specTitle, specVersion, specVendor, implTitle,
797                          implVersion, implVendor, url);
798   }
799 
800   /**
801    * Finds (the first) class by name from one of the locations. The locations
802    * are searched in the order they were added to the URLClassLoader.
803    *
804    * @param className the classname to find
805    * @exception ClassNotFoundException when the class could not be found or
806    * loaded
807    * @return a Class object representing the found class
808    */
809   protected Class findClass(final String className)
810     throws ClassNotFoundException
811   {
812     // Just try to find the resource by the (almost) same name
813     String resourceName = className.replace('.', '/') + ".class";
814     Resource resource = findURLResource(resourceName);
815     if (resource == null)
816       throw new ClassNotFoundException(className + " not found in " + this);
817 
818     // Try to read the class data, create the CodeSource, Package and
819     // construct the class (and watch out for those nasty IOExceptions)
820     try
821       {
822         byte[] data;
823         InputStream in = resource.getInputStream();
824         try
825           {
826             int length = resource.getLength();
827             if (length != -1)
828               {
829                 // We know the length of the data.
830                 // Just try to read it in all at once
831                 data = new byte[length];
832                 int pos = 0;
833                 while (length - pos > 0)
834                   {
835                     int len = in.read(data, pos, length - pos);
836                     if (len == -1)
837                       throw new EOFException("Not enough data reading from: "
838                                              + in);
839                     pos += len;
840                   }
841               }
842             else
843               {
844                 // We don't know the data length.
845                 // Have to read it in chunks.
846                 ByteArrayOutputStream out = new ByteArrayOutputStream(4096);
847                 byte[] b = new byte[4096];
848                 int l = 0;
849                 while (l != -1)
850                   {
851                     l = in.read(b);
852                     if (l != -1)
853                       out.write(b, 0, l);
854                   }
855                 data = out.toByteArray();
856               }
857           }
858         finally
859           {
860             in.close();
861           }
862         final byte[] classData = data;
863 
864         // Now get the CodeSource
865         final CodeSource source = resource.getCodeSource();
866 
867         // Find out package name
868         String packageName = null;
869         int lastDot = className.lastIndexOf('.');
870         if (lastDot != -1)
871           packageName = className.substring(0, lastDot);
872 
873         if (packageName != null && getPackage(packageName) == null)
874           {
875             // define the package
876             Manifest manifest = resource.loader.getManifest();
877             if (manifest == null)
878               definePackage(packageName, null, null, null, null, null, null,
879                             null);
880             else
881               definePackage(packageName, manifest, resource.loader.baseURL);
882           }
883 
884         // And finally construct the class!
885         SecurityManager sm = System.getSecurityManager();
886         Class result = null;
887         if (sm != null && securityContext != null)
888           {
889             result = (Class)AccessController.doPrivileged
890               (new PrivilegedAction()
891                 {
892                   public Object run()
893                   {
894                     return defineClass(className, classData,
895                                        0, classData.length,
896                                        source);
897                   }
898                 }, securityContext);
899           }
900         else
901           result = defineClass(className, classData, 0, classData.length, source);
902 
903         // Avoid NullPointerExceptions.
904         Certificate[] resourceCertificates = resource.getCertificates();
905         if(resourceCertificates != null)
906           super.setSigners(result, resourceCertificates);
907         
908         return result;
909       }
910     catch (IOException ioe)
911       {
912   ClassNotFoundException cnfe;
913   cnfe = new ClassNotFoundException(className + " not found in " + this);
914   cnfe.initCause(ioe);
915   throw cnfe;
916       }
917   }
918   
919   // Cached String representation of this URLClassLoader
920   private String thisString;
921   
922   /**
923    * Returns a String representation of this URLClassLoader giving the
924    * actual Class name, the URLs that are searched and the parent
925    * ClassLoader.
926    */
927   public String toString()
928   {
929     synchronized (urlloaders)
930       {
931   if (thisString == null)
932     {
933       StringBuffer sb = new StringBuffer();
934       sb.append(this.getClass().getName());
935       sb.append("{urls=[" );
936       URL[] thisURLs = getURLs();
937       for (int i = 0; i < thisURLs.length; i++)
938         {
939     sb.append(thisURLs[i]);
940     if (i < thisURLs.length - 1)
941       sb.append(',');
942         }
943       sb.append(']');
944       sb.append(", parent=");
945       sb.append(getParent());
946       sb.append('}');
947       thisString = sb.toString();
948     }
949   return thisString;
950       }
951   }
952 
953   /**
954    * Finds the first occurrence of a resource that can be found. The locations
955    * are searched in the order they were added to the URLClassLoader.
956    *
957    * @param resourceName the resource name to look for
958    * @return the URLResource for the resource if found, null otherwise
959    */
960   private Resource findURLResource(String resourceName)
961   {
962     int max = urlinfos.size();
963     for (int i = 0; i < max; i++)
964       {
965         URLLoader loader = (URLLoader) urlinfos.elementAt(i);
966         if (loader == null)
967           continue;
968 
969         Resource resource = loader.getResource(resourceName);
970         if (resource != null)
971           return resource;
972       }
973     return null;
974   }
975 
976   /**
977    * Finds the first occurrence of a resource that can be found.
978    *
979    * @param resourceName the resource name to look for
980    * @return the URL if found, null otherwise
981    */
982   public URL findResource(String resourceName)
983   {
984     Resource resource = findURLResource(resourceName);
985     if (resource != null)
986       return resource.getURL();
987 
988     // Resource not found
989     return null;
990   }
991 
992   /**
993    * If the URLStreamHandlerFactory has been set this return the appropriate
994    * URLStreamHandler for the given protocol, if not set returns null.
995    *
996    * @param protocol the protocol for which we need a URLStreamHandler
997    * @return the appropriate URLStreamHandler or null
998    */
999   URLStreamHandler getURLStreamHandler(String protocol)
1000  {
1001    if (factory == null)
1002      return null;
1003
1004    URLStreamHandler handler;
1005    synchronized (factoryCache)
1006      {
1007        // Check if there're handler for the same protocol in cache.
1008        HashMap cache = (HashMap) factoryCache.get(factory);
1009        handler = (URLStreamHandler) cache.get(protocol);
1010        if (handler == null)
1011          {
1012            // Add it to cache.
1013            handler = factory.createURLStreamHandler(protocol);
1014            cache.put(protocol, handler);
1015          }
1016      }
1017    return handler;
1018  }
1019
1020  /**
1021   * Finds all the resources with a particular name from all the locations.
1022   *
1023   * @param resourceName the name of the resource to lookup
1024   * @return a (possible empty) enumeration of URLs where the resource can be
1025   * found
1026   * @exception IOException when an error occurs accessing one of the
1027   * locations
1028   */
1029  public Enumeration findResources(String resourceName)
1030    throws IOException
1031  {
1032    Vector resources = new Vector();
1033    int max = urlinfos.size();
1034    for (int i = 0; i < max; i++)
1035      {
1036        URLLoader loader = (URLLoader) urlinfos.elementAt(i);
1037        Resource resource = loader.getResource(resourceName);
1038        if (resource != null)
1039          resources.add(resource.getURL());
1040      }
1041    return resources.elements();
1042  }
1043
1044  /**
1045   * Returns the permissions needed to access a particular code
1046   * source.  These permissions includes those returned by
1047   * <code>SecureClassLoader.getPermissions()</code> and the actual
1048   * permissions to access the objects referenced by the URL of the
1049   * code source.  The extra permissions added depend on the protocol
1050   * and file portion of the URL in the code source. If the URL has
1051   * the "file" protocol ends with a '/' character then it must be a
1052   * directory and a file Permission to read everything in that
1053   * directory and all subdirectories is added. If the URL had the
1054   * "file" protocol and doesn't end with a '/' character then it must
1055   * be a normal file and a file permission to read that file is
1056   * added. If the <code>URL</code> has any other protocol then a
1057   * socket permission to connect and accept connections from the host
1058   * portion of the URL is added.
1059   *
1060   * @param source The codesource that needs the permissions to be accessed
1061   * @return the collection of permissions needed to access the code resource
1062   * @see java.security.SecureClassLoader#getPermissions(CodeSource)
1063   */
1064  protected PermissionCollection getPermissions(CodeSource source)
1065  {
1066    // XXX - This implementation does exactly as the Javadoc describes.
1067    // But maybe we should/could use URLConnection.getPermissions()?
1068    // First get the permissions that would normally be granted
1069    PermissionCollection permissions = super.getPermissions(source);
1070
1071    // Now add any extra permissions depending on the URL location.
1072    URL url = source.getLocation();
1073    String protocol = url.getProtocol();
1074    if (protocol.equals("file"))
1075      {
1076        String file = url.getFile();
1077
1078        // If the file end in / it must be an directory.
1079        if (file.endsWith("/") || file.endsWith(File.separator))
1080          {
1081            // Grant permission to read everything in that directory and
1082            // all subdirectories.
1083            permissions.add(new FilePermission(file + "-", "read"));
1084          }
1085        else
1086          {
1087            // It is a 'normal' file.
1088            // Grant permission to access that file.
1089            permissions.add(new FilePermission(file, "read"));
1090          }
1091      }
1092    else
1093      {
1094        // Grant permission to connect to and accept connections from host
1095        String host = url.getHost();
1096        if (host != null)
1097          permissions.add(new SocketPermission(host, "connect,accept"));
1098      }
1099
1100    return permissions;
1101  }
1102
1103  /**
1104   * Returns all the locations that this class loader currently uses the
1105   * resolve classes and resource. This includes both the initially supplied
1106   * URLs as any URLs added later by the loader.
1107   * @return All the currently used URLs
1108   */
1109  public URL[] getURLs()
1110  {
1111    return (URL[]) urls.toArray(new URL[urls.size()]);
1112  }
1113
1114  /**
1115   * Creates a new instance of a <code>URLClassLoader</code> that gets
1116   * classes from the supplied <code>URL</code>s. This class loader
1117   * will have as parent the standard system class loader.
1118   *
1119   * @param urls the initial URLs used to resolve classes and
1120   * resources
1121   *
1122   * @return the class loader
1123   *
1124   * @exception SecurityException when the calling code does not have
1125   * permission to access the given <code>URL</code>s
1126   */
1127  public static URLClassLoader newInstance(URL[] urls)
1128    throws SecurityException
1129  {
1130    return newInstance(urls, null);
1131  }
1132
1133  /**
1134   * Creates a new instance of a <code>URLClassLoader</code> that gets
1135   * classes from the supplied <code>URL</code>s and with the supplied
1136   * loader as parent class loader.
1137   *
1138   * @param urls the initial URLs used to resolve classes and
1139   * resources
1140   * @param parent the parent class loader
1141   *
1142   * @return the class loader
1143   *
1144   * @exception SecurityException when the calling code does not have
1145   * permission to access the given <code>URL</code>s
1146   */
1147  public static URLClassLoader newInstance(URL[] urls, final ClassLoader parent)
1148    throws SecurityException
1149  {
1150    SecurityManager sm = System.getSecurityManager();
1151    if (sm == null)
1152      return new URLClassLoader(urls, parent);
1153    else
1154      {
1155        final Object securityContext = sm.getSecurityContext();
1156
1157        // XXX - What to do with anything else then an AccessControlContext?
1158        if (! (securityContext instanceof AccessControlContext))
1159          throw new SecurityException("securityContext must be AccessControlContext: "
1160                                      + securityContext);
1161
1162        URLClassLoader loader =
1163          (URLClassLoader) AccessController.doPrivileged(new PrivilegedAction()
1164              {
1165                public Object run()
1166                {
1167                  return new URLClassLoader(parent,
1168                                            (AccessControlContext) securityContext);
1169                }
1170              });
1171        loader.addURLs(urls);
1172        return loader;
1173      }
1174  }
1175}