001    /* URLClassLoader.java --  ClassLoader that loads classes from one or more URLs
002       Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
003       Free Software Foundation, Inc.
004    
005    This file is part of GNU Classpath.
006    
007    GNU Classpath is free software; you can redistribute it and/or modify
008    it under the terms of the GNU General Public License as published by
009    the Free Software Foundation; either version 2, or (at your option)
010    any later version.
011    
012    GNU Classpath is distributed in the hope that it will be useful, but
013    WITHOUT ANY WARRANTY; without even the implied warranty of
014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015    General Public License for more details.
016    
017    You should have received a copy of the GNU General Public License
018    along with GNU Classpath; see the file COPYING.  If not, write to the
019    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
020    02110-1301 USA.
021    
022    Linking this library statically or dynamically with other modules is
023    making a combined work based on this library.  Thus, the terms and
024    conditions of the GNU General Public License cover the whole
025    combination.
026    
027    As a special exception, the copyright holders of this library give you
028    permission to link this library with independent modules to produce an
029    executable, regardless of the license terms of these independent
030    modules, and to copy and distribute the resulting executable under
031    terms of your choice, provided that you also meet, for each linked
032    independent module, the terms and conditions of the license of that
033    module.  An independent module is a module which is not derived from
034    or based on this library.  If you modify this library, you may extend
035    this exception to your version of the library, but you are not
036    obligated to do so.  If you do not wish to do so, delete this
037    exception statement from your version. */
038    
039    
040    package java.net;
041    
042    import gnu.java.net.loader.FileURLLoader;
043    import gnu.java.net.loader.JarURLLoader;
044    import gnu.java.net.loader.RemoteURLLoader;
045    import gnu.java.net.loader.Resource;
046    import gnu.java.net.loader.URLLoader;
047    import gnu.java.net.loader.URLStreamHandlerCache;
048    
049    import java.io.ByteArrayOutputStream;
050    import java.io.EOFException;
051    import java.io.File;
052    import java.io.FilePermission;
053    import java.io.IOException;
054    import java.io.InputStream;
055    import java.lang.reflect.Constructor;
056    import java.lang.reflect.InvocationTargetException;
057    import java.security.AccessControlContext;
058    import java.security.AccessController;
059    import java.security.CodeSource;
060    import java.security.PermissionCollection;
061    import java.security.PrivilegedAction;
062    import java.security.SecureClassLoader;
063    import java.security.cert.Certificate;
064    import java.util.ArrayList;
065    import java.util.Enumeration;
066    import java.util.Vector;
067    import java.util.jar.Attributes;
068    import java.util.jar.Manifest;
069    
070    
071    /**
072     * A secure class loader that can load classes and resources from
073     * multiple locations.  Given an array of <code>URL</code>s this class
074     * loader will retrieve classes and resources by fetching them from
075     * possible remote locations.  Each <code>URL</code> is searched in
076     * order in which it was added.  If the file portion of the
077     * <code>URL</code> ends with a '/' character then it is interpreted
078     * as a base directory, otherwise it is interpreted as a jar file from
079     * which the classes/resources are resolved.
080     *
081     * <p>New instances can be created by two static
082     * <code>newInstance()</code> methods or by three public
083     * contructors. Both ways give the option to supply an initial array
084     * of <code>URL</code>s and (optionally) a parent classloader (that is
085     * different from the standard system class loader).</p>
086     *
087     * <p>Normally creating a <code>URLClassLoader</code> throws a
088     * <code>SecurityException</code> if a <code>SecurityManager</code> is
089     * installed and the <code>checkCreateClassLoader()</code> method does
090     * not return true.  But the <code>newInstance()</code> methods may be
091     * used by any code as long as it has permission to acces the given
092     * <code>URL</code>s.  <code>URLClassLoaders</code> created by the
093     * <code>newInstance()</code> methods also explicitly call the
094     * <code>checkPackageAccess()</code> method of
095     * <code>SecurityManager</code> if one is installed before trying to
096     * load a class.  Note that only subclasses of
097     * <code>URLClassLoader</code> can add new URLs after the
098     * URLClassLoader had been created. But it is always possible to get
099     * 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        StringBuffer xform = new StringBuffer(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                StringBuffer sb = new StringBuffer();
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    }