OSDN Git Service

Merged gcj-eclipse branch to trunk.
[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 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    * 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     addURLs(urls);
266
267     // If this factory is still not in factoryCache, add it.
268     factoryCache.add(factory);
269   }
270
271   // Methods
272
273   /**
274    * Adds a new location to the end of the internal URL store.
275    * @param newUrl the location to add
276    */
277   protected void addURL(URL newUrl)
278   {
279     urls.add(newUrl);
280     addURLImpl(newUrl);
281   }
282
283   private void addURLImpl(URL newUrl)
284   {
285     synchronized (this)
286       {
287         if (newUrl == null)
288           return; // Silently ignore...
289
290         // Reset the toString() value.
291         thisString = null;
292
293         // Create a loader for this URL.
294         URLLoader loader = null;
295         String file = newUrl.getFile();
296         String protocol = newUrl.getProtocol();
297
298         // If we have a file: URL, we want to make it absolute
299         // here, before we decide whether it is really a jar.
300         URL absoluteURL;
301         if ("file".equals (protocol))
302           {
303             File dir = new File(file);
304             URL absUrl;
305             try
306               {
307                 absoluteURL = dir.getCanonicalFile().toURL();
308               }
309             catch (IOException ignore)
310               {
311                 try
312                   {
313                     absoluteURL = dir.getAbsoluteFile().toURL();
314                   }
315                 catch (MalformedURLException _)
316                   {
317                     // This really should not happen.
318                     absoluteURL = newUrl;
319                   }
320               }
321           }
322         else
323           {
324             // This doesn't hurt, and it simplifies the logic a
325             // little.
326             absoluteURL = newUrl;
327           }
328
329         // First see if we can find a handler with the correct name.
330         try
331           {
332             Class handler = Class.forName(URL_LOADER_PREFIX + protocol);
333             Class[] argTypes = new Class[] { URLClassLoader.class,
334                                              URLStreamHandlerCache.class,
335                                              URLStreamHandlerFactory.class,
336                                              URL.class,
337                                              URL.class };
338             Constructor k = handler.getDeclaredConstructor(argTypes);
339             loader
340               = (URLLoader) k.newInstance(new Object[] { this,
341                                                          factoryCache,
342                                                          factory,
343                                                          newUrl,
344                                                          absoluteURL });
345           }
346         catch (ClassNotFoundException ignore)
347           {
348             // Fall through.
349           }
350         catch (NoSuchMethodException nsme)
351           {
352             // Programming error in the class library.
353             InternalError vme
354               = new InternalError("couldn't find URLLoader constructor");
355             vme.initCause(nsme);
356             throw vme;
357           }
358         catch (InstantiationException inste)
359           {
360             // Programming error in the class library.
361             InternalError vme
362               = new InternalError("couldn't instantiate URLLoader");
363             vme.initCause(inste);
364             throw vme;
365           }
366         catch (InvocationTargetException ite)
367           {
368             // Programming error in the class library.
369             InternalError vme
370               = new InternalError("error instantiating URLLoader");
371             vme.initCause(ite);
372             throw vme;
373           }
374         catch (IllegalAccessException illae)
375           {
376             // Programming error in the class library.
377             InternalError vme
378               = new InternalError("invalid access to URLLoader");
379             vme.initCause(illae);
380             throw vme;
381           }
382
383         if (loader == null)
384           {
385             // If it is not a directory, use the jar loader.
386             if (! (file.endsWith("/") || file.endsWith(File.separator)))
387               loader = new JarURLLoader(this, factoryCache, factory,
388                                         newUrl, absoluteURL);
389             else if ("file".equals(protocol))
390               loader = new FileURLLoader(this, factoryCache, factory,
391                                          newUrl, absoluteURL);
392             else
393               loader = new RemoteURLLoader(this, factoryCache, factory,
394                                            newUrl);
395           }
396
397         urlinfos.add(loader);
398         ArrayList extra = loader.getClassPath();
399         if (extra != null)
400           urlinfos.addAll(extra);
401       }
402   }
403
404   /**
405    * Adds an array of new locations to the end of the internal URL
406    * store.  Called from the the constructors. Should not call to the
407    * protected addURL() method since that can be overridden and
408    * subclasses are not yet in a good state at this point.
409    * jboss 4.0.3 for example depends on this.
410    *
411    * @param newUrls the locations to add
412    */
413   private void addURLs(URL[] newUrls)
414   {
415     for (int i = 0; i < newUrls.length; i++)
416       {
417         urls.add(newUrls[i]);
418         addURLImpl(newUrls[i]);
419       }
420   }
421
422   /**
423    * Look in both Attributes for a given value.  The first Attributes
424    * object, if not null, has precedence.
425    */
426   private String getAttributeValue(Attributes.Name name, Attributes first,
427                                    Attributes second)
428   {
429     String result = null;
430     if (first != null)
431       result = first.getValue(name);
432     if (result == null)
433       result = second.getValue(name);
434     return result;
435   }
436
437   /**
438    * Defines a Package based on the given name and the supplied manifest
439    * information. The manifest indicates the title, version and
440    * vendor information of the specification and implementation and whether the
441    * package is sealed. If the Manifest indicates that the package is sealed
442    * then the Package will be sealed with respect to the supplied URL.
443    *
444    * @param name The name of the package
445    * @param manifest The manifest describing the specification,
446    * implementation and sealing details of the package
447    * @param url the code source url to seal the package
448    * @return the defined Package
449    * @throws IllegalArgumentException If this package name already exists
450    * in this class loader
451    */
452   protected Package definePackage(String name, Manifest manifest, URL url)
453     throws IllegalArgumentException
454   {
455     // Compute the name of the package as it may appear in the
456     // Manifest.
457     StringBuffer xform = new StringBuffer(name);
458     for (int i = xform.length () - 1; i >= 0; --i)
459       if (xform.charAt(i) == '.')
460         xform.setCharAt(i, '/');
461     xform.append('/');
462     String xformName = xform.toString();
463
464     Attributes entryAttr = manifest.getAttributes(xformName);
465     Attributes attr = manifest.getMainAttributes();
466
467     String specTitle
468       = getAttributeValue(Attributes.Name.SPECIFICATION_TITLE,
469                           entryAttr, attr);
470     String specVersion
471       = getAttributeValue(Attributes.Name.SPECIFICATION_VERSION,
472                           entryAttr, attr);
473     String specVendor
474       = getAttributeValue(Attributes.Name.SPECIFICATION_VENDOR,
475                           entryAttr, attr);
476     String implTitle
477       = getAttributeValue(Attributes.Name.IMPLEMENTATION_TITLE,
478                           entryAttr, attr);
479     String implVersion
480       = getAttributeValue(Attributes.Name.IMPLEMENTATION_VERSION,
481                           entryAttr, attr);
482     String implVendor
483       = getAttributeValue(Attributes.Name.IMPLEMENTATION_VENDOR,
484                           entryAttr, attr);
485
486     // Look if the Manifest indicates that this package is sealed
487     // XXX - most likely not completely correct!
488     // Shouldn't we also check the sealed attribute of the complete jar?
489     // http://java.sun.com/products/jdk/1.4/docs/guide/extensions/spec.html#bundled
490     // But how do we get that jar manifest here?
491     String sealed = attr.getValue(Attributes.Name.SEALED);
492     if ("false".equals(sealed))
493       // make sure that the URL is null so the package is not sealed
494       url = null;
495
496     return definePackage(name,
497                          specTitle, specVendor, specVersion,
498                          implTitle, implVendor, implVersion,
499                          url);
500   }
501
502   /**
503    * Finds (the first) class by name from one of the locations. The locations
504    * are searched in the order they were added to the URLClassLoader.
505    *
506    * @param className the classname to find
507    * @exception ClassNotFoundException when the class could not be found or
508    * loaded
509    * @return a Class object representing the found class
510    */
511   protected Class<?> findClass(final String className)
512     throws ClassNotFoundException
513   {
514     // Just try to find the resource by the (almost) same name
515     String resourceName = className.replace('.', '/') + ".class";
516     int max = urlinfos.size();
517     Resource resource = null;
518     for (int i = 0; i < max && resource == null; i++)
519       {
520         URLLoader loader = (URLLoader)urlinfos.elementAt(i);
521         if (loader == null)
522           continue;
523
524         Class k = loader.getClass(className);
525         if (k != null)
526           return k;
527
528         resource = loader.getResource(resourceName);
529       }
530     if (resource == null)
531       throw new ClassNotFoundException(className + " not found in " + this);
532
533     // Try to read the class data, create the CodeSource, Package and
534     // construct the class (and watch out for those nasty IOExceptions)
535     try
536       {
537         byte[] data;
538         InputStream in = resource.getInputStream();
539         try
540           {
541             int length = resource.getLength();
542             if (length != -1)
543               {
544                 // We know the length of the data.
545                 // Just try to read it in all at once
546                 data = new byte[length];
547                 int pos = 0;
548                 while (length - pos > 0)
549                   {
550                     int len = in.read(data, pos, length - pos);
551                     if (len == -1)
552                       throw new EOFException("Not enough data reading from: "
553                                              + in);
554                     pos += len;
555                   }
556               }
557             else
558               {
559                 // We don't know the data length.
560                 // Have to read it in chunks.
561                 ByteArrayOutputStream out = new ByteArrayOutputStream(4096);
562                 byte[] b = new byte[4096];
563                 int l = 0;
564                 while (l != -1)
565                   {
566                     l = in.read(b);
567                     if (l != -1)
568                       out.write(b, 0, l);
569                   }
570                 data = out.toByteArray();
571               }
572           }
573         finally
574           {
575             in.close();
576           }
577         final byte[] classData = data;
578
579         // Now get the CodeSource
580         final CodeSource source = resource.getCodeSource();
581
582         // Find out package name
583         String packageName = null;
584         int lastDot = className.lastIndexOf('.');
585         if (lastDot != -1)
586           packageName = className.substring(0, lastDot);
587
588         if (packageName != null && getPackage(packageName) == null)
589           {
590             // define the package
591             Manifest manifest = resource.getLoader().getManifest();
592             if (manifest == null)
593               definePackage(packageName, null, null, null, null, null, null,
594                             null);
595             else
596               definePackage(packageName, manifest,
597                             resource.getLoader().getBaseURL());
598           }
599
600         // And finally construct the class!
601         SecurityManager sm = System.getSecurityManager();
602         Class result = null;
603         if (sm != null && securityContext != null)
604           {
605             result = (Class)AccessController.doPrivileged
606               (new PrivilegedAction()
607                 {
608                   public Object run()
609                   {
610                     return defineClass(className, classData,
611                                        0, classData.length,
612                                        source);
613                   }
614                 }, securityContext);
615           }
616         else
617           result = defineClass(className, classData, 0, classData.length, source);
618
619         // Avoid NullPointerExceptions.
620         Certificate[] resourceCertificates = resource.getCertificates();
621         if(resourceCertificates != null)
622           super.setSigners(result, resourceCertificates);
623         
624         return result;
625       }
626     catch (IOException ioe)
627       {
628         ClassNotFoundException cnfe;
629         cnfe = new ClassNotFoundException(className + " not found in " + this);
630         cnfe.initCause(ioe);
631         throw cnfe;
632       }
633   }
634   
635   // Cached String representation of this URLClassLoader
636   private String thisString;
637   
638   /**
639    * Returns a String representation of this URLClassLoader giving the
640    * actual Class name, the URLs that are searched and the parent
641    * ClassLoader.
642    */
643   public String toString()
644   {
645     synchronized (this)
646       {
647         if (thisString == null)
648           {
649             StringBuffer sb = new StringBuffer();
650             sb.append(this.getClass().getName());
651             sb.append("{urls=[" );
652             URL[] thisURLs = getURLs();
653             for (int i = 0; i < thisURLs.length; i++)
654               {
655                 sb.append(thisURLs[i]);
656                 if (i < thisURLs.length - 1)
657                   sb.append(',');
658               }
659             sb.append(']');
660             sb.append(", parent=");
661             sb.append(getParent());
662             sb.append('}');
663             thisString = sb.toString();
664           }
665         return thisString;
666       }
667   }
668
669   /**
670    * Finds the first occurrence of a resource that can be found. The locations
671    * are searched in the order they were added to the URLClassLoader.
672    *
673    * @param resourceName the resource name to look for
674    * @return the URLResource for the resource if found, null otherwise
675    */
676   private Resource findURLResource(String resourceName)
677   {
678     int max = urlinfos.size();
679     for (int i = 0; i < max; i++)
680       {
681         URLLoader loader = (URLLoader) urlinfos.elementAt(i);
682         if (loader == null)
683           continue;
684
685         Resource resource = loader.getResource(resourceName);
686         if (resource != null)
687           return resource;
688       }
689     return null;
690   }
691
692   /**
693    * Finds the first occurrence of a resource that can be found.
694    *
695    * @param resourceName the resource name to look for
696    * @return the URL if found, null otherwise
697    */
698   public URL findResource(String resourceName)
699   {
700     Resource resource = findURLResource(resourceName);
701     if (resource != null)
702       return resource.getURL();
703
704     // Resource not found
705     return null;
706   }
707
708   /**
709    * Finds all the resources with a particular name from all the locations.
710    *
711    * @param resourceName the name of the resource to lookup
712    * @return a (possible empty) enumeration of URLs where the resource can be
713    * found
714    * @exception IOException when an error occurs accessing one of the
715    * locations
716    */
717   public Enumeration<URL> findResources(String resourceName)
718     throws IOException
719   {
720     Vector<URL> resources = new Vector<URL>();
721     int max = urlinfos.size();
722     for (int i = 0; i < max; i++)
723       {
724         URLLoader loader = (URLLoader) urlinfos.elementAt(i);
725         Resource resource = loader.getResource(resourceName);
726         if (resource != null)
727           resources.add(resource.getURL());
728       }
729     return resources.elements();
730   }
731
732   /**
733    * Returns the permissions needed to access a particular code
734    * source.  These permissions includes those returned by
735    * <code>SecureClassLoader.getPermissions()</code> and the actual
736    * permissions to access the objects referenced by the URL of the
737    * code source.  The extra permissions added depend on the protocol
738    * and file portion of the URL in the code source. If the URL has
739    * the "file" protocol ends with a '/' character then it must be a
740    * directory and a file Permission to read everything in that
741    * directory and all subdirectories is added. If the URL had the
742    * "file" protocol and doesn't end with a '/' character then it must
743    * be a normal file and a file permission to read that file is
744    * added. If the <code>URL</code> has any other protocol then a
745    * socket permission to connect and accept connections from the host
746    * portion of the URL is added.
747    *
748    * @param source The codesource that needs the permissions to be accessed
749    * @return the collection of permissions needed to access the code resource
750    * @see java.security.SecureClassLoader#getPermissions(CodeSource)
751    */
752   protected PermissionCollection getPermissions(CodeSource source)
753   {
754     // XXX - This implementation does exactly as the Javadoc describes.
755     // But maybe we should/could use URLConnection.getPermissions()?
756     // First get the permissions that would normally be granted
757     PermissionCollection permissions = super.getPermissions(source);
758
759     // Now add any extra permissions depending on the URL location.
760     URL url = source.getLocation();
761     String protocol = url.getProtocol();
762     if (protocol.equals("file"))
763       {
764         String file = url.getFile();
765
766         // If the file end in / it must be an directory.
767         if (file.endsWith("/") || file.endsWith(File.separator))
768           {
769             // Grant permission to read everything in that directory and
770             // all subdirectories.
771             permissions.add(new FilePermission(file + "-", "read"));
772           }
773         else
774           {
775             // It is a 'normal' file.
776             // Grant permission to access that file.
777             permissions.add(new FilePermission(file, "read"));
778           }
779       }
780     else
781       {
782         // Grant permission to connect to and accept connections from host
783         String host = url.getHost();
784         if (host != null)
785           permissions.add(new SocketPermission(host, "connect,accept"));
786       }
787
788     return permissions;
789   }
790
791   /**
792    * Returns all the locations that this class loader currently uses the
793    * resolve classes and resource. This includes both the initially supplied
794    * URLs as any URLs added later by the loader.
795    * @return All the currently used URLs
796    */
797   public URL[] getURLs()
798   {
799     return (URL[]) urls.toArray(new URL[urls.size()]);
800   }
801
802   /**
803    * Creates a new instance of a <code>URLClassLoader</code> that gets
804    * classes from the supplied <code>URL</code>s. This class loader
805    * will have as parent the standard system class loader.
806    *
807    * @param urls the initial URLs used to resolve classes and
808    * resources
809    *
810    * @return the class loader
811    *
812    * @exception SecurityException when the calling code does not have
813    * permission to access the given <code>URL</code>s
814    */
815   public static URLClassLoader newInstance(URL[] urls)
816     throws SecurityException
817   {
818     return newInstance(urls, null);
819   }
820
821   /**
822    * Creates a new instance of a <code>URLClassLoader</code> that gets
823    * classes from the supplied <code>URL</code>s and with the supplied
824    * loader as parent class loader.
825    *
826    * @param urls the initial URLs used to resolve classes and
827    * resources
828    * @param parent the parent class loader
829    *
830    * @return the class loader
831    *
832    * @exception SecurityException when the calling code does not have
833    * permission to access the given <code>URL</code>s
834    */
835   public static URLClassLoader newInstance(URL[] urls, final ClassLoader parent)
836     throws SecurityException
837   {
838     SecurityManager sm = System.getSecurityManager();
839     if (sm == null)
840       return new URLClassLoader(urls, parent);
841     else
842       {
843         final Object securityContext = sm.getSecurityContext();
844
845         // XXX - What to do with anything else then an AccessControlContext?
846         if (! (securityContext instanceof AccessControlContext))
847           throw new SecurityException("securityContext must be AccessControlContext: "
848                                       + securityContext);
849
850         URLClassLoader loader =
851           (URLClassLoader) AccessController.doPrivileged(new PrivilegedAction()
852               {
853                 public Object run()
854                 {
855                   return new URLClassLoader(parent,
856                                             (AccessControlContext) securityContext);
857                 }
858               });
859         loader.addURLs(urls);
860         return loader;
861       }
862   }
863 }