mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			861 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			861 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			Java
		
	
	
	
/* URLClassLoader.java --  ClassLoader that loads classes from one or more URLs
 | 
						|
   Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
 | 
						|
   Free Software Foundation, Inc.
 | 
						|
 | 
						|
This file is part of GNU Classpath.
 | 
						|
 | 
						|
GNU Classpath is free software; you can redistribute it and/or modify
 | 
						|
it under the terms of the GNU General Public License as published by
 | 
						|
the Free Software Foundation; either version 2, or (at your option)
 | 
						|
any later version.
 | 
						|
 | 
						|
GNU Classpath is distributed in the hope that it will be useful, but
 | 
						|
WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
General Public License for more details.
 | 
						|
 | 
						|
You should have received a copy of the GNU General Public License
 | 
						|
along with GNU Classpath; see the file COPYING.  If not, write to the
 | 
						|
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 | 
						|
02110-1301 USA.
 | 
						|
 | 
						|
Linking this library statically or dynamically with other modules is
 | 
						|
making a combined work based on this library.  Thus, the terms and
 | 
						|
conditions of the GNU General Public License cover the whole
 | 
						|
combination.
 | 
						|
 | 
						|
As a special exception, the copyright holders of this library give you
 | 
						|
permission to link this library with independent modules to produce an
 | 
						|
executable, regardless of the license terms of these independent
 | 
						|
modules, and to copy and distribute the resulting executable under
 | 
						|
terms of your choice, provided that you also meet, for each linked
 | 
						|
independent module, the terms and conditions of the license of that
 | 
						|
module.  An independent module is a module which is not derived from
 | 
						|
or based on this library.  If you modify this library, you may extend
 | 
						|
this exception to your version of the library, but you are not
 | 
						|
obligated to do so.  If you do not wish to do so, delete this
 | 
						|
exception statement from your version. */
 | 
						|
 | 
						|
 | 
						|
package java.net;
 | 
						|
 | 
						|
import gnu.java.lang.CPStringBuilder;
 | 
						|
 | 
						|
import gnu.java.net.loader.FileURLLoader;
 | 
						|
import gnu.java.net.loader.JarURLLoader;
 | 
						|
import gnu.java.net.loader.RemoteURLLoader;
 | 
						|
import gnu.java.net.loader.Resource;
 | 
						|
import gnu.java.net.loader.URLLoader;
 | 
						|
import gnu.java.net.loader.URLStreamHandlerCache;
 | 
						|
 | 
						|
import java.io.ByteArrayOutputStream;
 | 
						|
import java.io.EOFException;
 | 
						|
import java.io.File;
 | 
						|
import java.io.FilePermission;
 | 
						|
import java.io.IOException;
 | 
						|
import java.io.InputStream;
 | 
						|
import java.lang.reflect.Constructor;
 | 
						|
import java.lang.reflect.InvocationTargetException;
 | 
						|
import java.security.AccessControlContext;
 | 
						|
import java.security.AccessController;
 | 
						|
import java.security.CodeSource;
 | 
						|
import java.security.PermissionCollection;
 | 
						|
import java.security.PrivilegedAction;
 | 
						|
import java.security.SecureClassLoader;
 | 
						|
import java.security.cert.Certificate;
 | 
						|
import java.util.ArrayList;
 | 
						|
import java.util.Enumeration;
 | 
						|
import java.util.Vector;
 | 
						|
import java.util.jar.Attributes;
 | 
						|
import java.util.jar.Manifest;
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
 * A secure class loader that can load classes and resources from
 | 
						|
 * multiple locations.  Given an array of <code>URL</code>s this class
 | 
						|
 * loader will retrieve classes and resources by fetching them from
 | 
						|
 * possible remote locations.  Each <code>URL</code> is searched in
 | 
						|
 * order in which it was added.  If the file portion of the
 | 
						|
 * <code>URL</code> ends with a '/' character then it is interpreted
 | 
						|
 * as a base directory, otherwise it is interpreted as a jar file from
 | 
						|
 * which the classes/resources are resolved.
 | 
						|
 *
 | 
						|
 * <p>New instances can be created by two static
 | 
						|
 * <code>newInstance()</code> methods or by three public
 | 
						|
 * contructors. Both ways give the option to supply an initial array
 | 
						|
 * of <code>URL</code>s and (optionally) a parent classloader (that is
 | 
						|
 * different from the standard system class loader).</p>
 | 
						|
 *
 | 
						|
 * <p>Normally creating a <code>URLClassLoader</code> throws a
 | 
						|
 * <code>SecurityException</code> if a <code>SecurityManager</code> is
 | 
						|
 * installed and the <code>checkCreateClassLoader()</code> method does
 | 
						|
 * not return true.  But the <code>newInstance()</code> methods may be
 | 
						|
 * used by any code as long as it has permission to acces the given
 | 
						|
 * <code>URL</code>s.  <code>URLClassLoaders</code> created by the
 | 
						|
 * <code>newInstance()</code> methods also explicitly call the
 | 
						|
 * <code>checkPackageAccess()</code> method of
 | 
						|
 * <code>SecurityManager</code> if one is installed before trying to
 | 
						|
 * load a class.  Note that only subclasses of
 | 
						|
 * <code>URLClassLoader</code> can add new URLs after the
 | 
						|
 * URLClassLoader had been created. But it is always possible to get
 | 
						|
 * an array of all URLs that the class loader uses to resolve classes
 | 
						|
 * and resources by way of the <code>getURLs()</code> method.</p>
 | 
						|
 *
 | 
						|
 * <p>Open issues:
 | 
						|
 * <ul>
 | 
						|
 *
 | 
						|
 * <li>Should the URLClassLoader actually add the locations found in
 | 
						|
 * the manifest or is this the responsibility of some other
 | 
						|
 * loader/(sub)class?  (see <a
 | 
						|
 * href="http://java.sun.com/products/jdk/1.4/docs/guide/extensions/spec.html">
 | 
						|
 * Extension Mechanism Architecture - Bundles Extensions</a>)</li>
 | 
						|
 *
 | 
						|
 * <li>How does <code>definePackage()</code> and sealing work
 | 
						|
 * precisely?</li>
 | 
						|
 *
 | 
						|
 * <li>We save and use the security context (when a created by
 | 
						|
 * <code>newInstance()</code> but do we have to use it in more
 | 
						|
 * places?</li>
 | 
						|
 *
 | 
						|
 * <li>The use of <code>URLStreamHandler</code>s has not been tested.</li>
 | 
						|
 *
 | 
						|
 * </ul>
 | 
						|
 * </p>
 | 
						|
 *
 | 
						|
 * @since 1.2
 | 
						|
 *
 | 
						|
 * @author Mark Wielaard (mark@klomp.org)
 | 
						|
 * @author Wu Gansha (gansha.wu@intel.com)
 | 
						|
 */
 | 
						|
public class URLClassLoader extends SecureClassLoader
 | 
						|
{
 | 
						|
  // Class Variables
 | 
						|
 | 
						|
  /**
 | 
						|
   * A cache to store mappings between handler factory and its
 | 
						|
   * private protocol handler cache (also a HashMap), so we can avoid
 | 
						|
   * creating handlers each time the same protocol comes.
 | 
						|
   */
 | 
						|
  private static URLStreamHandlerCache factoryCache
 | 
						|
    = new URLStreamHandlerCache();
 | 
						|
 | 
						|
  /**
 | 
						|
   * The prefix for URL loaders.
 | 
						|
   */
 | 
						|
  private static final String URL_LOADER_PREFIX = "gnu.java.net.loader.Load_";
 | 
						|
 | 
						|
  // Instance variables
 | 
						|
 | 
						|
  /** Locations to load classes from */
 | 
						|
  private final Vector<URL> urls = new Vector<URL>();
 | 
						|
 | 
						|
  /**
 | 
						|
   * Store pre-parsed information for each url into this vector: each
 | 
						|
   * element is a URL loader.  A jar file has its own class-path
 | 
						|
   * attribute which adds to the URLs that will be searched, but this
 | 
						|
   * does not add to the list of urls.
 | 
						|
   */
 | 
						|
  private final Vector<URLLoader> urlinfos = new Vector<URLLoader>();
 | 
						|
 | 
						|
  /** Factory used to get the protocol handlers of the URLs */
 | 
						|
  private final URLStreamHandlerFactory factory;
 | 
						|
 | 
						|
  /**
 | 
						|
   * The security context when created from <code>newInstance()</code>
 | 
						|
   * or null when created through a normal constructor or when no
 | 
						|
   * <code>SecurityManager</code> was installed.
 | 
						|
   */
 | 
						|
  private final AccessControlContext securityContext;
 | 
						|
 | 
						|
  // Helper classes
 | 
						|
 | 
						|
  /**
 | 
						|
   * Creates a URLClassLoader that gets classes from the supplied URLs.
 | 
						|
   * To determine if this classloader may be created the constructor of
 | 
						|
   * the super class (<code>SecureClassLoader</code>) is called first, which
 | 
						|
   * can throw a SecurityException. Then the supplied URLs are added
 | 
						|
   * in the order given to the URLClassLoader which uses these URLs to
 | 
						|
   * load classes and resources (after using the default parent ClassLoader).
 | 
						|
   *
 | 
						|
   * @param urls Locations that should be searched by this ClassLoader when
 | 
						|
   * resolving Classes or Resources.
 | 
						|
   * @exception SecurityException if the SecurityManager disallows the
 | 
						|
   * creation of a ClassLoader.
 | 
						|
   * @see SecureClassLoader
 | 
						|
   */
 | 
						|
  public URLClassLoader(URL[] urls) throws SecurityException
 | 
						|
  {
 | 
						|
    super();
 | 
						|
    this.factory = null;
 | 
						|
    this.securityContext = null;
 | 
						|
    addURLs(urls);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Creates a <code>URLClassLoader</code> that gets classes from the supplied
 | 
						|
   * <code>URL</code>s.
 | 
						|
   * To determine if this classloader may be created the constructor of
 | 
						|
   * the super class (<code>SecureClassLoader</code>) is called first, which
 | 
						|
   * can throw a SecurityException. Then the supplied URLs are added
 | 
						|
   * in the order given to the URLClassLoader which uses these URLs to
 | 
						|
   * load classes and resources (after using the supplied parent ClassLoader).
 | 
						|
   * @param urls Locations that should be searched by this ClassLoader when
 | 
						|
   * resolving Classes or Resources.
 | 
						|
   * @param parent The parent class loader used before trying this class
 | 
						|
   * loader.
 | 
						|
   * @exception SecurityException if the SecurityManager disallows the
 | 
						|
   * creation of a ClassLoader.
 | 
						|
   * @exception SecurityException
 | 
						|
   * @see SecureClassLoader
 | 
						|
   */
 | 
						|
  public URLClassLoader(URL[] urls, ClassLoader parent)
 | 
						|
    throws SecurityException
 | 
						|
  {
 | 
						|
    super(parent);
 | 
						|
    this.factory = null;
 | 
						|
    this.securityContext = null;
 | 
						|
    addURLs(urls);
 | 
						|
  }
 | 
						|
 | 
						|
  // Package-private to avoid a trampoline constructor.
 | 
						|
  /**
 | 
						|
   * Package-private constructor used by the static
 | 
						|
   * <code>newInstance(URL[])</code> method.  Creates an
 | 
						|
   * <code>URLClassLoader</code> with the given parent but without any
 | 
						|
   * <code>URL</code>s yet. This is used to bypass the normal security
 | 
						|
   * check for creating classloaders, but remembers the security
 | 
						|
   * context which will be used when defining classes.  The
 | 
						|
   * <code>URL</code>s to load from must be added by the
 | 
						|
   * <code>newInstance()</code> method in the security context of the
 | 
						|
   * caller.
 | 
						|
   *
 | 
						|
   * @param securityContext the security context of the unprivileged code.
 | 
						|
   */
 | 
						|
  URLClassLoader(ClassLoader parent, AccessControlContext securityContext)
 | 
						|
  {
 | 
						|
    super(parent);
 | 
						|
    this.factory = null;
 | 
						|
    this.securityContext = securityContext;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Creates a URLClassLoader that gets classes from the supplied URLs.
 | 
						|
   * To determine if this classloader may be created the constructor of
 | 
						|
   * the super class (<CODE>SecureClassLoader</CODE>) is called first, which
 | 
						|
   * can throw a SecurityException. Then the supplied URLs are added
 | 
						|
   * in the order given to the URLClassLoader which uses these URLs to
 | 
						|
   * load classes and resources (after using the supplied parent ClassLoader).
 | 
						|
   * It will use the supplied <CODE>URLStreamHandlerFactory</CODE> to get the
 | 
						|
   * protocol handlers of the supplied URLs.
 | 
						|
   * @param urls Locations that should be searched by this ClassLoader when
 | 
						|
   * resolving Classes or Resources.
 | 
						|
   * @param parent The parent class loader used before trying this class
 | 
						|
   * loader.
 | 
						|
   * @param factory Used to get the protocol handler for the URLs.
 | 
						|
   * @exception SecurityException if the SecurityManager disallows the
 | 
						|
   * creation of a ClassLoader.
 | 
						|
   * @exception SecurityException
 | 
						|
   * @see SecureClassLoader
 | 
						|
   */
 | 
						|
  public URLClassLoader(URL[] urls, ClassLoader parent,
 | 
						|
                        URLStreamHandlerFactory factory)
 | 
						|
    throws SecurityException
 | 
						|
  {
 | 
						|
    super(parent);
 | 
						|
    this.securityContext = null;
 | 
						|
    this.factory = factory;
 | 
						|
    // If this factory is not yet in factoryCache, add it.
 | 
						|
    factoryCache.add(factory);
 | 
						|
    addURLs(urls);
 | 
						|
  }
 | 
						|
 | 
						|
  // Methods
 | 
						|
 | 
						|
  /**
 | 
						|
   * Adds a new location to the end of the internal URL store.
 | 
						|
   * @param newUrl the location to add
 | 
						|
   */
 | 
						|
  protected void addURL(URL newUrl)
 | 
						|
  {
 | 
						|
    urls.add(newUrl);
 | 
						|
    addURLImpl(newUrl);
 | 
						|
  }
 | 
						|
 | 
						|
  private void addURLImpl(URL newUrl)
 | 
						|
  {
 | 
						|
    synchronized (this)
 | 
						|
      {
 | 
						|
        if (newUrl == null)
 | 
						|
          return; // Silently ignore...
 | 
						|
 | 
						|
        // Reset the toString() value.
 | 
						|
        thisString = null;
 | 
						|
 | 
						|
        // Create a loader for this URL.
 | 
						|
        URLLoader loader = null;
 | 
						|
        String file = newUrl.getFile();
 | 
						|
        String protocol = newUrl.getProtocol();
 | 
						|
 | 
						|
        // If we have a file: URL, we want to make it absolute
 | 
						|
        // here, before we decide whether it is really a jar.
 | 
						|
        URL absoluteURL;
 | 
						|
        if ("file".equals (protocol))
 | 
						|
          {
 | 
						|
            File dir = new File(file);
 | 
						|
            try
 | 
						|
              {
 | 
						|
                absoluteURL = dir.getCanonicalFile().toURL();
 | 
						|
              }
 | 
						|
            catch (IOException ignore)
 | 
						|
              {
 | 
						|
                try
 | 
						|
                  {
 | 
						|
                    absoluteURL = dir.getAbsoluteFile().toURL();
 | 
						|
                  }
 | 
						|
                catch (MalformedURLException _)
 | 
						|
                  {
 | 
						|
                    // This really should not happen.
 | 
						|
                    absoluteURL = newUrl;
 | 
						|
                  }
 | 
						|
              }
 | 
						|
          }
 | 
						|
        else
 | 
						|
          {
 | 
						|
            // This doesn't hurt, and it simplifies the logic a
 | 
						|
            // little.
 | 
						|
            absoluteURL = newUrl;
 | 
						|
          }
 | 
						|
 | 
						|
        // First see if we can find a handler with the correct name.
 | 
						|
        try
 | 
						|
          {
 | 
						|
            Class<?> handler = Class.forName(URL_LOADER_PREFIX + protocol);
 | 
						|
            Class<?>[] argTypes = new Class<?>[] { URLClassLoader.class,
 | 
						|
                                                   URLStreamHandlerCache.class,
 | 
						|
                                                   URLStreamHandlerFactory.class,
 | 
						|
                                                   URL.class,
 | 
						|
                                                   URL.class };
 | 
						|
            Constructor k = handler.getDeclaredConstructor(argTypes);
 | 
						|
            loader
 | 
						|
              = (URLLoader) k.newInstance(new Object[] { this,
 | 
						|
                                                         factoryCache,
 | 
						|
                                                         factory,
 | 
						|
                                                         newUrl,
 | 
						|
                                                         absoluteURL });
 | 
						|
          }
 | 
						|
        catch (ClassNotFoundException ignore)
 | 
						|
          {
 | 
						|
            // Fall through.
 | 
						|
          }
 | 
						|
        catch (NoSuchMethodException nsme)
 | 
						|
          {
 | 
						|
            // Programming error in the class library.
 | 
						|
            InternalError vme
 | 
						|
              = new InternalError("couldn't find URLLoader constructor");
 | 
						|
            vme.initCause(nsme);
 | 
						|
            throw vme;
 | 
						|
          }
 | 
						|
        catch (InstantiationException inste)
 | 
						|
          {
 | 
						|
            // Programming error in the class library.
 | 
						|
            InternalError vme
 | 
						|
              = new InternalError("couldn't instantiate URLLoader");
 | 
						|
            vme.initCause(inste);
 | 
						|
            throw vme;
 | 
						|
          }
 | 
						|
        catch (InvocationTargetException ite)
 | 
						|
          {
 | 
						|
            // Programming error in the class library.
 | 
						|
            InternalError vme
 | 
						|
              = new InternalError("error instantiating URLLoader");
 | 
						|
            vme.initCause(ite);
 | 
						|
            throw vme;
 | 
						|
          }
 | 
						|
        catch (IllegalAccessException illae)
 | 
						|
          {
 | 
						|
            // Programming error in the class library.
 | 
						|
            InternalError vme
 | 
						|
              = new InternalError("invalid access to URLLoader");
 | 
						|
            vme.initCause(illae);
 | 
						|
            throw vme;
 | 
						|
          }
 | 
						|
 | 
						|
        if (loader == null)
 | 
						|
          {
 | 
						|
            // If it is not a directory, use the jar loader.
 | 
						|
            if (! (file.endsWith("/") || file.endsWith(File.separator)))
 | 
						|
              loader = new JarURLLoader(this, factoryCache, factory,
 | 
						|
                                        newUrl, absoluteURL);
 | 
						|
            else if ("file".equals(protocol))
 | 
						|
              loader = new FileURLLoader(this, factoryCache, factory,
 | 
						|
                                         newUrl, absoluteURL);
 | 
						|
            else
 | 
						|
              loader = new RemoteURLLoader(this, factoryCache, factory,
 | 
						|
                                           newUrl);
 | 
						|
          }
 | 
						|
 | 
						|
        urlinfos.add(loader);
 | 
						|
        ArrayList<URLLoader> extra = loader.getClassPath();
 | 
						|
        if (extra != null)
 | 
						|
          urlinfos.addAll(extra);
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Adds an array of new locations to the end of the internal URL
 | 
						|
   * store.  Called from the the constructors. Should not call to the
 | 
						|
   * protected addURL() method since that can be overridden and
 | 
						|
   * subclasses are not yet in a good state at this point.
 | 
						|
   * jboss 4.0.3 for example depends on this.
 | 
						|
   *
 | 
						|
   * @param newUrls the locations to add
 | 
						|
   */
 | 
						|
  private void addURLs(URL[] newUrls)
 | 
						|
  {
 | 
						|
    for (int i = 0; i < newUrls.length; i++)
 | 
						|
      {
 | 
						|
        urls.add(newUrls[i]);
 | 
						|
        addURLImpl(newUrls[i]);
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Look in both Attributes for a given value.  The first Attributes
 | 
						|
   * object, if not null, has precedence.
 | 
						|
   */
 | 
						|
  private String getAttributeValue(Attributes.Name name, Attributes first,
 | 
						|
                                   Attributes second)
 | 
						|
  {
 | 
						|
    String result = null;
 | 
						|
    if (first != null)
 | 
						|
      result = first.getValue(name);
 | 
						|
    if (result == null)
 | 
						|
      result = second.getValue(name);
 | 
						|
    return result;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Defines a Package based on the given name and the supplied manifest
 | 
						|
   * information. The manifest indicates the title, version and
 | 
						|
   * vendor information of the specification and implementation and whether the
 | 
						|
   * package is sealed. If the Manifest indicates that the package is sealed
 | 
						|
   * then the Package will be sealed with respect to the supplied URL.
 | 
						|
   *
 | 
						|
   * @param name The name of the package
 | 
						|
   * @param manifest The manifest describing the specification,
 | 
						|
   * implementation and sealing details of the package
 | 
						|
   * @param url the code source url to seal the package
 | 
						|
   * @return the defined Package
 | 
						|
   * @throws IllegalArgumentException If this package name already exists
 | 
						|
   * in this class loader
 | 
						|
   */
 | 
						|
  protected Package definePackage(String name, Manifest manifest, URL url)
 | 
						|
    throws IllegalArgumentException
 | 
						|
  {
 | 
						|
    // Compute the name of the package as it may appear in the
 | 
						|
    // Manifest.
 | 
						|
    CPStringBuilder xform = new CPStringBuilder(name);
 | 
						|
    for (int i = xform.length () - 1; i >= 0; --i)
 | 
						|
      if (xform.charAt(i) == '.')
 | 
						|
        xform.setCharAt(i, '/');
 | 
						|
    xform.append('/');
 | 
						|
    String xformName = xform.toString();
 | 
						|
 | 
						|
    Attributes entryAttr = manifest.getAttributes(xformName);
 | 
						|
    Attributes attr = manifest.getMainAttributes();
 | 
						|
 | 
						|
    String specTitle
 | 
						|
      = getAttributeValue(Attributes.Name.SPECIFICATION_TITLE,
 | 
						|
                          entryAttr, attr);
 | 
						|
    String specVersion
 | 
						|
      = getAttributeValue(Attributes.Name.SPECIFICATION_VERSION,
 | 
						|
                          entryAttr, attr);
 | 
						|
    String specVendor
 | 
						|
      = getAttributeValue(Attributes.Name.SPECIFICATION_VENDOR,
 | 
						|
                          entryAttr, attr);
 | 
						|
    String implTitle
 | 
						|
      = getAttributeValue(Attributes.Name.IMPLEMENTATION_TITLE,
 | 
						|
                          entryAttr, attr);
 | 
						|
    String implVersion
 | 
						|
      = getAttributeValue(Attributes.Name.IMPLEMENTATION_VERSION,
 | 
						|
                          entryAttr, attr);
 | 
						|
    String implVendor
 | 
						|
      = getAttributeValue(Attributes.Name.IMPLEMENTATION_VENDOR,
 | 
						|
                          entryAttr, attr);
 | 
						|
 | 
						|
    // Look if the Manifest indicates that this package is sealed
 | 
						|
    // XXX - most likely not completely correct!
 | 
						|
    // Shouldn't we also check the sealed attribute of the complete jar?
 | 
						|
    // http://java.sun.com/products/jdk/1.4/docs/guide/extensions/spec.html#bundled
 | 
						|
    // But how do we get that jar manifest here?
 | 
						|
    String sealed = attr.getValue(Attributes.Name.SEALED);
 | 
						|
    if ("false".equals(sealed))
 | 
						|
      // make sure that the URL is null so the package is not sealed
 | 
						|
      url = null;
 | 
						|
 | 
						|
    return definePackage(name,
 | 
						|
                         specTitle, specVendor, specVersion,
 | 
						|
                         implTitle, implVendor, implVersion,
 | 
						|
                         url);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Finds (the first) class by name from one of the locations. The locations
 | 
						|
   * are searched in the order they were added to the URLClassLoader.
 | 
						|
   *
 | 
						|
   * @param className the classname to find
 | 
						|
   * @exception ClassNotFoundException when the class could not be found or
 | 
						|
   * loaded
 | 
						|
   * @return a Class object representing the found class
 | 
						|
   */
 | 
						|
  protected Class<?> findClass(final String className)
 | 
						|
    throws ClassNotFoundException
 | 
						|
  {
 | 
						|
    // Just try to find the resource by the (almost) same name
 | 
						|
    String resourceName = className.replace('.', '/') + ".class";
 | 
						|
    int max = urlinfos.size();
 | 
						|
    Resource resource = null;
 | 
						|
    for (int i = 0; i < max && resource == null; i++)
 | 
						|
      {
 | 
						|
        URLLoader loader = (URLLoader)urlinfos.elementAt(i);
 | 
						|
        if (loader == null)
 | 
						|
          continue;
 | 
						|
 | 
						|
        Class k = loader.getClass(className);
 | 
						|
        if (k != null)
 | 
						|
          return k;
 | 
						|
 | 
						|
        resource = loader.getResource(resourceName);
 | 
						|
      }
 | 
						|
    if (resource == null)
 | 
						|
      throw new ClassNotFoundException(className + " not found in " + this);
 | 
						|
 | 
						|
    // Try to read the class data, create the CodeSource, Package and
 | 
						|
    // construct the class (and watch out for those nasty IOExceptions)
 | 
						|
    try
 | 
						|
      {
 | 
						|
        byte[] data;
 | 
						|
        InputStream in = resource.getInputStream();
 | 
						|
        try
 | 
						|
          {
 | 
						|
            int length = resource.getLength();
 | 
						|
            if (length != -1)
 | 
						|
              {
 | 
						|
                // We know the length of the data.
 | 
						|
                // Just try to read it in all at once
 | 
						|
                data = new byte[length];
 | 
						|
                int pos = 0;
 | 
						|
                while (length - pos > 0)
 | 
						|
                  {
 | 
						|
                    int len = in.read(data, pos, length - pos);
 | 
						|
                    if (len == -1)
 | 
						|
                      throw new EOFException("Not enough data reading from: "
 | 
						|
                                             + in);
 | 
						|
                    pos += len;
 | 
						|
                  }
 | 
						|
              }
 | 
						|
            else
 | 
						|
              {
 | 
						|
                // We don't know the data length.
 | 
						|
                // Have to read it in chunks.
 | 
						|
                ByteArrayOutputStream out = new ByteArrayOutputStream(4096);
 | 
						|
                byte[] b = new byte[4096];
 | 
						|
                int l = 0;
 | 
						|
                while (l != -1)
 | 
						|
                  {
 | 
						|
                    l = in.read(b);
 | 
						|
                    if (l != -1)
 | 
						|
                      out.write(b, 0, l);
 | 
						|
                  }
 | 
						|
                data = out.toByteArray();
 | 
						|
              }
 | 
						|
          }
 | 
						|
        finally
 | 
						|
          {
 | 
						|
            in.close();
 | 
						|
          }
 | 
						|
        final byte[] classData = data;
 | 
						|
 | 
						|
        // Now get the CodeSource
 | 
						|
        final CodeSource source = resource.getCodeSource();
 | 
						|
 | 
						|
        // Find out package name
 | 
						|
        String packageName = null;
 | 
						|
        int lastDot = className.lastIndexOf('.');
 | 
						|
        if (lastDot != -1)
 | 
						|
          packageName = className.substring(0, lastDot);
 | 
						|
 | 
						|
        if (packageName != null && getPackage(packageName) == null)
 | 
						|
          {
 | 
						|
            // define the package
 | 
						|
            Manifest manifest = resource.getLoader().getManifest();
 | 
						|
            if (manifest == null)
 | 
						|
              definePackage(packageName, null, null, null, null, null, null,
 | 
						|
                            null);
 | 
						|
            else
 | 
						|
              definePackage(packageName, manifest,
 | 
						|
                            resource.getLoader().getBaseURL());
 | 
						|
          }
 | 
						|
 | 
						|
        // And finally construct the class!
 | 
						|
        SecurityManager sm = System.getSecurityManager();
 | 
						|
        Class result = null;
 | 
						|
        if (sm != null && securityContext != null)
 | 
						|
          {
 | 
						|
            result = AccessController.doPrivileged
 | 
						|
              (new PrivilegedAction<Class>()
 | 
						|
                {
 | 
						|
                  public Class run()
 | 
						|
                  {
 | 
						|
                    return defineClass(className, classData,
 | 
						|
                                       0, classData.length,
 | 
						|
                                       source);
 | 
						|
                  }
 | 
						|
                }, securityContext);
 | 
						|
          }
 | 
						|
        else
 | 
						|
          result = defineClass(className, classData, 0, classData.length, source);
 | 
						|
 | 
						|
        // Avoid NullPointerExceptions.
 | 
						|
        Certificate[] resourceCertificates = resource.getCertificates();
 | 
						|
        if(resourceCertificates != null)
 | 
						|
          super.setSigners(result, resourceCertificates);
 | 
						|
 | 
						|
        return result;
 | 
						|
      }
 | 
						|
    catch (IOException ioe)
 | 
						|
      {
 | 
						|
        throw new ClassNotFoundException(className + " not found in " + this, ioe);
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  // Cached String representation of this URLClassLoader
 | 
						|
  private String thisString;
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns a String representation of this URLClassLoader giving the
 | 
						|
   * actual Class name, the URLs that are searched and the parent
 | 
						|
   * ClassLoader.
 | 
						|
   */
 | 
						|
  public String toString()
 | 
						|
  {
 | 
						|
    synchronized (this)
 | 
						|
      {
 | 
						|
        if (thisString == null)
 | 
						|
          {
 | 
						|
            CPStringBuilder sb = new CPStringBuilder();
 | 
						|
            sb.append(this.getClass().getName());
 | 
						|
            sb.append("{urls=[" );
 | 
						|
            URL[] thisURLs = getURLs();
 | 
						|
            for (int i = 0; i < thisURLs.length; i++)
 | 
						|
              {
 | 
						|
                sb.append(thisURLs[i]);
 | 
						|
                if (i < thisURLs.length - 1)
 | 
						|
                  sb.append(',');
 | 
						|
              }
 | 
						|
            sb.append(']');
 | 
						|
            sb.append(", parent=");
 | 
						|
            sb.append(getParent());
 | 
						|
            sb.append('}');
 | 
						|
            thisString = sb.toString();
 | 
						|
          }
 | 
						|
        return thisString;
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Finds the first occurrence of a resource that can be found. The locations
 | 
						|
   * are searched in the order they were added to the URLClassLoader.
 | 
						|
   *
 | 
						|
   * @param resourceName the resource name to look for
 | 
						|
   * @return the URLResource for the resource if found, null otherwise
 | 
						|
   */
 | 
						|
  private Resource findURLResource(String resourceName)
 | 
						|
  {
 | 
						|
    int max = urlinfos.size();
 | 
						|
    for (int i = 0; i < max; i++)
 | 
						|
      {
 | 
						|
        URLLoader loader = (URLLoader) urlinfos.elementAt(i);
 | 
						|
        if (loader == null)
 | 
						|
          continue;
 | 
						|
 | 
						|
        Resource resource = loader.getResource(resourceName);
 | 
						|
        if (resource != null)
 | 
						|
          return resource;
 | 
						|
      }
 | 
						|
    return null;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Finds the first occurrence of a resource that can be found.
 | 
						|
   *
 | 
						|
   * @param resourceName the resource name to look for
 | 
						|
   * @return the URL if found, null otherwise
 | 
						|
   */
 | 
						|
  public URL findResource(String resourceName)
 | 
						|
  {
 | 
						|
    Resource resource = findURLResource(resourceName);
 | 
						|
    if (resource != null)
 | 
						|
      return resource.getURL();
 | 
						|
 | 
						|
    // Resource not found
 | 
						|
    return null;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Finds all the resources with a particular name from all the locations.
 | 
						|
   *
 | 
						|
   * @param resourceName the name of the resource to lookup
 | 
						|
   * @return a (possible empty) enumeration of URLs where the resource can be
 | 
						|
   * found
 | 
						|
   * @exception IOException when an error occurs accessing one of the
 | 
						|
   * locations
 | 
						|
   */
 | 
						|
  public Enumeration<URL> findResources(String resourceName)
 | 
						|
    throws IOException
 | 
						|
  {
 | 
						|
    Vector<URL> resources = new Vector<URL>();
 | 
						|
    int max = urlinfos.size();
 | 
						|
    for (int i = 0; i < max; i++)
 | 
						|
      {
 | 
						|
        URLLoader loader = (URLLoader) urlinfos.elementAt(i);
 | 
						|
        Resource resource = loader.getResource(resourceName);
 | 
						|
        if (resource != null)
 | 
						|
          resources.add(resource.getURL());
 | 
						|
      }
 | 
						|
    return resources.elements();
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns the permissions needed to access a particular code
 | 
						|
   * source.  These permissions includes those returned by
 | 
						|
   * <code>SecureClassLoader.getPermissions()</code> and the actual
 | 
						|
   * permissions to access the objects referenced by the URL of the
 | 
						|
   * code source.  The extra permissions added depend on the protocol
 | 
						|
   * and file portion of the URL in the code source. If the URL has
 | 
						|
   * the "file" protocol ends with a '/' character then it must be a
 | 
						|
   * directory and a file Permission to read everything in that
 | 
						|
   * directory and all subdirectories is added. If the URL had the
 | 
						|
   * "file" protocol and doesn't end with a '/' character then it must
 | 
						|
   * be a normal file and a file permission to read that file is
 | 
						|
   * added. If the <code>URL</code> has any other protocol then a
 | 
						|
   * socket permission to connect and accept connections from the host
 | 
						|
   * portion of the URL is added.
 | 
						|
   *
 | 
						|
   * @param source The codesource that needs the permissions to be accessed
 | 
						|
   * @return the collection of permissions needed to access the code resource
 | 
						|
   * @see java.security.SecureClassLoader#getPermissions(CodeSource)
 | 
						|
   */
 | 
						|
  protected PermissionCollection getPermissions(CodeSource source)
 | 
						|
  {
 | 
						|
    // XXX - This implementation does exactly as the Javadoc describes.
 | 
						|
    // But maybe we should/could use URLConnection.getPermissions()?
 | 
						|
    // First get the permissions that would normally be granted
 | 
						|
    PermissionCollection permissions = super.getPermissions(source);
 | 
						|
 | 
						|
    // Now add any extra permissions depending on the URL location.
 | 
						|
    URL url = source.getLocation();
 | 
						|
    String protocol = url.getProtocol();
 | 
						|
    if (protocol.equals("file"))
 | 
						|
      {
 | 
						|
        String file = url.getFile();
 | 
						|
 | 
						|
        // If the file end in / it must be an directory.
 | 
						|
        if (file.endsWith("/") || file.endsWith(File.separator))
 | 
						|
          {
 | 
						|
            // Grant permission to read everything in that directory and
 | 
						|
            // all subdirectories.
 | 
						|
            permissions.add(new FilePermission(file + "-", "read"));
 | 
						|
          }
 | 
						|
        else
 | 
						|
          {
 | 
						|
            // It is a 'normal' file.
 | 
						|
            // Grant permission to access that file.
 | 
						|
            permissions.add(new FilePermission(file, "read"));
 | 
						|
          }
 | 
						|
      }
 | 
						|
    else
 | 
						|
      {
 | 
						|
        // Grant permission to connect to and accept connections from host
 | 
						|
        String host = url.getHost();
 | 
						|
        if (host != null)
 | 
						|
          permissions.add(new SocketPermission(host, "connect,accept"));
 | 
						|
      }
 | 
						|
 | 
						|
    return permissions;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns all the locations that this class loader currently uses the
 | 
						|
   * resolve classes and resource. This includes both the initially supplied
 | 
						|
   * URLs as any URLs added later by the loader.
 | 
						|
   * @return All the currently used URLs
 | 
						|
   */
 | 
						|
  public URL[] getURLs()
 | 
						|
  {
 | 
						|
    return (URL[]) urls.toArray(new URL[urls.size()]);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Creates a new instance of a <code>URLClassLoader</code> that gets
 | 
						|
   * classes from the supplied <code>URL</code>s. This class loader
 | 
						|
   * will have as parent the standard system class loader.
 | 
						|
   *
 | 
						|
   * @param urls the initial URLs used to resolve classes and
 | 
						|
   * resources
 | 
						|
   *
 | 
						|
   * @return the class loader
 | 
						|
   *
 | 
						|
   * @exception SecurityException when the calling code does not have
 | 
						|
   * permission to access the given <code>URL</code>s
 | 
						|
   */
 | 
						|
  public static URLClassLoader newInstance(URL[] urls)
 | 
						|
    throws SecurityException
 | 
						|
  {
 | 
						|
    return newInstance(urls, null);
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Creates a new instance of a <code>URLClassLoader</code> that gets
 | 
						|
   * classes from the supplied <code>URL</code>s and with the supplied
 | 
						|
   * loader as parent class loader.
 | 
						|
   *
 | 
						|
   * @param urls the initial URLs used to resolve classes and
 | 
						|
   * resources
 | 
						|
   * @param parent the parent class loader
 | 
						|
   *
 | 
						|
   * @return the class loader
 | 
						|
   *
 | 
						|
   * @exception SecurityException when the calling code does not have
 | 
						|
   * permission to access the given <code>URL</code>s
 | 
						|
   */
 | 
						|
  public static URLClassLoader newInstance(URL[] urls, final ClassLoader parent)
 | 
						|
    throws SecurityException
 | 
						|
  {
 | 
						|
    SecurityManager sm = System.getSecurityManager();
 | 
						|
    if (sm == null)
 | 
						|
      return new URLClassLoader(urls, parent);
 | 
						|
    else
 | 
						|
      {
 | 
						|
        final Object securityContext = sm.getSecurityContext();
 | 
						|
 | 
						|
        // XXX - What to do with anything else then an AccessControlContext?
 | 
						|
        if (! (securityContext instanceof AccessControlContext))
 | 
						|
          throw new SecurityException("securityContext must be AccessControlContext: "
 | 
						|
                                      + securityContext);
 | 
						|
 | 
						|
        URLClassLoader loader =
 | 
						|
          AccessController.doPrivileged(new PrivilegedAction<URLClassLoader>()
 | 
						|
              {
 | 
						|
                public URLClassLoader run()
 | 
						|
                {
 | 
						|
                  return new URLClassLoader(parent,
 | 
						|
                                            (AccessControlContext) securityContext);
 | 
						|
                }
 | 
						|
              });
 | 
						|
        loader.addURLs(urls);
 | 
						|
        return loader;
 | 
						|
      }
 | 
						|
  }
 | 
						|
}
 |