home *** CD-ROM | disk | FTP | other *** search
/ Java 1.2 How-To / JavaHowTo.iso / 3rdParty / jbuilder / unsupported / JDK1.2beta3 / SOURCE / SRC.ZIP / java / net / URLClassLoader.java < prev    next >
Encoding:
Java Source  |  1998-03-20  |  11.3 KB  |  356 lines

  1. /*
  2.  * @(#)URLClassLoader.java    1.32 98/03/18
  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.lang.reflect.InvocationTargetException;
  20. import java.io.File;
  21. import java.io.InputStream;
  22. import java.io.IOException;
  23. import java.net.URL;
  24. import java.net.URLConnection;
  25. import java.util.Enumeration;
  26. import java.util.StringTokenizer;
  27. import java.util.jar.Manifest;
  28. import java.util.jar.Attributes;
  29. import java.util.jar.Attributes.Name;
  30. import java.security.AccessController;
  31. import java.security.AccessControlContext;
  32. import java.security.SecureClassLoader;
  33. import java.security.CodeSource;
  34. import java.security.Identity;
  35. import sun.misc.Resource;
  36. import sun.misc.URLClassPath;
  37.  
  38. /**
  39.  * This class loader is used to load classes and resources from a search
  40.  * path of URLs referring to both JAR files and directories. Any URL that
  41.  * ends with a '/' is assumed to refer to a directory. Otherwise, the URL
  42.  * is assumed to refer to a JAR file which will be opened as needed.
  43.  * <p>
  44.  * The AccessControlContext of the thread that created the instance of
  45.  * URLClassLoader will be used when subsequently loading classes and
  46.  * resources.
  47.  * <p>
  48.  * The classes that are loaded are by default granted permission only to
  49.  * access the URLs specified when the URLClassLoader was created.
  50.  *
  51.  * @author  David Connelly
  52.  * @version 1.32, 03/18/98
  53.  * @since   JDK1.2
  54.  */
  55. public class URLClassLoader extends SecureClassLoader {
  56.     /* The search path for classes and resources */
  57.     private URLClassPath ucp;
  58.  
  59.     /* The context to be used when loading classes and resources */
  60.     private AccessControlContext acc;
  61.  
  62.     /**
  63.      * Constructs a new URLClassLoader for the given URLs. The URLs will be
  64.      * searched in the order specified for classes and resources after first
  65.      * searching in the specified parent class loader. Any URL that ends with
  66.      * a '/' is assumed to refer to a directory. Otherwise, the URL is assumed
  67.      * to refer to a JAR file which will be downloaded and opened as needed.
  68.      *
  69.      * @param urls the URLs from which to load classes and resources
  70.      * @param parent the parent class loader for delegation
  71.      */
  72.     public URLClassLoader(URL[] urls, ClassLoader parent) {
  73.     super(parent);
  74.     ucp = new URLClassPath(urls);
  75.     acc = AccessController.getContext();
  76.     }
  77.  
  78.     /**
  79.      * Constructs a new URLClassLoader for the specified URLs using the
  80.      * default delegation parent <code>ClassLoader</code>. The URLs will
  81.      * be searched in the order specified for classes and resources after
  82.      * first searching in the parent class loader. Any URL that ends with
  83.      * a '/' is assumed to refer to a directory. Otherwise, the URL is
  84.      * assumed to refer to a JAR file which will be downloaded and opened
  85.      * as needed.
  86.      *
  87.      * @param urls the URLs from which to load classes and resources
  88.      */
  89.     public URLClassLoader(URL[] urls) {
  90.     super();
  91.     ucp = new URLClassPath(urls);
  92.     acc = AccessController.getContext();
  93.     }
  94.  
  95.     /**
  96.      * Searches the URL class path for the first manifest which defines
  97.      * a 'Main-Class' main attribute. Returns the value of the attribute
  98.      * or null if not found.
  99.      */
  100.     public String getMainClassName() {
  101.     Enumeration e = ucp.getManifests();
  102.     try {
  103.         AccessController.beginPrivileged(acc);
  104.         while (e.hasMoreElements()) {
  105.         Resource res = (Resource)e.nextElement();
  106.         Manifest man = res.getManifest();
  107.         if (man != null) {
  108.             Attributes attr = man.getMainAttributes();
  109.             if (attr != null) {
  110.             String value = attr.getValue(Name.MAIN_CLASS);
  111.             if (value != null) {
  112.                 return value;
  113.             }
  114.             }
  115.         }
  116.         }
  117.     } finally {
  118.         AccessController.endPrivileged();
  119.     }
  120.     return null;
  121.     }
  122.  
  123.     /**
  124.      * Finds and loads the class with the specified name from the URL search
  125.      * path. Any URLs referring to JAR files are loaded and opened as needed
  126.      * until the class is found.
  127.      *
  128.      * @param name the name of the class
  129.      * @return the resulting class
  130.      * @exception ClassNotFoundException if the class could not be found
  131.      */
  132.     protected Class findLocalClass(String name) throws ClassNotFoundException {
  133.     try {
  134.         AccessController.beginPrivileged(acc);
  135.         String path = name.replace('.', '/').concat(".class");
  136.         Resource res = ucp.getResource(path);
  137.         if (res != null) {
  138.         try {
  139.             return defineClass(name, res);
  140.         } catch (IOException e) {
  141.             throw new ClassNotFoundException(name, e);
  142.         }
  143.         } else {
  144.         throw new ClassNotFoundException(name);
  145.         }
  146.     } finally {
  147.         AccessController.endPrivileged();
  148.     }
  149.     }
  150.  
  151.     /*
  152.      * Defines a Class using the class bytes obtained from the specified
  153.      * Resource. The resulting Class must be resolved before it can be
  154.      * used.
  155.      */
  156.     private Class defineClass(String name, Resource res) throws IOException {
  157.     int i = name.lastIndexOf('.');
  158.     URL csu = res.getCodeSourceURL();
  159.     if (i != -1) {
  160.         String pkgname = name.substring(0, i);
  161.         String pkgpath = pkgname.replace('.', '/').concat("/");
  162.         // First, make sure we are allowed to define new classes
  163.         // in this package.
  164.         checkPackageDefinition(pkgname);
  165.         // Get the manifest associated with the resource.
  166.         Manifest man = res.getManifest();
  167.         // Get the package attributes if any
  168.         Attributes attr = null;
  169.         if (man != null) {
  170.         attr = man.getAttributes(pkgpath);
  171.         }
  172.         // Get the Package information if already defined.
  173.         Package pkg = getPackage(pkgname);
  174.         if (pkg != null) {
  175.         // Package already defined, so check package sealing.
  176.         boolean ok;
  177.         if (pkg.isSealed()) {
  178.             URL base = pkg.getSealBase();
  179.             ok = isSealed(man, pkgpath) && csu.equals(base);
  180.         } else {
  181.             ok = !isSealed(man, pkgpath);
  182.         }
  183.         if (!ok) {
  184.             throw new SecurityException("sealing violation");
  185.         }
  186.         } else {
  187.         // Otherwise, define the package
  188.         if (isSealed(man, pkgpath)) {
  189.             definePackage(pkgname, attr, csu);
  190.         } else {
  191.             definePackage(pkgname, attr, null);
  192.         }
  193.         }
  194.     }
  195.     // Now read the class bytes and define the class
  196.     byte[] b = res.getBytes();
  197.     Identity[] ids = res.getIdentities();
  198.     CodeSource cs = getCodeSource(csu, ids);
  199.     return defineClass(name, b, 0, b.length, cs, ids);
  200.     }
  201.  
  202.     /*
  203.      * Returns true iff the specified package is sealed given the archive's
  204.      * manifest and the path name of the package.
  205.      */
  206.     private boolean isSealed(Manifest man, String pkgpath) {
  207.     if (man != null) {
  208.         Attributes attr = man.getAttributes(pkgpath);
  209.         String value = null;
  210.         if (attr != null) {
  211.         value = attr.getValue(Name.PACKAGE_SEALED);
  212.         }
  213.         if (value == null) {
  214.         attr = man.getMainAttributes();
  215.         if (attr != null) {
  216.             value = attr.getValue(Name.ARCHIVE_SEALED);
  217.         }
  218.         }
  219.         return "true".equalsIgnoreCase(value);
  220.     } else {
  221.         return false;
  222.     }
  223.     }
  224.  
  225.     /**
  226.      * Defines a package by name in this ClassLoader. The specification
  227.      * and implementation title, name, and vendor is obtained from the
  228.      * given Attributes object.
  229.      *
  230.      * @param name the package name
  231.      * @param attr the package Attributes
  232.      * @param sealBase if specified then this package is sealed with respect
  233.      *                 to the given code base URL
  234.      * @exception IllegalArgumentException if the package name duplicates an
  235.      *            existing package either in this class loader or the parent
  236.      *            class loader
  237.      */
  238.     protected Package definePackage(String name, Attributes attr, URL sealBase)
  239.     {
  240.     String specTitle = null, specVersion = null, specVendor = null,
  241.            implTitle = null, implVersion = null, implVendor = null;
  242.     if (attr != null) {
  243.         specTitle   = attr.getValue(Name.SPECIFICATION_TITLE);
  244.         specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
  245.         specVendor  = attr.getValue(Name.SPECIFICATION_VENDOR);
  246.         implTitle   = attr.getValue(Name.IMPLEMENTATION_TITLE);
  247.         implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
  248.         implVendor  = attr.getValue(Name.IMPLEMENTATION_VENDOR);
  249.     }
  250.     return definePackage(name,
  251.                  specTitle, specVersion, specVendor,
  252.                  implTitle, implVersion, implVendor,
  253.                  sealBase);
  254.     }
  255.  
  256.     /**
  257.      * This method can be overridden to throw a SecurityException if the
  258.      * calling thread is not allowed to define classes in the package
  259.      * specified by the argument. By default, the calling thread is allowed
  260.      * to define classes in any package.
  261.      *
  262.      * @param name the package name
  263.      * @exception SecurityException if the caller cannot define classes in
  264.      *            the specified package
  265.      */
  266.     protected void checkPackageDefinition(String name)
  267.     throws SecurityException
  268.     {
  269.     }
  270.  
  271.     /**
  272.      * Finds the resource with the specified name on the URL search path.
  273.      * Returns a URL for the resource, or null if the resource could not
  274.      * be found.
  275.      *
  276.      * @param name the name of the resource
  277.      */
  278.     public URL getLocalResource(String name) {
  279.         Resource res = ucp.getResource(name, true);
  280.         return res != null ? res.getURL() : null;
  281.     }
  282.  
  283.     /**
  284.      * Returns an Enumeration of URLs representing all of the resources
  285.      * on the URL search path having the specified name.
  286.      *
  287.      * @param name the resource name
  288.      */
  289.     public Enumeration getLocalResources(String name) throws IOException {
  290.     final Enumeration e = ucp.getResources(name, true);
  291.  
  292.     return new Enumeration() {
  293.         public Object nextElement() {
  294.         return ((Resource)e.nextElement()).getURL();
  295.         }
  296.         public boolean hasMoreElements() {
  297.         return e.hasMoreElements();
  298.         }
  299.     };
  300.     }
  301.  
  302.     /**
  303.      * Utility method for converting a search path string to an array
  304.      * of directory and JAR file URLs.
  305.      *
  306.      * @param path the search path string
  307.      * @return the resulting array of directory and JAR file URLs
  308.      */
  309.     public static URL[] pathToURLs(String path) {
  310.     StringTokenizer st = new StringTokenizer(path, File.pathSeparator);
  311.     URL[] urls = new URL[st.countTokens()];
  312.     int count = 0;
  313.     while (st.hasMoreTokens()) {
  314.         URL url = fileToURL(new File(st.nextToken()));
  315.         if (url != null) {
  316.         urls[count++] = url;
  317.         }
  318.     }
  319.     if (urls.length != count) {
  320.         URL[] tmp = new URL[count];
  321.         System.arraycopy(urls, 0, tmp, 0, count);
  322.         urls = tmp;
  323.     }
  324.     return urls;
  325.     }
  326.  
  327.     /**
  328.      * Returns the directory or JAR file URL corresponding to the specified
  329.      * local file name.
  330.      *
  331.      * @param file the File object
  332.      * @return the resulting directory or JAR file URL, or null if unknown
  333.      */
  334.     public static URL fileToURL(File file) {
  335.     String name;
  336.     try {
  337.         name = file.getCanonicalPath();
  338.     } catch (IOException e) {
  339.         name = file.getAbsolutePath();
  340.     }
  341.     name = name.replace(File.separatorChar, '/');
  342.     if (!name.startsWith("/")) {
  343.         name = "/" + name;
  344.     }
  345.     // If the file does not exist, then assume that it's a directory
  346.     if (!file.isFile()) {
  347.         name = name + "/";
  348.     }
  349.     try {
  350.         return new URL("file", "", name);
  351.     } catch (MalformedURLException e) {
  352.         throw new IllegalArgumentException("file");
  353.     }
  354.     }
  355. }
  356.