OSDN Git Service

libjava/
[pf3gnuchains/gcc-fork.git] / libjava / classpath / 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 gnu.java.net.loader.FileURLLoader;
43 import gnu.java.net.loader.JarURLLoader;
44 import gnu.java.net.loader.RemoteURLLoader;
45 import gnu.java.net.loader.Resource;
46 import gnu.java.net.loader.URLLoader;
47 import gnu.java.net.loader.URLStreamHandlerCache;
48
49 import java.io.ByteArrayOutputStream;
50 import java.io.EOFException;
51 import java.io.File;
52 import java.io.FilePermission;
53 import java.io.IOException;
54 import java.io.InputStream;
55 import java.lang.reflect.Constructor;
56 import java.lang.reflect.InvocationTargetException;
57 import java.security.AccessControlContext;
58 import java.security.AccessController;
59 import java.security.CodeSource;
60 import java.security.PermissionCollection;
61 import java.security.PrivilegedAction;
62 import java.security.SecureClassLoader;
63 import java.security.cert.Certificate;
64 import java.util.ArrayList;
65 import java.util.Enumeration;
66 import java.util.Vector;
67 import java.util.jar.Attributes;
68 import java.util.jar.Manifest;
69
70
71 /**
72  * A secure class loader that can load classes and resources from
73  * multiple locations.  Given an array of <code>URL</code>s this class
74  * loader will retrieve classes and resources by fetching them from
75  * possible remote locations.  Each <code>URL</code> is searched in
76  * order in which it was added.  If the file portion of the
77  * <code>URL</code> ends with a '/' character then it is interpreted
78  * as a base directory, otherwise it is interpreted as a jar file from
79  * which the classes/resources are resolved.
80  *
81  * <p>New instances can be created by two static
82  * <code>newInstance()</code> methods or by three public
83  * contructors. Both ways give the option to supply an initial array
84  * of <code>URL</code>s and (optionally) a parent classloader (that is
85  * different from the standard system class loader).</p>
86  *
87  * <p>Normally creating a <code>URLClassLoader</code> throws a
88  * <code>SecurityException</code> if a <code>SecurityManager</code> is
89  * installed and the <code>checkCreateClassLoader()</code> method does
90  * not return true.  But the <code>newInstance()</code> methods may be
91  * used by any code as long as it has permission to acces the given
92  * <code>URL</code>s.  <code>URLClassLoaders</code> created by the
93  * <code>newInstance()</code> methods also explicitly call the
94  * <code>checkPackageAccess()</code> method of
95  * <code>SecurityManager</code> if one is installed before trying to
96  * load a class.  Note that only subclasses of
97  * <code>URLClassLoader</code> can add new URLs after the
98  * URLClassLoader had been created. But it is always possible to get
99  * an array of all URLs that the class loader uses to resolve classes
100  * and resources by way of the <code>getURLs()</code> method.</p>
101  *
102  * <p>Open issues:
103  * <ul>
104  *
105  * <li>Should the URLClassLoader actually add the locations found in
106  * the manifest or is this the responsibility of some other
107  * loader/(sub)class?  (see <a
108  * href="http://java.sun.com/products/jdk/1.4/docs/guide/extensions/spec.html">
109  * Extension Mechanism Architecture - Bundles Extensions</a>)</li>
110  *
111  * <li>How does <code>definePackage()</code> and sealing work
112  * precisely?</li>
113  *
114  * <li>We save and use the security context (when a created by
115  * <code>newInstance()</code> but do we have to use it in more
116  * places?</li>
117  *
118  * <li>The use of <code>URLStreamHandler</code>s has not been tested.</li>
119  *
120  * </ul>
121  * </p>
122  *
123  * @since 1.2
124  *
125  * @author Mark Wielaard (mark@klomp.org)
126  * @author Wu Gansha (gansha.wu@intel.com)
127  */
128 public class URLClassLoader extends SecureClassLoader
129 {
130   // Class Variables
131
132   /**
133    * A cache to store mappings between handler factory and its
134    * private protocol handler cache (also a HashMap), so we can avoid
135    * creating handlers each time the same protocol comes.
136    */
137   private static URLStreamHandlerCache factoryCache
138     = new URLStreamHandlerCache();
139
140   /**
141    * The prefix for URL loaders.
142    */
143   private static final String URL_LOADER_PREFIX = "gnu.java.net.loader.Load_";
144
145   // Instance variables
146
147   /** Locations to load classes from */
148   private final Vector<URL> urls = new Vector<URL>();
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<URLLoader> urlinfos = new Vector<URLLoader>();
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    * Creates a URLClassLoader that gets classes from the supplied URLs.
172    * To determine if this classloader may be created the constructor of
173    * the super class (<code>SecureClassLoader</code>) is called first, which
174    * can throw a SecurityException. Then the supplied URLs are added
175    * in the order given to the URLClassLoader which uses these URLs to
176    * load classes and resources (after using the default parent ClassLoader).
177    *
178    * @param urls Locations that should be searched by this ClassLoader when
179    * resolving Classes or Resources.
180    * @exception SecurityException if the SecurityManager disallows the
181    * creation of a ClassLoader.
182    * @see SecureClassLoader
183    */
184   public URLClassLoader(URL[] urls) throws SecurityException
185   {
186     super();
187     this.factory = null;
188     this.securityContext = null;
189     addURLs(urls);
190   }
191
192   /**
193    * Creates a <code>URLClassLoader</code> that gets classes from the supplied
194    * <code>URL</code>s.
195    * To determine if this classloader may be created the constructor of
196    * the super class (<code>SecureClassLoader</code>) is called first, which
197    * can throw a SecurityException. Then the supplied URLs are added
198    * in the order given to the URLClassLoader which uses these URLs to
199    * load classes and resources (after using the supplied parent ClassLoader).
200    * @param urls Locations that should be searched by this ClassLoader when
201    * resolving Classes or Resources.
202    * @param parent The parent class loader used before trying this class
203    * loader.
204    * @exception SecurityException if the SecurityManager disallows the
205    * creation of a ClassLoader.
206    * @exception SecurityException
207    * @see SecureClassLoader
208    */
209   public URLClassLoader(URL[] urls, ClassLoader parent)
210     throws SecurityException
211   {
212     super(parent);
213     this.factory = null;
214     this.securityContext = null;
215     addURLs(urls);
216   }
217
218   // Package-private to avoid a trampoline constructor.
219   /**
220    * Package-private constructor used by the static
221    * <code>newInstance(URL[])</code> method.  Creates an
222    * <code>URLClassLoader</code> with the given parent but without any
223    * <code>URL</code>s yet. This is used to bypass the normal security
224    * check for creating classloaders, but remembers the security
225    * context which will be used when defining classes.  The
226    * <code>URL</code>s to load from must be added by the
227    * <code>newInstance()</code> method in the security context of the
228    * caller.
229    *
230    * @param securityContext the security context of the unprivileged code.
231    */
232   URLClassLoader(ClassLoader parent, AccessControlContext securityContext)
233   {
234     super(parent);
235     this.factory = null;
236     this.securityContext = securityContext;
237   }
238
239   /**
240    * Creates a URLClassLoader that gets classes from the supplied URLs.
241    * To determine if this classloader may be created the constructor of
242    * the super class (<CODE>SecureClassLoader</CODE>) is called first, which
243    * can throw a SecurityException. Then the supplied URLs are added
244    * in the order given to the URLClassLoader which uses these URLs to
245    * load classes and resources (after using the supplied parent ClassLoader).
246    * It will use the supplied <CODE>URLStreamHandlerFactory</CODE> to get the
247    * protocol handlers of the supplied URLs.
248    * @param urls Locations that should be searched by this ClassLoader when
249    * resolving Classes or Resources.
250    * @param parent The parent class loader used before trying this class
251    * loader.
252    * @param factory Used to get the protocol handler for the URLs.
253    * @exception SecurityException if the SecurityManager disallows the
254    * creation of a ClassLoader.
255    * @exception SecurityException
256    * @see SecureClassLoader
257    */
258   public URLClassLoader(URL[] urls, ClassLoader parent,
259                         URLStreamHandlerFactory factory)
260     throws SecurityException
261   {
262     super(parent);
263     this.securityContext = null;
264     this.factory = factory;
265     // If this factory is not yet in factoryCache, add it.
266     factoryCache.add(factory);
267     addURLs(urls);
268   }
269
270   // Methods
271
272   /**
273    * Adds a new location to the end of the internal URL store.
274    * @param newUrl the location to add
275    */
276   protected void addURL(URL newUrl)
277   {
278     urls.add(newUrl);
279     addURLImpl(newUrl);
280   }
281
282   private void addURLImpl(URL newUrl)
283   {
284     synchronized (this)
285       {
286         if (newUrl == null)
287           return; // Silently ignore...
288
289         // Reset the toString() value.
290         thisString = null;
291
292         // Create a loader for this URL.
293         URLLoader loader = null;
294         String file = newUrl.getFile();
295         String protocol = newUrl.getProtocol();
296
297         // If we have a file: URL, we want to make it absolute
298         // here, before we decide whether it is really a jar.
299         URL absoluteURL;
300         if ("file".equals (protocol))
301           {
302             File dir = new File(file);
303             try
304               {
305                 absoluteURL = dir.getCanonicalFile().toURL();
306               }
307             catch (IOException ignore)
308               {
309                 try
310                   {
311                     absoluteURL = dir.getAbsoluteFile().toURL();
312                   }
313                 catch (MalformedURLException _)
314                   {
315                     // This really should not happen.
316                     absoluteURL = newUrl;
317                   }
318               }
319           }
320         else
321           {
322             // This doesn't hurt, and it simplifies the logic a
323             // little.
324             absoluteURL = newUrl;
325           }
326
327         // First see if we can find a handler with the correct name.
328         try
329           {
330             Class<?> handler = Class.forName(URL_LOADER_PREFIX + protocol);
331             Class<?>[] argTypes = new Class<?>[] { URLClassLoader.class,
332                                                    URLStreamHandlerCache.class,
333                                                    URLStreamHandlerFactory.class,
334                                                    URL.class,
335                                                    URL.class };
336             Constructor k = handler.getDeclaredConstructor(argTypes);
337             loader
338               = (URLLoader) k.newInstance(new Object[] { this,
339                                                          factoryCache,
340                                                          factory,
341                                                          newUrl,
342                                                          absoluteURL });
343           }
344         catch (ClassNotFoundException ignore)
345           {
346             // Fall through.
347           }
348         catch (NoSuchMethodException nsme)
349           {
350             // Programming error in the class library.
351             InternalError vme
352               = new InternalError("couldn't find URLLoader constructor");
353             vme.initCause(nsme);
354             throw vme;
355           }
356         catch (InstantiationException inste)
357           {
358             // Programming error in the class library.
359             InternalError vme
360               = new InternalError("couldn't instantiate URLLoader");
361             vme.initCause(inste);
362             throw vme;
363           }
364         catch (InvocationTargetException ite)
365           {
366             // Programming error in the class library.
367             InternalError vme
368               = new InternalError("error instantiating URLLoader");
369             vme.initCause(ite);
370             throw vme;
371           }
372         catch (IllegalAccessException illae)
373           {
374             // Programming error in the class library.
375             InternalError vme
376               = new InternalError("invalid access to URLLoader");
377             vme.initCause(illae);
378             throw vme;
379           }
380
381         if (loader == null)
382           {
383             // If it is not a directory, use the jar loader.
384             if (! (file.endsWith("/") || file.endsWith(File.separator)))
385               loader = new JarURLLoader(this, factoryCache, factory,
386                                         newUrl, absoluteURL);
387             else if ("file".equals(protocol))
388               loader = new FileURLLoader(this, factoryCache, factory,
389                                          newUrl, absoluteURL);
390             else
391               loader = new RemoteURLLoader(this, factoryCache, factory,
392                                            newUrl);
393           }
394
395         urlinfos.add(loader);
396         ArrayList<URLLoader> extra = loader.getClassPath();
397         if (extra != null)
398           urlinfos.addAll(extra);
399       }
400   }
401
402   /**
403    * Adds an array of new locations to the end of the internal URL
404    * store.  Called from the the constructors. Should not call to the
405    * protected addURL() method since that can be overridden and
406    * subclasses are not yet in a good state at this point.
407    * jboss 4.0.3 for example depends on this.
408    *
409    * @param newUrls the locations to add
410    */
411   private void addURLs(URL[] newUrls)
412   {
413     for (int i = 0; i < newUrls.length; i++)
414       {
415         urls.add(newUrls[i]);
416         addURLImpl(newUrls[i]);
417       }
418   }
419
420   /**
421    * Look in both Attributes for a given value.  The first Attributes
422    * object, if not null, has precedence.
423    */
424   private String getAttributeValue(Attributes.Name name, Attributes first,
425                                    Attributes second)
426   {
427     String result = null;
428     if (first != null)
429       result = first.getValue(name);
430     if (result == null)
431       result = second.getValue(name);
432     return result;
433   }
434
435   /**
436    * Defines a Package based on the given name and the supplied manifest
437    * information. The manifest indicates the title, version and
438    * vendor information of the specification and implementation and whether the
439    * package is sealed. If the Manifest indicates that the package is sealed
440    * then the Package will be sealed with respect to the supplied URL.
441    *
442    * @param name The name of the package
443    * @param manifest The manifest describing the specification,
444    * implementation and sealing details of the package
445    * @param url the code source url to seal the package
446    * @return the defined Package
447    * @throws IllegalArgumentException If this package name already exists
448    * in this class loader
449    */
450   protected Package definePackage(String name, Manifest manifest, URL url)
451     throws IllegalArgumentException
452   {
453     // Compute the name of the package as it may appear in the
454     // Manifest.
455     StringBuilder xform = new StringBuilder(name);
456     for (int i = xform.length () - 1; i >= 0; --i)
457       if (xform.charAt(i) == '.')
458         xform.setCharAt(i, '/');
459     xform.append('/');
460     String xformName = xform.toString();
461
462     Attributes entryAttr = manifest.getAttributes(xformName);
463     Attributes attr = manifest.getMainAttributes();
464
465     String specTitle
466       = getAttributeValue(Attributes.Name.SPECIFICATION_TITLE,
467                           entryAttr, attr);
468     String specVersion
469       = getAttributeValue(Attributes.Name.SPECIFICATION_VERSION,
470                           entryAttr, attr);
471     String specVendor
472       = getAttributeValue(Attributes.Name.SPECIFICATION_VENDOR,
473                           entryAttr, attr);
474     String implTitle
475       = getAttributeValue(Attributes.Name.IMPLEMENTATION_TITLE,
476                           entryAttr, attr);
477     String implVersion
478       = getAttributeValue(Attributes.Name.IMPLEMENTATION_VERSION,
479                           entryAttr, attr);
480     String implVendor
481       = getAttributeValue(Attributes.Name.IMPLEMENTATION_VENDOR,
482                           entryAttr, attr);
483
484     // Look if the Manifest indicates that this package is sealed
485     // XXX - most likely not completely correct!
486     // Shouldn't we also check the sealed attribute of the complete jar?
487     // http://java.sun.com/products/jdk/1.4/docs/guide/extensions/spec.html#bundled
488     // But how do we get that jar manifest here?
489     String sealed = attr.getValue(Attributes.Name.SEALED);
490     if ("false".equals(sealed))
491       // make sure that the URL is null so the package is not sealed
492       url = null;
493
494     return definePackage(name,
495                          specTitle, specVendor, specVersion,
496                          implTitle, implVendor, implVersion,
497                          url);
498   }
499
500   /**
501    * Finds (the first) class by name from one of the locations. The locations
502    * are searched in the order they were added to the URLClassLoader.
503    *
504    * @param className the classname to find
505    * @exception ClassNotFoundException when the class could not be found or
506    * loaded
507    * @return a Class object representing the found class
508    */
509   protected Class<?> findClass(final String className)
510     throws ClassNotFoundException
511   {
512     // Just try to find the resource by the (almost) same name
513     String resourceName = className.replace('.', '/') + ".class";
514     int max = urlinfos.size();
515     Resource resource = null;
516     for (int i = 0; i < max && resource == null; i++)
517       {
518         URLLoader loader = (URLLoader)urlinfos.elementAt(i);
519         if (loader == null)
520           continue;
521
522         Class k = loader.getClass(className);
523         if (k != null)
524           return k;
525
526         resource = loader.getResource(resourceName);
527       }
528     if (resource == null)
529       throw new ClassNotFoundException(className + " not found in " + this);
530
531     // Try to read the class data, create the CodeSource, Package and
532     // construct the class (and watch out for those nasty IOExceptions)
533     try
534       {
535         byte[] data;
536         InputStream in = resource.getInputStream();
537         try
538           {
539             int length = resource.getLength();
540             if (length != -1)
541               {
542                 // We know the length of the data.
543                 // Just try to read it in all at once
544                 data = new byte[length];
545                 int pos = 0;
546                 while (length - pos > 0)
547                   {
548                     int len = in.read(data, pos, length - pos);
549                     if (len == -1)
550                       throw new EOFException("Not enough data reading from: "
551                                              + in);
552                     pos += len;
553                   }
554               }
555             else
556               {
557                 // We don't know the data length.
558                 // Have to read it in chunks.
559                 ByteArrayOutputStream out = new ByteArrayOutputStream(4096);
560                 byte[] b = new byte[4096];
561                 int l = 0;
562                 while (l != -1)
563                   {
564                     l = in.read(b);
565                     if (l != -1)
566                       out.write(b, 0, l);
567                   }
568                 data = out.toByteArray();
569               }
570           }
571         finally
572           {
573             in.close();
574           }
575         final byte[] classData = data;
576
577         // Now get the CodeSource
578         final CodeSource source = resource.getCodeSource();
579
580         // Find out package name
581         String packageName = null;
582         int lastDot = className.lastIndexOf('.');
583         if (lastDot != -1)
584           packageName = className.substring(0, lastDot);
585
586         if (packageName != null && getPackage(packageName) == null)
587           {
588             // define the package
589             Manifest manifest = resource.getLoader().getManifest();
590             if (manifest == null)
591               definePackage(packageName, null, null, null, null, null, null,
592                             null);
593             else
594               definePackage(packageName, manifest,
595                             resource.getLoader().getBaseURL());
596           }
597
598         // And finally construct the class!
599         SecurityManager sm = System.getSecurityManager();
600         Class result = null;
601         if (sm != null && securityContext != null)
602           {
603             result = AccessController.doPrivileged
604               (new PrivilegedAction<Class>()
605                 {
606                   public Class run()
607                   {
608                     return defineClass(className, classData,
609                                        0, classData.length,
610                                        source);
611                   }
612                 }, securityContext);
613           }
614         else
615           result = defineClass(className, classData, 0, classData.length, source);
616
617         // Avoid NullPointerExceptions.
618         Certificate[] resourceCertificates = resource.getCertificates();
619         if(resourceCertificates != null)
620           super.setSigners(result, resourceCertificates);
621         
622         return result;
623       }
624     catch (IOException ioe)
625       {
626         throw new ClassNotFoundException(className + " not found in " + this, ioe);
627       }
628   }
629   
630   // Cached String representation of this URLClassLoader
631   private String thisString;
632   
633   /**
634    * Returns a String representation of this URLClassLoader giving the
635    * actual Class name, the URLs that are searched and the parent
636    * ClassLoader.
637    */
638   public String toString()
639   {
640     synchronized (this)
641       {
642         if (thisString == null)
643           {
644             StringBuilder sb = new StringBuilder();
645             sb.append(this.getClass().getName());
646             sb.append("{urls=[" );
647             URL[] thisURLs = getURLs();
648             for (int i = 0; i < thisURLs.length; i++)
649               {
650                 sb.append(thisURLs[i]);
651                 if (i < thisURLs.length - 1)
652                   sb.append(',');
653               }
654             sb.append(']');
655             sb.append(", parent=");
656             sb.append(getParent());
657             sb.append('}');
658             thisString = sb.toString();
659           }
660         return thisString;
661       }
662   }
663
664   /**
665    * Finds the first occurrence of a resource that can be found. The locations
666    * are searched in the order they were added to the URLClassLoader.
667    *
668    * @param resourceName the resource name to look for
669    * @return the URLResource for the resource if found, null otherwise
670    */
671   private Resource findURLResource(String resourceName)
672   {
673     int max = urlinfos.size();
674     for (int i = 0; i < max; i++)
675       {
676         URLLoader loader = (URLLoader) urlinfos.elementAt(i);
677         if (loader == null)
678           continue;
679
680         Resource resource = loader.getResource(resourceName);
681         if (resource != null)
682           return resource;
683       }
684     return null;
685   }
686
687   /**
688    * Finds the first occurrence of a resource that can be found.
689    *
690    * @param resourceName the resource name to look for
691    * @return the URL if found, null otherwise
692    */
693   public URL findResource(String resourceName)
694   {
695     Resource resource = findURLResource(resourceName);
696     if (resource != null)
697       return resource.getURL();
698
699     // Resource not found
700     return null;
701   }
702
703   /**
704    * Finds all the resources with a particular name from all the locations.
705    *
706    * @param resourceName the name of the resource to lookup
707    * @return a (possible empty) enumeration of URLs where the resource can be
708    * found
709    * @exception IOException when an error occurs accessing one of the
710    * locations
711    */
712   public Enumeration<URL> findResources(String resourceName)
713     throws IOException
714   {
715     Vector<URL> resources = new Vector<URL>();
716     int max = urlinfos.size();
717     for (int i = 0; i < max; i++)
718       {
719         URLLoader loader = (URLLoader) urlinfos.elementAt(i);
720         Resource resource = loader.getResource(resourceName);
721         if (resource != null)
722           resources.add(resource.getURL());
723       }
724     return resources.elements();
725   }
726
727   /**
728    * Returns the permissions needed to access a particular code
729    * source.  These permissions includes those returned by
730    * <code>SecureClassLoader.getPermissions()</code> and the actual
731    * permissions to access the objects referenced by the URL of the
732    * code source.  The extra permissions added depend on the protocol
733    * and file portion of the URL in the code source. If the URL has
734    * the "file" protocol ends with a '/' character then it must be a
735    * directory and a file Permission to read everything in that
736    * directory and all subdirectories is added. If the URL had the
737    * "file" protocol and doesn't end with a '/' character then it must
738    * be a normal file and a file permission to read that file is
739    * added. If the <code>URL</code> has any other protocol then a
740    * socket permission to connect and accept connections from the host
741    * portion of the URL is added.
742    *
743    * @param source The codesource that needs the permissions to be accessed
744    * @return the collection of permissions needed to access the code resource
745    * @see java.security.SecureClassLoader#getPermissions(CodeSource)
746    */
747   protected PermissionCollection getPermissions(CodeSource source)
748   {
749     // XXX - This implementation does exactly as the Javadoc describes.
750     // But maybe we should/could use URLConnection.getPermissions()?
751     // First get the permissions that would normally be granted
752     PermissionCollection permissions = super.getPermissions(source);
753
754     // Now add any extra permissions depending on the URL location.
755     URL url = source.getLocation();
756     String protocol = url.getProtocol();
757     if (protocol.equals("file"))
758       {
759         String file = url.getFile();
760
761         // If the file end in / it must be an directory.
762         if (file.endsWith("/") || file.endsWith(File.separator))
763           {
764             // Grant permission to read everything in that directory and
765             // all subdirectories.
766             permissions.add(new FilePermission(file + "-", "read"));
767           }
768         else
769           {
770             // It is a 'normal' file.
771             // Grant permission to access that file.
772             permissions.add(new FilePermission(file, "read"));
773           }
774       }
775     else
776       {
777         // Grant permission to connect to and accept connections from host
778         String host = url.getHost();
779         if (host != null)
780           permissions.add(new SocketPermission(host, "connect,accept"));
781       }
782
783     return permissions;
784   }
785
786   /**
787    * Returns all the locations that this class loader currently uses the
788    * resolve classes and resource. This includes both the initially supplied
789    * URLs as any URLs added later by the loader.
790    * @return All the currently used URLs
791    */
792   public URL[] getURLs()
793   {
794     return (URL[]) urls.toArray(new URL[urls.size()]);
795   }
796
797   /**
798    * Creates a new instance of a <code>URLClassLoader</code> that gets
799    * classes from the supplied <code>URL</code>s. This class loader
800    * will have as parent the standard system class loader.
801    *
802    * @param urls the initial URLs used to resolve classes and
803    * resources
804    *
805    * @return the class loader
806    *
807    * @exception SecurityException when the calling code does not have
808    * permission to access the given <code>URL</code>s
809    */
810   public static URLClassLoader newInstance(URL[] urls)
811     throws SecurityException
812   {
813     return newInstance(urls, null);
814   }
815
816   /**
817    * Creates a new instance of a <code>URLClassLoader</code> that gets
818    * classes from the supplied <code>URL</code>s and with the supplied
819    * loader as parent class loader.
820    *
821    * @param urls the initial URLs used to resolve classes and
822    * resources
823    * @param parent the parent class loader
824    *
825    * @return the class loader
826    *
827    * @exception SecurityException when the calling code does not have
828    * permission to access the given <code>URL</code>s
829    */
830   public static URLClassLoader newInstance(URL[] urls, final ClassLoader parent)
831     throws SecurityException
832   {
833     SecurityManager sm = System.getSecurityManager();
834     if (sm == null)
835       return new URLClassLoader(urls, parent);
836     else
837       {
838         final Object securityContext = sm.getSecurityContext();
839
840         // XXX - What to do with anything else then an AccessControlContext?
841         if (! (securityContext instanceof AccessControlContext))
842           throw new SecurityException("securityContext must be AccessControlContext: "
843                                       + securityContext);
844
845         URLClassLoader loader =
846           AccessController.doPrivileged(new PrivilegedAction<URLClassLoader>()
847               {
848                 public URLClassLoader run()
849                 {
850                   return new URLClassLoader(parent,
851                                             (AccessControlContext) securityContext);
852                 }
853               });
854         loader.addURLs(urls);
855         return loader;
856       }
857   }
858 }