OSDN Git Service

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