OSDN Git Service

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