fd70fdf2045a8dcbcb157d344cbb6e787933480d
[platform/upstream/gcc.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, 2006
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                         URL absoluteUrl)
310     {
311       super(classloader, baseURL, absoluteUrl);
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     private final String name;
407
408     JarURLResource(JarURLLoader loader, String name, JarEntry entry)
409     {
410       super(loader);
411       this.entry = entry;
412       this.name = name;
413     }
414
415     InputStream getInputStream() throws IOException
416     {
417       return ((JarURLLoader) loader).jarfile.getInputStream(entry);
418     }
419
420     int getLength()
421     {
422       return (int) entry.getSize();
423     }
424
425     Certificate[] getCertificates()
426     {
427       // We have to get the entry from the jar file again, because the
428       // certificates will not be available until the entire entry has
429       // been read.
430       return ((JarEntry) ((JarURLLoader) loader).jarfile.getEntry(name))
431         .getCertificates();
432     }
433
434     URL getURL()
435     {
436       try
437         {
438           return new URL(((JarURLLoader) loader).baseJarURL, name,
439                          loader.classloader.getURLStreamHandler("jar"));
440         }
441       catch (MalformedURLException e)
442         {
443           InternalError ie = new InternalError();
444           ie.initCause(e);
445           throw ie;
446         }
447     }
448   }
449
450   /**
451    * Loader for remote directories.
452    */
453   static final class RemoteURLLoader extends URLLoader
454   {
455     private final String protocol;
456
457     RemoteURLLoader(URLClassLoader classloader, URL url)
458     {
459       super(classloader, url);
460       protocol = url.getProtocol();
461     }
462
463     /**
464      * Get a remote resource.
465      * Returns null if no such resource exists.
466      */
467     Resource getResource(String name)
468     {
469       try
470         {
471           URL url =
472             new URL(baseURL, name, classloader.getURLStreamHandler(protocol));
473           URLConnection connection = url.openConnection();
474
475           // Open the connection and check the stream
476           // just to be sure it exists.
477           int length = connection.getContentLength();
478           InputStream stream = connection.getInputStream();
479
480           // We can do some extra checking if it is a http request
481           if (connection instanceof HttpURLConnection)
482             {
483               int response =
484                 ((HttpURLConnection) connection).getResponseCode();
485               if (response / 100 != 2)
486                 return null;
487             }
488
489           if (stream != null)
490             return new RemoteResource(this, name, url, stream, length);
491           else
492             return null;
493         }
494       catch (IOException ioe)
495         {
496           return null;
497         }
498     }
499   }
500
501   /**
502    * A resource from some remote location.
503    */
504   static final class RemoteResource extends Resource
505   {
506     private final URL url;
507     private final InputStream stream;
508     private final int length;
509
510     RemoteResource(RemoteURLLoader loader, String name, URL url,
511                    InputStream stream, int length)
512     {
513       super(loader);
514       this.url = url;
515       this.stream = stream;
516       this.length = length;
517     }
518
519     InputStream getInputStream() throws IOException
520     {
521       return stream;
522     }
523
524     public int getLength()
525     {
526       return length;
527     }
528
529     public URL getURL()
530     {
531       return url;
532     }
533   }
534
535   /**
536    * A <code>SoURLLoader</code> is a type of <code>URLLoader</code>
537    * that loads classes and resources from a shared library.
538    */
539   final static class SoURLLoader extends URLLoader
540   {
541     SharedLibHelper helper;
542
543     SoURLLoader(URLClassLoader classloader, URL url)
544     {
545       this(classloader, url, url);
546     }
547
548     SoURLLoader(URLClassLoader classloader, URL url, URL overrideURL)
549     {
550       super(classloader, url, overrideURL);
551       helper = SharedLibHelper.findHelper(classloader, url.getFile(),
552                                           noCertCodeSource, true);
553     }
554
555     Class getClass(String className)
556     {
557       return helper.findClass(className);
558     }
559
560     Resource getResource(String name)
561     {
562       URL url = helper.findResource(name);
563       if (url == null)
564         return null;
565       return new SoResource(this, url);
566     }
567   }
568
569   final static class SoResource extends Resource
570   {
571     SoResource(SoURLLoader loader, URL url)
572     {
573       super(loader);
574       this.url = url;
575     }
576
577     InputStream getInputStream() throws IOException
578     {
579       URLConnection conn = url.openConnection();
580       return conn.getInputStream();
581     }
582
583     public int getLength()
584     {
585       // FIXME we could find this by asking the core object.
586       return -1;
587     }
588
589     public URL getURL ()
590     {
591       return url;
592     }
593
594     final URL url;
595   }
596
597   /**
598    * A <code>FileURLLoader</code> is a type of <code>URLLoader</code>
599    * only loading from file url.
600    */
601   static final class FileURLLoader extends URLLoader
602   {
603     File dir; //the file for this file url
604
605     FileURLLoader(URLClassLoader classloader, URL url, URL absoluteUrl)
606     {
607       super(classloader, url, absoluteUrl);
608       dir = new File(absoluteUrl.getFile());
609     }
610
611     /** get resource with the name "name" in the file url */
612     Resource getResource(String name)
613     {
614       try 
615         {
616           File file = new File(dir, name).getCanonicalFile();
617           if (file.exists() && !file.isDirectory())
618             return new FileResource(this, file);
619         }
620       catch (IOException e)
621         {
622           // Fall through...
623         }
624       return null;
625     }
626   }
627
628   static final class FileResource extends Resource
629   {
630     final File file;
631
632     FileResource(FileURLLoader loader, File file)
633     {
634       super(loader);
635       this.file = file;
636     }
637
638     InputStream getInputStream() throws IOException
639     {
640       // Delegate to the URL content handler mechanism to retrieve an
641       // HTML representation of the directory listing if a directory
642       if (file.isDirectory())
643         {
644           URL url = getURL();
645           return url.openStream();
646         }
647       // Otherwise simply return a FileInputStream
648       return new FileInputStream(file);
649     }
650
651     public int getLength()
652     {
653       // Delegate to the URL content handler mechanism to retrieve the
654       // length of the HTML representation of the directory listing if
655       // a directory, or -1 if an exception occurs opening the directory.
656       if (file.isDirectory())
657         {
658           URL url = getURL();
659           try
660             {
661               URLConnection connection = url.openConnection();
662               return connection.getContentLength();
663             }
664           catch (IOException e)
665             {
666               return -1;
667             }
668         }
669       // Otherwise simply return the file length
670       return (int) file.length();
671     }
672
673     public URL getURL()
674     {
675       try
676         {
677           return file.toURL();
678         }
679       catch (MalformedURLException e)
680         {
681           InternalError ie = new InternalError();
682           ie.initCause(e);
683           throw ie;
684         }
685     }
686   }
687
688   /**
689    * A <code>CoreURLLoader</code> is a type of <code>URLLoader</code>
690    * only loading from core url.
691    */
692   static final class CoreURLLoader extends URLLoader
693   {
694     private String dir;
695
696     CoreURLLoader(URLClassLoader classloader, URL url)
697     {
698       super(classloader, url);
699       dir = baseURL.getFile();
700     }
701
702     /** get resource with the name "name" in the core url */
703     Resource getResource(String name)
704     {
705       Core core = Core.find (dir + name);
706       if (core != null)
707         return new CoreResource(this, name, core);
708       return null;
709     }
710   }
711
712   static final class CoreResource extends Resource
713   {
714     private final Core core;
715     private final String name;
716
717     CoreResource(CoreURLLoader loader, String name, Core core)
718     {
719       super(loader);
720       this.core = core;
721       this.name = name;
722     }
723
724     InputStream getInputStream() throws IOException
725     {
726       return new CoreInputStream(core);
727     }
728
729     public int getLength()
730     {
731       return core.length;
732     }
733
734     public URL getURL()
735     {
736       try
737         {
738           return new URL(loader.baseURL, name,
739                          loader.classloader.getURLStreamHandler("core"));
740         }
741       catch (MalformedURLException e)
742         {
743           InternalError ie = new InternalError();
744           ie.initCause(e);
745           throw ie;
746         }
747     }
748   }
749
750   // Constructors
751
752   /**
753    * Creates a URLClassLoader that gets classes from the supplied URLs.
754    * To determine if this classloader may be created the constructor of
755    * the super class (<code>SecureClassLoader</code>) is called first, which
756    * can throw a SecurityException. Then the supplied URLs are added
757    * in the order given to the URLClassLoader which uses these URLs to
758    * load classes and resources (after using the default parent ClassLoader).
759    *
760    * @param urls Locations that should be searched by this ClassLoader when
761    * resolving Classes or Resources.
762    * @exception SecurityException if the SecurityManager disallows the
763    * creation of a ClassLoader.
764    * @see SecureClassLoader
765    */
766   public URLClassLoader(URL[] urls) throws SecurityException
767   {
768     super();
769     this.factory = null;
770     this.securityContext = null;
771     addURLs(urls);
772   }
773
774   /**
775    * Creates a <code>URLClassLoader</code> that gets classes from the supplied
776    * <code>URL</code>s.
777    * To determine if this classloader may be created the constructor of
778    * the super class (<code>SecureClassLoader</code>) is called first, which
779    * can throw a SecurityException. Then the supplied URLs are added
780    * in the order given to the URLClassLoader which uses these URLs to
781    * load classes and resources (after using the supplied parent ClassLoader).
782    * @param urls Locations that should be searched by this ClassLoader when
783    * resolving Classes or Resources.
784    * @param parent The parent class loader used before trying this class
785    * loader.
786    * @exception SecurityException if the SecurityManager disallows the
787    * creation of a ClassLoader.
788    * @exception SecurityException
789    * @see SecureClassLoader
790    */
791   public URLClassLoader(URL[] urls, ClassLoader parent)
792     throws SecurityException
793   {
794     super(parent);
795     this.factory = null;
796     this.securityContext = null;
797     addURLs(urls);
798   }
799
800   // Package-private to avoid a trampoline constructor.
801   /**
802    * Package-private constructor used by the static
803    * <code>newInstance(URL[])</code> method.  Creates an
804    * <code>URLClassLoader</code> with the given parent but without any
805    * <code>URL</code>s yet. This is used to bypass the normal security
806    * check for creating classloaders, but remembers the security
807    * context which will be used when defining classes.  The
808    * <code>URL</code>s to load from must be added by the
809    * <code>newInstance()</code> method in the security context of the
810    * caller.
811    *
812    * @param securityContext the security context of the unprivileged code.
813    */
814   URLClassLoader(ClassLoader parent, AccessControlContext securityContext)
815   {
816     super(parent);
817     this.factory = null;
818     this.securityContext = securityContext;
819   }
820
821   /**
822    * Creates a URLClassLoader that gets classes from the supplied URLs.
823    * To determine if this classloader may be created the constructor of
824    * the super class (<CODE>SecureClassLoader</CODE>) is called first, which
825    * can throw a SecurityException. Then the supplied URLs are added
826    * in the order given to the URLClassLoader which uses these URLs to
827    * load classes and resources (after using the supplied parent ClassLoader).
828    * It will use the supplied <CODE>URLStreamHandlerFactory</CODE> to get the
829    * protocol handlers of the supplied URLs.
830    * @param urls Locations that should be searched by this ClassLoader when
831    * resolving Classes or Resources.
832    * @param parent The parent class loader used before trying this class
833    * loader.
834    * @param factory Used to get the protocol handler for the URLs.
835    * @exception SecurityException if the SecurityManager disallows the
836    * creation of a ClassLoader.
837    * @exception SecurityException
838    * @see SecureClassLoader
839    */
840   public URLClassLoader(URL[] urls, ClassLoader parent,
841                         URLStreamHandlerFactory factory)
842     throws SecurityException
843   {
844     super(parent);
845     this.securityContext = null;
846     this.factory = factory;
847     addURLs(urls);
848
849     // If this factory is still not in factoryCache, add it,
850     //   since we only support three protocols so far, 5 is enough
851     //   for cache initial size
852     synchronized (factoryCache)
853       {
854         if (factory != null && factoryCache.get(factory) == null)
855           factoryCache.put(factory, new HashMap(5));
856       }
857   }
858
859   // Methods
860
861   /**
862    * Adds a new location to the end of the internal URL store.
863    * @param newUrl the location to add
864    */
865   protected void addURL(URL newUrl)
866   {
867     urls.add(newUrl);
868     addURLImpl(newUrl);
869   }
870
871   private void addURLImpl(URL newUrl)
872   {
873     synchronized (this)
874       {
875         if (newUrl == null)
876           return; // Silently ignore...
877
878         // Reset the toString() value.
879         thisString = null;
880
881         // Check global cache to see if there're already url loader
882         // for this url.
883         URLLoader loader = (URLLoader) urlloaders.get(newUrl);
884         if (loader == null)
885           {
886             String file = newUrl.getFile();
887             String protocol = newUrl.getProtocol();
888
889             // If we have a file: URL, we want to make it absolute
890             // here, before we decide whether it is really a jar.
891             URL absoluteURL;
892             if ("file".equals (protocol))
893               {
894                 File dir = new File(file);
895                 URL absUrl;
896                 try
897                   {
898                     absoluteURL = dir.getCanonicalFile().toURL();
899                   }
900                 catch (IOException ignore)
901                   {
902                     try
903                       {
904                         absoluteURL = dir.getAbsoluteFile().toURL();
905                       }
906                     catch (MalformedURLException _)
907                       {
908                         // This really should not happen.
909                         absoluteURL = newUrl;
910                       }
911                   }
912               }
913             else
914               {
915                 // This doesn't hurt, and it simplifies the logic a
916                 // little.
917                 absoluteURL = newUrl;
918               }
919
920             // Check that it is not a directory
921             if ("gcjlib".equals(protocol))
922               loader = new SoURLLoader(this, newUrl);
923             else if (! (file.endsWith("/") || file.endsWith(File.separator)))
924               loader = new JarURLLoader(this, newUrl, absoluteURL);
925             else if ("file".equals(protocol))
926               loader = new FileURLLoader(this, newUrl, absoluteURL);
927             else if ("core".equals(protocol))
928               loader = new CoreURLLoader(this, newUrl);
929             else
930               loader = new RemoteURLLoader(this, newUrl);
931
932             // Cache it.
933             urlloaders.put(newUrl, loader);
934           }
935
936         urlinfos.add(loader);
937
938         Vector extraUrls = loader.getClassPath();
939         if (extraUrls != null)
940           {
941             Iterator it = extraUrls.iterator();
942             while (it.hasNext())
943               {
944                 URL url = (URL)it.next();
945                 URLLoader extraLoader = (URLLoader) urlloaders.get(url);
946                 if (! urlinfos.contains (extraLoader))
947                   addURLImpl(url);
948               }
949           }
950
951       }
952   }
953
954   /**
955    * Adds an array of new locations to the end of the internal URL
956    * store.  Called from the the constructors. Should not call to the
957    * protected addURL() method since that can be overridden and
958    * subclasses are not yet in a good state at this point.
959    * jboss 4.0.3 for example depends on this.
960    *
961    * @param newUrls the locations to add
962    */
963   private void addURLs(URL[] newUrls)
964   {
965     for (int i = 0; i < newUrls.length; i++)
966       {
967         urls.add(newUrls[i]);
968         addURLImpl(newUrls[i]);
969       }
970   }
971
972   /**
973    * Look in both Attributes for a given value.  The first Attributes
974    * object, if not null, has precedence.
975    */
976   private String getAttributeValue(Attributes.Name name, Attributes first,
977                                    Attributes second)
978   {
979     String result = null;
980     if (first != null)
981       result = first.getValue(name);
982     if (result == null)
983       result = second.getValue(name);
984     return result;
985   }
986
987   /**
988    * Defines a Package based on the given name and the supplied manifest
989    * information. The manifest indicates the title, version and
990    * vendor information of the specification and implementation and whether the
991    * package is sealed. If the Manifest indicates that the package is sealed
992    * then the Package will be sealed with respect to the supplied URL.
993    *
994    * @param name The name of the package
995    * @param manifest The manifest describing the specification,
996    * implementation and sealing details of the package
997    * @param url the code source url to seal the package
998    * @return the defined Package
999    * @throws IllegalArgumentException If this package name already exists
1000    * in this class loader
1001    */
1002   protected Package definePackage(String name, Manifest manifest, URL url)
1003     throws IllegalArgumentException
1004   {
1005     // Compute the name of the package as it may appear in the
1006     // Manifest.
1007     StringBuffer xform = new StringBuffer(name);
1008     for (int i = xform.length () - 1; i >= 0; --i)
1009       if (xform.charAt(i) == '.')
1010         xform.setCharAt(i, '/');
1011     xform.append('/');
1012     String xformName = xform.toString();
1013
1014     Attributes entryAttr = manifest.getAttributes(xformName);
1015     Attributes attr = manifest.getMainAttributes();
1016
1017     String specTitle
1018       = getAttributeValue(Attributes.Name.SPECIFICATION_TITLE,
1019                           entryAttr, attr);
1020     String specVersion
1021       = getAttributeValue(Attributes.Name.SPECIFICATION_VERSION,
1022                           entryAttr, attr);
1023     String specVendor
1024       = getAttributeValue(Attributes.Name.SPECIFICATION_VENDOR,
1025                           entryAttr, attr);
1026     String implTitle
1027       = getAttributeValue(Attributes.Name.IMPLEMENTATION_TITLE,
1028                           entryAttr, attr);
1029     String implVersion
1030       = getAttributeValue(Attributes.Name.IMPLEMENTATION_VERSION,
1031                           entryAttr, attr);
1032     String implVendor
1033       = getAttributeValue(Attributes.Name.IMPLEMENTATION_VENDOR,
1034                           entryAttr, attr);
1035
1036     // Look if the Manifest indicates that this package is sealed
1037     // XXX - most likely not completely correct!
1038     // Shouldn't we also check the sealed attribute of the complete jar?
1039     // http://java.sun.com/products/jdk/1.4/docs/guide/extensions/spec.html#bundled
1040     // But how do we get that jar manifest here?
1041     String sealed = attr.getValue(Attributes.Name.SEALED);
1042     if ("false".equals(sealed))
1043       // make sure that the URL is null so the package is not sealed
1044       url = null;
1045
1046     return definePackage(name,
1047                          specTitle, specVendor, specVersion,
1048                          implTitle, implVendor, implVersion,
1049                          url);
1050   }
1051
1052   /**
1053    * Finds (the first) class by name from one of the locations. The locations
1054    * are searched in the order they were added to the URLClassLoader.
1055    *
1056    * @param className the classname to find
1057    * @exception ClassNotFoundException when the class could not be found or
1058    * loaded
1059    * @return a Class object representing the found class
1060    */
1061   protected Class findClass(final String className)
1062     throws ClassNotFoundException
1063   {
1064     // Just try to find the resource by the (almost) same name
1065     String resourceName = className.replace('.', '/') + ".class";
1066     int max = urlinfos.size();
1067     Resource resource = null;
1068     for (int i = 0; i < max && resource == null; i++)
1069       {
1070         URLLoader loader = (URLLoader)urlinfos.elementAt(i);
1071         if (loader == null)
1072           continue;
1073
1074         Class k = loader.getClass(className);
1075         if (k != null)
1076           return k;
1077
1078         resource = loader.getResource(resourceName);
1079       }
1080     if (resource == null)
1081       {
1082         String message = className + " not found";
1083         // Calling this.toString() during VM startup when a
1084         // security manager is in force causes the stack to
1085         // be unwound before it can properly be decoded.
1086         if (Thread.currentThread() != null)
1087           message += " in " + this;
1088         throw new ClassNotFoundException(message);
1089       }
1090
1091     // Try to read the class data, create the CodeSource, Package and
1092     // construct the class (and watch out for those nasty IOExceptions)
1093     try
1094       {
1095         byte[] data;
1096         InputStream in = resource.getInputStream();
1097         try
1098           {
1099             int length = resource.getLength();
1100             if (length != -1)
1101               {
1102                 // We know the length of the data.
1103                 // Just try to read it in all at once
1104                 data = new byte[length];
1105                 int pos = 0;
1106                 while (length - pos > 0)
1107                   {
1108                     int len = in.read(data, pos, length - pos);
1109                     if (len == -1)
1110                       throw new EOFException("Not enough data reading from: "
1111                                              + in);
1112                     pos += len;
1113                   }
1114               }
1115             else
1116               {
1117                 // We don't know the data length.
1118                 // Have to read it in chunks.
1119                 ByteArrayOutputStream out = new ByteArrayOutputStream(4096);
1120                 byte[] b = new byte[4096];
1121                 int l = 0;
1122                 while (l != -1)
1123                   {
1124                     l = in.read(b);
1125                     if (l != -1)
1126                       out.write(b, 0, l);
1127                   }
1128                 data = out.toByteArray();
1129               }
1130           }
1131         finally
1132           {
1133             in.close();
1134           }
1135         final byte[] classData = data;
1136
1137         // Now get the CodeSource
1138         final CodeSource source = resource.getCodeSource();
1139
1140         // Find out package name
1141         String packageName = null;
1142         int lastDot = className.lastIndexOf('.');
1143         if (lastDot != -1)
1144           packageName = className.substring(0, lastDot);
1145
1146         if (packageName != null && getPackage(packageName) == null)
1147           {
1148             // define the package
1149             Manifest manifest = resource.loader.getManifest();
1150             if (manifest == null)
1151               definePackage(packageName, null, null, null, null, null, null,
1152                             null);
1153             else
1154               definePackage(packageName, manifest, resource.loader.baseURL);
1155           }
1156
1157         // And finally construct the class!
1158         SecurityManager sm = System.getSecurityManager();
1159         Class result = null;
1160         if (sm != null && securityContext != null)
1161           {
1162             result = (Class)AccessController.doPrivileged
1163               (new PrivilegedAction()
1164                 {
1165                   public Object run()
1166                   {
1167                     return defineClass(className, classData,
1168                                        0, classData.length,
1169                                        source);
1170                   }
1171                 }, securityContext);
1172           }
1173         else
1174           result = defineClass(className, classData, 0, classData.length, source);
1175
1176         // Avoid NullPointerExceptions.
1177         Certificate[] resourceCertificates = resource.getCertificates();
1178         if(resourceCertificates != null)
1179           super.setSigners(result, resourceCertificates);
1180         
1181         return result;
1182       }
1183     catch (IOException ioe)
1184       {
1185         ClassNotFoundException cnfe;
1186         cnfe = new ClassNotFoundException(className + " not found in " + this);
1187         cnfe.initCause(ioe);
1188         throw cnfe;
1189       }
1190   }
1191   
1192   // Cached String representation of this URLClassLoader
1193   private String thisString;
1194   
1195   /**
1196    * Returns a String representation of this URLClassLoader giving the
1197    * actual Class name, the URLs that are searched and the parent
1198    * ClassLoader.
1199    */
1200   public String toString()
1201   {
1202     synchronized (this)
1203       {
1204         if (thisString == null)
1205           {
1206             StringBuffer sb = new StringBuffer();
1207             sb.append(this.getClass().getName());
1208             sb.append("{urls=[" );
1209             URL[] thisURLs = getURLs();
1210             for (int i = 0; i < thisURLs.length; i++)
1211               {
1212                 sb.append(thisURLs[i]);
1213                 if (i < thisURLs.length - 1)
1214                   sb.append(',');
1215               }
1216             sb.append(']');
1217             sb.append(", parent=");
1218             sb.append(getParent());
1219             sb.append('}');
1220             thisString = sb.toString();
1221           }
1222         return thisString;
1223       }
1224   }
1225
1226   /**
1227    * Finds the first occurrence of a resource that can be found. The locations
1228    * are searched in the order they were added to the URLClassLoader.
1229    *
1230    * @param resourceName the resource name to look for
1231    * @return the URLResource for the resource if found, null otherwise
1232    */
1233   private Resource findURLResource(String resourceName)
1234   {
1235     int max = urlinfos.size();
1236     for (int i = 0; i < max; i++)
1237       {
1238         URLLoader loader = (URLLoader) urlinfos.elementAt(i);
1239         if (loader == null)
1240           continue;
1241
1242         Resource resource = loader.getResource(resourceName);
1243         if (resource != null)
1244           return resource;
1245       }
1246     return null;
1247   }
1248
1249   /**
1250    * Finds the first occurrence of a resource that can be found.
1251    *
1252    * @param resourceName the resource name to look for
1253    * @return the URL if found, null otherwise
1254    */
1255   public URL findResource(String resourceName)
1256   {
1257     Resource resource = findURLResource(resourceName);
1258     if (resource != null)
1259       return resource.getURL();
1260
1261     // Resource not found
1262     return null;
1263   }
1264
1265   /**
1266    * If the URLStreamHandlerFactory has been set this return the appropriate
1267    * URLStreamHandler for the given protocol, if not set returns null.
1268    *
1269    * @param protocol the protocol for which we need a URLStreamHandler
1270    * @return the appropriate URLStreamHandler or null
1271    */
1272   URLStreamHandler getURLStreamHandler(String protocol)
1273   {
1274     if (factory == null)
1275       return null;
1276
1277     URLStreamHandler handler;
1278     synchronized (factoryCache)
1279       {
1280         // Check if there're handler for the same protocol in cache.
1281         HashMap cache = (HashMap) factoryCache.get(factory);
1282         handler = (URLStreamHandler) cache.get(protocol);
1283         if (handler == null)
1284           {
1285             // Add it to cache.
1286             handler = factory.createURLStreamHandler(protocol);
1287             cache.put(protocol, handler);
1288           }
1289       }
1290     return handler;
1291   }
1292
1293   /**
1294    * Finds all the resources with a particular name from all the locations.
1295    *
1296    * @param resourceName the name of the resource to lookup
1297    * @return a (possible empty) enumeration of URLs where the resource can be
1298    * found
1299    * @exception IOException when an error occurs accessing one of the
1300    * locations
1301    */
1302   public Enumeration findResources(String resourceName)
1303     throws IOException
1304   {
1305     Vector resources = new Vector();
1306     int max = urlinfos.size();
1307     for (int i = 0; i < max; i++)
1308       {
1309         URLLoader loader = (URLLoader) urlinfos.elementAt(i);
1310         Resource resource = loader.getResource(resourceName);
1311         if (resource != null)
1312           resources.add(resource.getURL());
1313       }
1314     return resources.elements();
1315   }
1316
1317   /**
1318    * Returns the permissions needed to access a particular code
1319    * source.  These permissions includes those returned by
1320    * <code>SecureClassLoader.getPermissions()</code> and the actual
1321    * permissions to access the objects referenced by the URL of the
1322    * code source.  The extra permissions added depend on the protocol
1323    * and file portion of the URL in the code source. If the URL has
1324    * the "file" protocol ends with a '/' character then it must be a
1325    * directory and a file Permission to read everything in that
1326    * directory and all subdirectories is added. If the URL had the
1327    * "file" protocol and doesn't end with a '/' character then it must
1328    * be a normal file and a file permission to read that file is
1329    * added. If the <code>URL</code> has any other protocol then a
1330    * socket permission to connect and accept connections from the host
1331    * portion of the URL is added.
1332    *
1333    * @param source The codesource that needs the permissions to be accessed
1334    * @return the collection of permissions needed to access the code resource
1335    * @see java.security.SecureClassLoader#getPermissions(CodeSource)
1336    */
1337   protected PermissionCollection getPermissions(CodeSource source)
1338   {
1339     // XXX - This implementation does exactly as the Javadoc describes.
1340     // But maybe we should/could use URLConnection.getPermissions()?
1341     // First get the permissions that would normally be granted
1342     PermissionCollection permissions = super.getPermissions(source);
1343
1344     // Now add any extra permissions depending on the URL location.
1345     URL url = source.getLocation();
1346     String protocol = url.getProtocol();
1347     if (protocol.equals("file"))
1348       {
1349         String file = url.getFile();
1350
1351         // If the file end in / it must be an directory.
1352         if (file.endsWith("/") || file.endsWith(File.separator))
1353           {
1354             // Grant permission to read everything in that directory and
1355             // all subdirectories.
1356             permissions.add(new FilePermission(file + "-", "read"));
1357           }
1358         else
1359           {
1360             // It is a 'normal' file.
1361             // Grant permission to access that file.
1362             permissions.add(new FilePermission(file, "read"));
1363           }
1364       }
1365     else
1366       {
1367         // Grant permission to connect to and accept connections from host
1368         String host = url.getHost();
1369         if (host != null)
1370           permissions.add(new SocketPermission(host, "connect,accept"));
1371       }
1372
1373     return permissions;
1374   }
1375
1376   /**
1377    * Returns all the locations that this class loader currently uses the
1378    * resolve classes and resource. This includes both the initially supplied
1379    * URLs as any URLs added later by the loader.
1380    * @return All the currently used URLs
1381    */
1382   public URL[] getURLs()
1383   {
1384     return (URL[]) urls.toArray(new URL[urls.size()]);
1385   }
1386
1387   /**
1388    * Creates a new instance of a <code>URLClassLoader</code> that gets
1389    * classes from the supplied <code>URL</code>s. This class loader
1390    * will have as parent the standard system class loader.
1391    *
1392    * @param urls the initial URLs used to resolve classes and
1393    * resources
1394    *
1395    * @return the class loader
1396    *
1397    * @exception SecurityException when the calling code does not have
1398    * permission to access the given <code>URL</code>s
1399    */
1400   public static URLClassLoader newInstance(URL[] urls)
1401     throws SecurityException
1402   {
1403     return newInstance(urls, null);
1404   }
1405
1406   /**
1407    * Creates a new instance of a <code>URLClassLoader</code> that gets
1408    * classes from the supplied <code>URL</code>s and with the supplied
1409    * loader as parent class loader.
1410    *
1411    * @param urls the initial URLs used to resolve classes and
1412    * resources
1413    * @param parent the parent class loader
1414    *
1415    * @return the class loader
1416    *
1417    * @exception SecurityException when the calling code does not have
1418    * permission to access the given <code>URL</code>s
1419    */
1420   public static URLClassLoader newInstance(URL[] urls, final ClassLoader parent)
1421     throws SecurityException
1422   {
1423     SecurityManager sm = System.getSecurityManager();
1424     if (sm == null)
1425       return new URLClassLoader(urls, parent);
1426     else
1427       {
1428         final Object securityContext = sm.getSecurityContext();
1429
1430         // XXX - What to do with anything else then an AccessControlContext?
1431         if (! (securityContext instanceof AccessControlContext))
1432           throw new SecurityException("securityContext must be AccessControlContext: "
1433                                       + securityContext);
1434
1435         URLClassLoader loader =
1436           (URLClassLoader) AccessController.doPrivileged(new PrivilegedAction()
1437               {
1438                 public Object run()
1439                 {
1440                   return new URLClassLoader(parent,
1441                                             (AccessControlContext) securityContext);
1442                 }
1443               });
1444         loader.addURLs(urls);
1445         return loader;
1446       }
1447   }
1448 }