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.lang.CPStringBuilder;
043    
044    import gnu.java.net.loader.FileURLLoader;
045    import gnu.java.net.loader.JarURLLoader;
046    import gnu.java.net.loader.RemoteURLLoader;
047    import gnu.java.net.loader.Resource;
048    import gnu.java.net.loader.URLLoader;
049    import gnu.java.net.loader.URLStreamHandlerCache;
050    
051    import java.io.ByteArrayOutputStream;
052    import java.io.EOFException;
053    import java.io.File;
054    import java.io.FilePermission;
055    import java.io.IOException;
056    import java.io.InputStream;
057    import java.lang.reflect.Constructor;
058    import java.lang.reflect.InvocationTargetException;
059    import java.security.AccessControlContext;
060    import java.security.AccessController;
061    import java.security.CodeSource;
062    import java.security.PermissionCollection;
063    import java.security.PrivilegedAction;
064    import java.security.SecureClassLoader;
065    import java.security.cert.Certificate;
066    import java.util.ArrayList;
067    import java.util.Enumeration;
068    import java.util.Vector;
069    import java.util.jar.Attributes;
070    import java.util.jar.Manifest;
071    
072    
073    /**
074     * A secure class loader that can load classes and resources from
075     * multiple locations.  Given an array of <code>URL</code>s this class
076     * loader will retrieve classes and resources by fetching them from
077     * possible remote locations.  Each <code>URL</code> is searched in
078     * order in which it was added.  If the file portion of the
079     * <code>URL</code> ends with a '/' character then it is interpreted
080     * as a base directory, otherwise it is interpreted as a jar file from
081     * which the classes/resources are resolved.
082     *
083     * <p>New instances can be created by two static
084     * <code>newInstance()</code> methods or by three public
085     * contructors. Both ways give the option to supply an initial array
086     * of <code>URL</code>s and (optionally) a parent classloader (that is
087     * different from the standard system class loader).</p>
088     *
089     * <p>Normally creating a <code>URLClassLoader</code> throws a
090     * <code>SecurityException</code> if a <code>SecurityManager</code> is
091     * installed and the <code>checkCreateClassLoader()</code> method does
092     * not return true.  But the <code>newInstance()</code> methods may be
093     * used by any code as long as it has permission to acces the given
094     * <code>URL</code>s.  <code>URLClassLoaders</code> created by the
095     * <code>newInstance()</code> methods also explicitly call the
096     * <code>checkPackageAccess()</code> method of
097     * <code>SecurityManager</code> if one is installed before trying to
098     * load a class.  Note that only subclasses of
099     * <code>URLClassLoader</code> can add new URLs after the
100     * URLClassLoader had been created. But it is always possible to get
101     * an array of all URLs that the class loader uses to resolve classes
102     * and resources by way of the <code>getURLs()</code> method.</p>
103     *
104     * <p>Open issues:
105     * <ul>
106     *
107     * <li>Should the URLClassLoader actually add the locations found in
108     * the manifest or is this the responsibility of some other
109     * loader/(sub)class?  (see <a
110     * href="http://java.sun.com/products/jdk/1.4/docs/guide/extensions/spec.html">
111     * Extension Mechanism Architecture - Bundles Extensions</a>)</li>
112     *
113     * <li>How does <code>definePackage()</code> and sealing work
114     * precisely?</li>
115     *
116     * <li>We save and use the security context (when a created by
117     * <code>newInstance()</code> but do we have to use it in more
118     * places?</li>
119     *
120     * <li>The use of <code>URLStreamHandler</code>s has not been tested.</li>
121     *
122     * </ul>
123     * </p>
124     *
125     * @since 1.2
126     *
127     * @author Mark Wielaard (mark@klomp.org)
128     * @author Wu Gansha (gansha.wu@intel.com)
129     */
130    public class URLClassLoader extends SecureClassLoader
131    {
132      // Class Variables
133    
134      /**
135       * A cache to store mappings between handler factory and its
136       * private protocol handler cache (also a HashMap), so we can avoid
137       * creating handlers each time the same protocol comes.
138       */
139      private static URLStreamHandlerCache factoryCache
140        = new URLStreamHandlerCache();
141    
142      /**
143       * The prefix for URL loaders.
144       */
145      private static final String URL_LOADER_PREFIX = "gnu.java.net.loader.Load_";
146    
147      // Instance variables
148    
149      /** Locations to load classes from */
150      private final Vector<URL> urls = new Vector<URL>();
151    
152      /**
153       * Store pre-parsed information for each url into this vector: each
154       * element is a URL loader.  A jar file has its own class-path
155       * attribute which adds to the URLs that will be searched, but this
156       * does not add to the list of urls.
157       */
158      private final Vector<URLLoader> urlinfos = new Vector<URLLoader>();
159    
160      /** Factory used to get the protocol handlers of the URLs */
161      private final URLStreamHandlerFactory factory;
162    
163      /**
164       * The security context when created from <code>newInstance()</code>
165       * or null when created through a normal constructor or when no
166       * <code>SecurityManager</code> was installed.
167       */
168      private final AccessControlContext securityContext;
169    
170      // Helper classes
171    
172      /**
173       * Creates a URLClassLoader that gets classes from the supplied URLs.
174       * To determine if this classloader may be created the constructor of
175       * the super class (<code>SecureClassLoader</code>) is called first, which
176       * can throw a SecurityException. Then the supplied URLs are added
177       * in the order given to the URLClassLoader which uses these URLs to
178       * load classes and resources (after using the default parent ClassLoader).
179       *
180       * @param urls Locations that should be searched by this ClassLoader when
181       * resolving Classes or Resources.
182       * @exception SecurityException if the SecurityManager disallows the
183       * creation of a ClassLoader.
184       * @see SecureClassLoader
185       */
186      public URLClassLoader(URL[] urls) throws SecurityException
187      {
188        super();
189        this.factory = null;
190        this.securityContext = null;
191        addURLs(urls);
192      }
193    
194      /**
195       * Creates a <code>URLClassLoader</code> that gets classes from the supplied
196       * <code>URL</code>s.
197       * To determine if this classloader may be created the constructor of
198       * the super class (<code>SecureClassLoader</code>) is called first, which
199       * can throw a SecurityException. Then the supplied URLs are added
200       * in the order given to the URLClassLoader which uses these URLs to
201       * load classes and resources (after using the supplied parent ClassLoader).
202       * @param urls Locations that should be searched by this ClassLoader when
203       * resolving Classes or Resources.
204       * @param parent The parent class loader used before trying this class
205       * loader.
206       * @exception SecurityException if the SecurityManager disallows the
207       * creation of a ClassLoader.
208       * @exception SecurityException
209       * @see SecureClassLoader
210       */
211      public URLClassLoader(URL[] urls, ClassLoader parent)
212        throws SecurityException
213      {
214        super(parent);
215        this.factory = null;
216        this.securityContext = null;
217        addURLs(urls);
218      }
219    
220      // Package-private to avoid a trampoline constructor.
221      /**
222       * Package-private constructor used by the static
223       * <code>newInstance(URL[])</code> method.  Creates an
224       * <code>URLClassLoader</code> with the given parent but without any
225       * <code>URL</code>s yet. This is used to bypass the normal security
226       * check for creating classloaders, but remembers the security
227       * context which will be used when defining classes.  The
228       * <code>URL</code>s to load from must be added by the
229       * <code>newInstance()</code> method in the security context of the
230       * caller.
231       *
232       * @param securityContext the security context of the unprivileged code.
233       */
234      URLClassLoader(ClassLoader parent, AccessControlContext securityContext)
235      {
236        super(parent);
237        this.factory = null;
238        this.securityContext = securityContext;
239      }
240    
241      /**
242       * Creates a URLClassLoader that gets classes from the supplied URLs.
243       * To determine if this classloader may be created the constructor of
244       * the super class (<CODE>SecureClassLoader</CODE>) is called first, which
245       * can throw a SecurityException. Then the supplied URLs are added
246       * in the order given to the URLClassLoader which uses these URLs to
247       * load classes and resources (after using the supplied parent ClassLoader).
248       * It will use the supplied <CODE>URLStreamHandlerFactory</CODE> to get the
249       * protocol handlers of the supplied URLs.
250       * @param urls Locations that should be searched by this ClassLoader when
251       * resolving Classes or Resources.
252       * @param parent The parent class loader used before trying this class
253       * loader.
254       * @param factory Used to get the protocol handler for the URLs.
255       * @exception SecurityException if the SecurityManager disallows the
256       * creation of a ClassLoader.
257       * @exception SecurityException
258       * @see SecureClassLoader
259       */
260      public URLClassLoader(URL[] urls, ClassLoader parent,
261                            URLStreamHandlerFactory factory)
262        throws SecurityException
263      {
264        super(parent);
265        this.securityContext = null;
266        this.factory = factory;
267        // If this factory is not yet in factoryCache, add it.
268        factoryCache.add(factory);
269        addURLs(urls);
270      }
271    
272      // Methods
273    
274      /**
275       * Adds a new location to the end of the internal URL store.
276       * @param newUrl the location to add
277       */
278      protected void addURL(URL newUrl)
279      {
280        urls.add(newUrl);
281        addURLImpl(newUrl);
282      }
283    
284      private void addURLImpl(URL newUrl)
285      {
286        synchronized (this)
287          {
288            if (newUrl == null)
289              return; // Silently ignore...
290    
291            // Reset the toString() value.
292            thisString = null;
293    
294            // Create a loader for this URL.
295            URLLoader loader = null;
296            String file = newUrl.getFile();
297            String protocol = newUrl.getProtocol();
298    
299            // If we have a file: URL, we want to make it absolute
300            // here, before we decide whether it is really a jar.
301            URL absoluteURL;
302            if ("file".equals (protocol))
303              {
304                File dir = new File(file);
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<URLLoader> 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        CPStringBuilder xform = new CPStringBuilder(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 = AccessController.doPrivileged
606                  (new PrivilegedAction<Class>()
607                    {
608                      public Class 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            throw new ClassNotFoundException(className + " not found in " + this, ioe);
629          }
630      }
631    
632      // Cached String representation of this URLClassLoader
633      private String thisString;
634    
635      /**
636       * Returns a String representation of this URLClassLoader giving the
637       * actual Class name, the URLs that are searched and the parent
638       * ClassLoader.
639       */
640      public String toString()
641      {
642        synchronized (this)
643          {
644            if (thisString == null)
645              {
646                CPStringBuilder sb = new CPStringBuilder();
647                sb.append(this.getClass().getName());
648                sb.append("{urls=[" );
649                URL[] thisURLs = getURLs();
650                for (int i = 0; i < thisURLs.length; i++)
651                  {
652                    sb.append(thisURLs[i]);
653                    if (i < thisURLs.length - 1)
654                      sb.append(',');
655                  }
656                sb.append(']');
657                sb.append(", parent=");
658                sb.append(getParent());
659                sb.append('}');
660                thisString = sb.toString();
661              }
662            return thisString;
663          }
664      }
665    
666      /**
667       * Finds the first occurrence of a resource that can be found. The locations
668       * are searched in the order they were added to the URLClassLoader.
669       *
670       * @param resourceName the resource name to look for
671       * @return the URLResource for the resource if found, null otherwise
672       */
673      private Resource findURLResource(String resourceName)
674      {
675        int max = urlinfos.size();
676        for (int i = 0; i < max; i++)
677          {
678            URLLoader loader = (URLLoader) urlinfos.elementAt(i);
679            if (loader == null)
680              continue;
681    
682            Resource resource = loader.getResource(resourceName);
683            if (resource != null)
684              return resource;
685          }
686        return null;
687      }
688    
689      /**
690       * Finds the first occurrence of a resource that can be found.
691       *
692       * @param resourceName the resource name to look for
693       * @return the URL if found, null otherwise
694       */
695      public URL findResource(String resourceName)
696      {
697        Resource resource = findURLResource(resourceName);
698        if (resource != null)
699          return resource.getURL();
700    
701        // Resource not found
702        return null;
703      }
704    
705      /**
706       * Finds all the resources with a particular name from all the locations.
707       *
708       * @param resourceName the name of the resource to lookup
709       * @return a (possible empty) enumeration of URLs where the resource can be
710       * found
711       * @exception IOException when an error occurs accessing one of the
712       * locations
713       */
714      public Enumeration<URL> findResources(String resourceName)
715        throws IOException
716      {
717        Vector<URL> resources = new Vector<URL>();
718        int max = urlinfos.size();
719        for (int i = 0; i < max; i++)
720          {
721            URLLoader loader = (URLLoader) urlinfos.elementAt(i);
722            Resource resource = loader.getResource(resourceName);
723            if (resource != null)
724              resources.add(resource.getURL());
725          }
726        return resources.elements();
727      }
728    
729      /**
730       * Returns the permissions needed to access a particular code
731       * source.  These permissions includes those returned by
732       * <code>SecureClassLoader.getPermissions()</code> and the actual
733       * permissions to access the objects referenced by the URL of the
734       * code source.  The extra permissions added depend on the protocol
735       * and file portion of the URL in the code source. If the URL has
736       * the "file" protocol ends with a '/' character then it must be a
737       * directory and a file Permission to read everything in that
738       * directory and all subdirectories is added. If the URL had the
739       * "file" protocol and doesn't end with a '/' character then it must
740       * be a normal file and a file permission to read that file is
741       * added. If the <code>URL</code> has any other protocol then a
742       * socket permission to connect and accept connections from the host
743       * portion of the URL is added.
744       *
745       * @param source The codesource that needs the permissions to be accessed
746       * @return the collection of permissions needed to access the code resource
747       * @see java.security.SecureClassLoader#getPermissions(CodeSource)
748       */
749      protected PermissionCollection getPermissions(CodeSource source)
750      {
751        // XXX - This implementation does exactly as the Javadoc describes.
752        // But maybe we should/could use URLConnection.getPermissions()?
753        // First get the permissions that would normally be granted
754        PermissionCollection permissions = super.getPermissions(source);
755    
756        // Now add any extra permissions depending on the URL location.
757        URL url = source.getLocation();
758        String protocol = url.getProtocol();
759        if (protocol.equals("file"))
760          {
761            String file = url.getFile();
762    
763            // If the file end in / it must be an directory.
764            if (file.endsWith("/") || file.endsWith(File.separator))
765              {
766                // Grant permission to read everything in that directory and
767                // all subdirectories.
768                permissions.add(new FilePermission(file + "-", "read"));
769              }
770            else
771              {
772                // It is a 'normal' file.
773                // Grant permission to access that file.
774                permissions.add(new FilePermission(file, "read"));
775              }
776          }
777        else
778          {
779            // Grant permission to connect to and accept connections from host
780            String host = url.getHost();
781            if (host != null)
782              permissions.add(new SocketPermission(host, "connect,accept"));
783          }
784    
785        return permissions;
786      }
787    
788      /**
789       * Returns all the locations that this class loader currently uses the
790       * resolve classes and resource. This includes both the initially supplied
791       * URLs as any URLs added later by the loader.
792       * @return All the currently used URLs
793       */
794      public URL[] getURLs()
795      {
796        return (URL[]) urls.toArray(new URL[urls.size()]);
797      }
798    
799      /**
800       * Creates a new instance of a <code>URLClassLoader</code> that gets
801       * classes from the supplied <code>URL</code>s. This class loader
802       * will have as parent the standard system class loader.
803       *
804       * @param urls the initial URLs used to resolve classes and
805       * resources
806       *
807       * @return the class loader
808       *
809       * @exception SecurityException when the calling code does not have
810       * permission to access the given <code>URL</code>s
811       */
812      public static URLClassLoader newInstance(URL[] urls)
813        throws SecurityException
814      {
815        return newInstance(urls, null);
816      }
817    
818      /**
819       * Creates a new instance of a <code>URLClassLoader</code> that gets
820       * classes from the supplied <code>URL</code>s and with the supplied
821       * loader as parent class loader.
822       *
823       * @param urls the initial URLs used to resolve classes and
824       * resources
825       * @param parent the parent class loader
826       *
827       * @return the class loader
828       *
829       * @exception SecurityException when the calling code does not have
830       * permission to access the given <code>URL</code>s
831       */
832      public static URLClassLoader newInstance(URL[] urls, final ClassLoader parent)
833        throws SecurityException
834      {
835        SecurityManager sm = System.getSecurityManager();
836        if (sm == null)
837          return new URLClassLoader(urls, parent);
838        else
839          {
840            final Object securityContext = sm.getSecurityContext();
841    
842            // XXX - What to do with anything else then an AccessControlContext?
843            if (! (securityContext instanceof AccessControlContext))
844              throw new SecurityException("securityContext must be AccessControlContext: "
845                                          + securityContext);
846    
847            URLClassLoader loader =
848              AccessController.doPrivileged(new PrivilegedAction<URLClassLoader>()
849                  {
850                    public URLClassLoader run()
851                    {
852                      return new URLClassLoader(parent,
853                                                (AccessControlContext) securityContext);
854                    }
855                  });
856            loader.addURLs(urls);
857            return loader;
858          }
859      }
860    }