home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection Student Program / ADC Tools Sampler CD Disk 3 1999.iso / Metrowerks CodeWarrior / Java Support / Java_Source / Java2 / src / java / net / URLClassLoader.java < prev    next >
Encoding:
Java Source  |  1999-05-28  |  17.7 KB  |  522 lines  |  [TEXT/CWIE]

  1. /*
  2.  * @(#)URLClassLoader.java    1.61 98/09/24
  3.  *
  4.  * Copyright 1997, 1998 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14.  
  15. package java.net;
  16.  
  17. import java.lang.reflect.Method;
  18. import java.lang.reflect.Modifier;
  19. import java.io.File;
  20. import java.io.FilePermission;
  21. import java.io.InputStream;
  22. import java.io.IOException;
  23. import java.net.URL;
  24. import java.net.URLConnection;
  25. import java.net.URLStreamHandlerFactory;
  26. import java.util.Enumeration;
  27. import java.util.StringTokenizer;
  28. import java.util.jar.Manifest;
  29. import java.util.jar.Attributes;
  30. import java.util.jar.Attributes.Name;
  31. import java.security.PrivilegedAction;
  32. import java.security.PrivilegedExceptionAction;
  33. import java.security.AccessController;
  34. import java.security.AccessControlContext;
  35. import java.security.SecureClassLoader;
  36. import java.security.CodeSource;
  37. import java.security.Permission;
  38. import java.security.PermissionCollection;
  39. import sun.misc.Resource;
  40. import sun.misc.URLClassPath;
  41.  
  42. /**
  43.  * This class loader is used to load classes and resources from a search
  44.  * path of URLs referring to both JAR files and directories. Any URL that
  45.  * ends with a '/' is assumed to refer to a directory. Otherwise, the URL
  46.  * is assumed to refer to a JAR file which will be opened as needed.
  47.  * <p>
  48.  * The AccessControlContext of the thread that created the instance of
  49.  * URLClassLoader will be used when subsequently loading classes and
  50.  * resources.
  51.  * <p>
  52.  * The classes that are loaded are by default granted permission only to
  53.  * access the URLs specified when the URLClassLoader was created.
  54.  *
  55.  * @author  David Connelly
  56.  * @version 1.61, 09/24/98
  57.  * @since   JDK1.2
  58.  */
  59. public class URLClassLoader extends SecureClassLoader {
  60.     /* The search path for classes and resources */
  61.     private URLClassPath ucp;
  62.  
  63.     /* The context to be used when loading classes and resources */
  64.     private AccessControlContext acc;
  65.  
  66.     /**
  67.      * Constructs a new URLClassLoader for the given URLs. The URLs will be
  68.      * searched in the order specified for classes and resources after first
  69.      * searching in the specified parent class loader. Any URL that ends with
  70.      * a '/' is assumed to refer to a directory. Otherwise, the URL is assumed
  71.      * to refer to a JAR file which will be downloaded and opened as needed.
  72.      *
  73.      * <p>If there is a security manager, this method first
  74.      * calls the security manager's <code>checkCreateClassLoader</code> method
  75.      * to ensure creation of a class loader is allowed.
  76.      * 
  77.      * @param urls the URLs from which to load classes and resources
  78.      * @param parent the parent class loader for delegation
  79.      * @exception  SecurityException  if a security manager exists and its  
  80.      *             <code>checkCreateClassLoader</code> method doesn't allow 
  81.      *             creation of a class loader.
  82.      * @see SecurityManager#checkCreateClassLoader
  83.      */
  84.     public URLClassLoader(URL[] urls, ClassLoader parent) {
  85.     super(parent);
  86.     // this is to make the stack depth consistent with 1.1
  87.     SecurityManager security = System.getSecurityManager();
  88.     if (security != null) {
  89.         security.checkCreateClassLoader();
  90.     }
  91.     ucp = new URLClassPath(urls);
  92.     acc = AccessController.getContext();
  93.     }
  94.  
  95.     /**
  96.      * Constructs a new URLClassLoader for the specified URLs using the
  97.      * default delegation parent <code>ClassLoader</code>. The URLs will
  98.      * be searched in the order specified for classes and resources after
  99.      * first searching in the parent class loader. Any URL that ends with
  100.      * a '/' is assumed to refer to a directory. Otherwise, the URL is
  101.      * assumed to refer to a JAR file which will be downloaded and opened
  102.      * as needed.
  103.      *
  104.      * <p>If there is a security manager, this method first
  105.      * calls the security manager's <code>checkCreateClassLoader</code> method
  106.      * to ensure creation of a class loader is allowed.
  107.      * 
  108.      * @param urls the URLs from which to load classes and resources
  109.      *
  110.      * @exception  SecurityException  if a security manager exists and its  
  111.      *             <code>checkCreateClassLoader</code> method doesn't allow 
  112.      *             creation of a class loader.
  113.      * @see SecurityManager#checkCreateClassLoader
  114.      */
  115.     public URLClassLoader(URL[] urls) {
  116.     super();
  117.     // this is to make the stack depth consistent with 1.1
  118.     SecurityManager security = System.getSecurityManager();
  119.     if (security != null) {
  120.         security.checkCreateClassLoader();
  121.     }
  122.     ucp = new URLClassPath(urls);
  123.     acc = AccessController.getContext();
  124.     }
  125.  
  126.     /**
  127.      * Constructs a new URLClassLoader for the specified URLs, parent
  128.      * class loader, and URLStreamHandlerFactory. The parent argument
  129.      * will be used as the parent class loader for delegation. The
  130.      * factory argument will be used as the stream handler factory to
  131.      * obtain protocol handlers when creating new URLs.
  132.      *
  133.      * <p>If there is a security manager, this method first
  134.      * calls the security manager's <code>checkCreateClassLoader</code> method
  135.      * to ensure creation of a class loader is allowed.
  136.      *
  137.      * @param urls the URLs from which to load classes and resources
  138.      * @param parent the parent class loader for delegation
  139.      * @param factory the URLStreamHandlerFactory to use when creating URLs
  140.      *
  141.      * @exception  SecurityException  if a security manager exists and its  
  142.      *             <code>checkCreateClassLoader</code> method doesn't allow 
  143.      *             creation of a class loader.
  144.      * @see SecurityManager#checkCreateClassLoader
  145.      */
  146.     public URLClassLoader(URL[] urls, ClassLoader parent,
  147.               URLStreamHandlerFactory factory) {
  148.     super(parent);
  149.     // this is to make the stack depth consistent with 1.1
  150.     SecurityManager security = System.getSecurityManager();
  151.     if (security != null) {
  152.         security.checkCreateClassLoader();
  153.     }
  154.     ucp = new URLClassPath(urls, factory);
  155.     acc = AccessController.getContext();
  156.     }
  157.  
  158.     /**
  159.      * Appends the specified URL to the list of URLs to search for
  160.      * classes and resources.
  161.      *
  162.      * @param url the URL to be added to the search path of URLs
  163.      */
  164.     protected void addURL(URL url) {
  165.     ucp.addURL(url);
  166.     }
  167.  
  168.     /**
  169.      * Returns the search path of URLs for loading classes and resources.
  170.      * This includes the original list of URLs specified to the constructor,
  171.      * along with any URLs subsequently appended by the addURL() method.
  172.      */
  173.     public URL[] getURLs() {
  174.     return ucp.getURLs();
  175.     }
  176.  
  177.     /**
  178.      * Finds and loads the class with the specified name from the URL search
  179.      * path. Any URLs referring to JAR files are loaded and opened as needed
  180.      * until the class is found.
  181.      *
  182.      * @param name the name of the class
  183.      * @return the resulting class
  184.      * @exception ClassNotFoundException if the class could not be found
  185.      */
  186.     protected Class findClass(final String name)
  187.      throws ClassNotFoundException
  188.     {
  189.     try {
  190.         return (Class)
  191.         AccessController.doPrivileged(new PrivilegedExceptionAction() {
  192.             public Object run() throws ClassNotFoundException {
  193.             String path = name.replace('.', '/').concat(".class");
  194.             Resource res = ucp.getResource(path, false);
  195.             if (res != null) {
  196.                 try {
  197.                 return defineClass(name, res);
  198.                 } catch (IOException e) {
  199.                 throw new ClassNotFoundException(name, e);
  200.                 }
  201.             } else {
  202.                 throw new ClassNotFoundException(name);
  203.             }
  204.             }
  205.         }, acc);
  206.     } catch (java.security.PrivilegedActionException pae) {
  207.         throw (ClassNotFoundException) pae.getException();
  208.     }
  209.     }
  210.  
  211.     /*
  212.      * Defines a Class using the class bytes obtained from the specified
  213.      * Resource. The resulting Class must be resolved before it can be
  214.      * used.
  215.      */
  216.     private Class defineClass(String name, Resource res) throws IOException {
  217.     int i = name.lastIndexOf('.');
  218.     URL url = res.getCodeSourceURL();
  219.     if (i != -1) {
  220.         String pkgname = name.substring(0, i);
  221.         // Check if package already loaded.
  222.         Package pkg = getPackage(pkgname);
  223.         Manifest man = res.getManifest();
  224.         if (pkg != null) {
  225.         // Package found, so check package sealing.
  226.         boolean ok;
  227.         if (pkg.isSealed()) {
  228.             // Verify that code source URL is the same.
  229.             ok = pkg.isSealed(url);
  230.         } else {
  231.             // Make sure we are not attempting to seal the package
  232.             // at this code source URL.
  233.             ok = (man == null) || !isSealed(pkgname, man);
  234.         }
  235.         if (!ok) {
  236.             throw new SecurityException("sealing violation");
  237.         }
  238.         } else {
  239.         if (man != null) {
  240.             definePackage(pkgname, man, url);
  241.         }
  242.         }
  243.     }
  244.     // Now read the class bytes and define the class
  245.     byte[] b = res.getBytes();
  246.     java.security.cert.Certificate[] certs = res.getCertificates();
  247.     CodeSource cs = new CodeSource(url, certs);
  248.     return defineClass(name, b, 0, b.length, cs);
  249.     }
  250.  
  251.     /**
  252.      * Defines a new package by name in this ClassLoader. The attributes
  253.      * contained in the specified Manifest will be used to obtain package
  254.      * version and sealing information. For sealed packages, the additional
  255.      * URL specifies the code source URL from which the package was loaded.
  256.      *
  257.      * @param name  the package name
  258.      * @param man   the Manifest containing package version and sealing
  259.      *              information
  260.      * @param url   the code source url for the package, or null if none
  261.      * @exception   IllegalArgumentException if the package name duplicates
  262.      *              an existing package either in this class loader or one
  263.      *              of its ancestors
  264.      */
  265.     protected Package definePackage(String name, Manifest man, URL url)
  266.     throws IllegalArgumentException
  267.     {
  268.     String path = name.replace('.', '/').concat("/");
  269.     String specTitle = null, specVersion = null, specVendor = null;
  270.     String implTitle = null, implVersion = null, implVendor = null;
  271.     String sealed = null;
  272.     URL sealBase = null;
  273.  
  274.     Attributes attr = man.getAttributes(path);
  275.     if (attr != null) {
  276.         specTitle   = attr.getValue(Name.SPECIFICATION_TITLE);
  277.         specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
  278.         specVendor  = attr.getValue(Name.SPECIFICATION_VENDOR);
  279.         implTitle   = attr.getValue(Name.IMPLEMENTATION_TITLE);
  280.         implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
  281.         implVendor  = attr.getValue(Name.IMPLEMENTATION_VENDOR);
  282.         sealed      = attr.getValue(Name.SEALED);
  283.     }
  284.     attr = man.getMainAttributes();
  285.     if (attr != null) {
  286.         if (specTitle == null) {
  287.         specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
  288.         }
  289.         if (specVersion == null) {
  290.         specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
  291.         }
  292.         if (specVendor == null) {
  293.         specVendor = attr.getValue(Name.SPECIFICATION_VENDOR);
  294.         }
  295.         if (implTitle == null) {
  296.         implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE);
  297.         }
  298.         if (implVersion == null) {
  299.         implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
  300.         }
  301.         if (implVendor == null) {
  302.         implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
  303.         }
  304.         if (sealed == null) {
  305.         sealed = attr.getValue(Name.SEALED);
  306.         }
  307.     }
  308.     if ("true".equalsIgnoreCase(sealed)) {
  309.         sealBase = url;
  310.     }
  311.     return definePackage(name, specTitle, specVersion, specVendor,
  312.                  implTitle, implVersion, implVendor, sealBase);
  313.     }
  314.  
  315.     /*
  316.      * Returns true if the specified package name is sealed according to the
  317.      * given manifest.
  318.      */
  319.     private boolean isSealed(String name, Manifest man) {
  320.     String path = name.replace('.', '/').concat("/");
  321.     Attributes attr = man.getAttributes(path);
  322.     String sealed = null;
  323.     if (attr != null) {
  324.         sealed = attr.getValue(Name.SEALED);
  325.     }
  326.     if (sealed == null) {
  327.         if ((attr = man.getMainAttributes()) != null) {
  328.         sealed = attr.getValue(Name.SEALED);
  329.         }
  330.     }
  331.     return "true".equalsIgnoreCase(sealed);
  332.     }
  333.  
  334.     /**
  335.      * Finds the resource with the specified name on the URL search path.
  336.      * Returns a URL for the resource, or null if the resource could not
  337.      * be found.
  338.      *
  339.      * @param name the name of the resource
  340.      */
  341.     public URL findResource(String name) {
  342.         Resource res = ucp.getResource(name, true);
  343.         return res != null ? res.getURL() : null;
  344.     }
  345.  
  346.     /**
  347.      * Returns an Enumeration of URLs representing all of the resources
  348.      * on the URL search path having the specified name.
  349.      *
  350.      * @param name the resource name
  351.      */
  352.     public Enumeration findResources(String name) throws IOException {
  353.     final Enumeration e = ucp.getResources(name, true);
  354.  
  355.     return new Enumeration() {
  356.         public Object nextElement() {
  357.         return ((Resource)e.nextElement()).getURL();
  358.         }
  359.         public boolean hasMoreElements() {
  360.         return e.hasMoreElements();
  361.         }
  362.     };
  363.     }
  364.  
  365.     /**
  366.      * Returns the permissions for the given codesource object.
  367.      * The implementation of this method first calls super.getPermissions,
  368.      * to get the permissions
  369.      * granted by the policy, and then adds additional permissions
  370.      * based on the URL of the codesource.
  371.      * <p>
  372.      * If the protocol is "file"
  373.      * and the path specifies a file, then permission to read that
  374.      * file is granted. If protocol is "file" and the path is
  375.      * a directory, permission is granted to read all files
  376.      * and (recursively) all files and subdirectories contained in
  377.      * that directory.
  378.      * <p>
  379.      * If the protocol is not "file", then
  380.      * to connect to and accept connections from the URL's host is granted.
  381.      * @param codesource the codesource
  382.      * @return the permissions granted to the codesource
  383.      */
  384.     protected PermissionCollection getPermissions(CodeSource codesource)
  385.     {
  386.     PermissionCollection perms = super.getPermissions(codesource);
  387.  
  388.     URL url = codesource.getLocation();
  389.  
  390.     Permission p;
  391.  
  392.     try {
  393.         p = url.openConnection().getPermission();
  394.     } catch (java.io.IOException ioe) {
  395.  
  396.         p = null;
  397.     }
  398.  
  399.     if (p instanceof FilePermission) {
  400.         // if the permission has a separator char on the end,
  401.         // it means the codebase is a directory, and we need
  402.         // to add an additional permission to read recursively
  403.         String path = p.getName();
  404.         if (path.endsWith(File.separator)) {
  405.         path += "-";
  406.         p = new FilePermission(path, "read");
  407.         }
  408.     } else if ((p == null) && (url.getProtocol().equals("file"))) {
  409.         String path = url.getFile().replace('/', File.separatorChar);
  410.         if (path.endsWith(File.separator))
  411.         path += "-";
  412.         p =  new FilePermission(path, "read");
  413.     } else {
  414.         String host = url.getHost();
  415.         if (host == null)
  416.         host = "localhost";
  417.         p = new SocketPermission(host,"connect, accept");
  418.     }
  419.  
  420.     // make sure the person that created this class loader
  421.     // would have this permission
  422.  
  423.     if (p != null) {
  424.         final SecurityManager sm = System.getSecurityManager();
  425.         if (sm != null) {
  426.         final Permission fp = p;
  427.         AccessController.doPrivileged(new PrivilegedAction() {
  428.             public Object run() throws SecurityException {
  429.             sm.checkPermission(fp);
  430.             return null;
  431.             }
  432.         }, acc);
  433.         }
  434.         perms.add(p);
  435.     }
  436.     return perms;
  437.     }
  438.  
  439.     /**
  440.      * Creates a new instance of URLClassLoader for the specified
  441.      * URLs and parent class loader. If a security manager is
  442.      * installed, the <code>loadClass</code> method of the URLClassLoader
  443.      * returned by this method will invoke the
  444.      * <code>SecurityManager.checkPackageAccess</code> method before
  445.      * loading the class.
  446.      *
  447.      * @param urls the URLs to search for classes and resources
  448.      * @param parent the parent class loader for delegation
  449.      * @return the resulting class loader
  450.      */
  451.     public static URLClassLoader newInstance(final URL[] urls,
  452.                          final ClassLoader parent) {
  453.     // Save the caller's context
  454.     AccessControlContext acc = AccessController.getContext();
  455.     // Need a privileged block to create the class loader
  456.     URLClassLoader ucl =
  457.         (URLClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
  458.         public Object run() {
  459.             return new FactoryURLClassLoader(urls, parent);
  460.         }
  461.         });
  462.     // Now set the context on the loader using the one we saved,
  463.     // not the one inside the privileged block...
  464.     ucl.acc = acc;
  465.     return ucl;
  466.     }
  467.  
  468.     /**
  469.      * Creates a new instance of URLClassLoader for the specified
  470.      * URLs and default parent class loader. If a security manager is
  471.      * installed, the <code>loadClass</code> method of the URLClassLoader
  472.      * returned by this method will invoke the
  473.      * <code>SecurityManager.checkPackageAccess</code> before
  474.      * loading the class.
  475.      *
  476.      * @param urls the URLs to search for classes and resources
  477.      * @return the resulting class loader
  478.      */
  479.     public static URLClassLoader newInstance(final URL[] urls) {
  480.     // Save the caller's context
  481.     AccessControlContext acc = AccessController.getContext();
  482.     // Need a privileged block to create the class loader
  483.     URLClassLoader ucl = (URLClassLoader)
  484.         AccessController.doPrivileged(new PrivilegedAction() {
  485.         public Object run() {
  486.             return new FactoryURLClassLoader(urls);
  487.         }
  488.         });
  489.  
  490.     // Now set the context on the loader using the one we saved,
  491.     // not the one inside the privileged block...
  492.     ucl.acc = acc;
  493.     return ucl;
  494.     }
  495. }
  496.  
  497. final class FactoryURLClassLoader extends URLClassLoader {
  498.  
  499.     FactoryURLClassLoader(URL[] urls, ClassLoader parent) {
  500.     super(urls, parent);
  501.     }
  502.  
  503.     FactoryURLClassLoader(URL[] urls) {
  504.     super(urls);
  505.     }
  506.  
  507.     public final synchronized Class loadClass(String name, boolean resolve)
  508.     throws ClassNotFoundException
  509.     {
  510.     // First check if we have permission to access the package. This
  511.     // should go away once we've added support for exported packages.
  512.     SecurityManager sm = System.getSecurityManager();
  513.     if (sm != null) {
  514.         int i = name.lastIndexOf('.');
  515.         if (i != -1) {
  516.         sm.checkPackageAccess(name.substring(0, i));
  517.         }
  518.     }
  519.     return super.loadClass(name, resolve);
  520.     }
  521. }
  522.