home *** CD-ROM | disk | FTP | other *** search
Java Source | 1998-03-20 | 11.3 KB | 356 lines |
- /*
- * @(#)URLClassLoader.java 1.32 98/03/18
- *
- * Copyright 1997, 1998 by Sun Microsystems, Inc.,
- * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
- * All rights reserved.
- *
- * This software is the confidential and proprietary information
- * of Sun Microsystems, Inc. ("Confidential Information"). You
- * shall not disclose such Confidential Information and shall use
- * it only in accordance with the terms of the license agreement
- * you entered into with Sun.
- */
-
- package java.net;
-
- import java.lang.reflect.Method;
- import java.lang.reflect.Modifier;
- import java.lang.reflect.InvocationTargetException;
- import java.io.File;
- import java.io.InputStream;
- import java.io.IOException;
- import java.net.URL;
- import java.net.URLConnection;
- import java.util.Enumeration;
- import java.util.StringTokenizer;
- import java.util.jar.Manifest;
- import java.util.jar.Attributes;
- import java.util.jar.Attributes.Name;
- import java.security.AccessController;
- import java.security.AccessControlContext;
- import java.security.SecureClassLoader;
- import java.security.CodeSource;
- import java.security.Identity;
- import sun.misc.Resource;
- import sun.misc.URLClassPath;
-
- /**
- * This class loader is used to load classes and resources from a search
- * path of URLs referring to both JAR files and directories. Any URL that
- * ends with a '/' is assumed to refer to a directory. Otherwise, the URL
- * is assumed to refer to a JAR file which will be opened as needed.
- * <p>
- * The AccessControlContext of the thread that created the instance of
- * URLClassLoader will be used when subsequently loading classes and
- * resources.
- * <p>
- * The classes that are loaded are by default granted permission only to
- * access the URLs specified when the URLClassLoader was created.
- *
- * @author David Connelly
- * @version 1.32, 03/18/98
- * @since JDK1.2
- */
- public class URLClassLoader extends SecureClassLoader {
- /* The search path for classes and resources */
- private URLClassPath ucp;
-
- /* The context to be used when loading classes and resources */
- private AccessControlContext acc;
-
- /**
- * Constructs a new URLClassLoader for the given URLs. The URLs will be
- * searched in the order specified for classes and resources after first
- * searching in the specified parent class loader. Any URL that ends with
- * a '/' is assumed to refer to a directory. Otherwise, the URL is assumed
- * to refer to a JAR file which will be downloaded and opened as needed.
- *
- * @param urls the URLs from which to load classes and resources
- * @param parent the parent class loader for delegation
- */
- public URLClassLoader(URL[] urls, ClassLoader parent) {
- super(parent);
- ucp = new URLClassPath(urls);
- acc = AccessController.getContext();
- }
-
- /**
- * Constructs a new URLClassLoader for the specified URLs using the
- * default delegation parent <code>ClassLoader</code>. The URLs will
- * be searched in the order specified for classes and resources after
- * first searching in the parent class loader. Any URL that ends with
- * a '/' is assumed to refer to a directory. Otherwise, the URL is
- * assumed to refer to a JAR file which will be downloaded and opened
- * as needed.
- *
- * @param urls the URLs from which to load classes and resources
- */
- public URLClassLoader(URL[] urls) {
- super();
- ucp = new URLClassPath(urls);
- acc = AccessController.getContext();
- }
-
- /**
- * Searches the URL class path for the first manifest which defines
- * a 'Main-Class' main attribute. Returns the value of the attribute
- * or null if not found.
- */
- public String getMainClassName() {
- Enumeration e = ucp.getManifests();
- try {
- AccessController.beginPrivileged(acc);
- while (e.hasMoreElements()) {
- Resource res = (Resource)e.nextElement();
- Manifest man = res.getManifest();
- if (man != null) {
- Attributes attr = man.getMainAttributes();
- if (attr != null) {
- String value = attr.getValue(Name.MAIN_CLASS);
- if (value != null) {
- return value;
- }
- }
- }
- }
- } finally {
- AccessController.endPrivileged();
- }
- return null;
- }
-
- /**
- * Finds and loads the class with the specified name from the URL search
- * path. Any URLs referring to JAR files are loaded and opened as needed
- * until the class is found.
- *
- * @param name the name of the class
- * @return the resulting class
- * @exception ClassNotFoundException if the class could not be found
- */
- protected Class findLocalClass(String name) throws ClassNotFoundException {
- try {
- AccessController.beginPrivileged(acc);
- String path = name.replace('.', '/').concat(".class");
- Resource res = ucp.getResource(path);
- if (res != null) {
- try {
- return defineClass(name, res);
- } catch (IOException e) {
- throw new ClassNotFoundException(name, e);
- }
- } else {
- throw new ClassNotFoundException(name);
- }
- } finally {
- AccessController.endPrivileged();
- }
- }
-
- /*
- * Defines a Class using the class bytes obtained from the specified
- * Resource. The resulting Class must be resolved before it can be
- * used.
- */
- private Class defineClass(String name, Resource res) throws IOException {
- int i = name.lastIndexOf('.');
- URL csu = res.getCodeSourceURL();
- if (i != -1) {
- String pkgname = name.substring(0, i);
- String pkgpath = pkgname.replace('.', '/').concat("/");
- // First, make sure we are allowed to define new classes
- // in this package.
- checkPackageDefinition(pkgname);
- // Get the manifest associated with the resource.
- Manifest man = res.getManifest();
- // Get the package attributes if any
- Attributes attr = null;
- if (man != null) {
- attr = man.getAttributes(pkgpath);
- }
- // Get the Package information if already defined.
- Package pkg = getPackage(pkgname);
- if (pkg != null) {
- // Package already defined, so check package sealing.
- boolean ok;
- if (pkg.isSealed()) {
- URL base = pkg.getSealBase();
- ok = isSealed(man, pkgpath) && csu.equals(base);
- } else {
- ok = !isSealed(man, pkgpath);
- }
- if (!ok) {
- throw new SecurityException("sealing violation");
- }
- } else {
- // Otherwise, define the package
- if (isSealed(man, pkgpath)) {
- definePackage(pkgname, attr, csu);
- } else {
- definePackage(pkgname, attr, null);
- }
- }
- }
- // Now read the class bytes and define the class
- byte[] b = res.getBytes();
- Identity[] ids = res.getIdentities();
- CodeSource cs = getCodeSource(csu, ids);
- return defineClass(name, b, 0, b.length, cs, ids);
- }
-
- /*
- * Returns true iff the specified package is sealed given the archive's
- * manifest and the path name of the package.
- */
- private boolean isSealed(Manifest man, String pkgpath) {
- if (man != null) {
- Attributes attr = man.getAttributes(pkgpath);
- String value = null;
- if (attr != null) {
- value = attr.getValue(Name.PACKAGE_SEALED);
- }
- if (value == null) {
- attr = man.getMainAttributes();
- if (attr != null) {
- value = attr.getValue(Name.ARCHIVE_SEALED);
- }
- }
- return "true".equalsIgnoreCase(value);
- } else {
- return false;
- }
- }
-
- /**
- * Defines a package by name in this ClassLoader. The specification
- * and implementation title, name, and vendor is obtained from the
- * given Attributes object.
- *
- * @param name the package name
- * @param attr the package Attributes
- * @param sealBase if specified then this package is sealed with respect
- * to the given code base URL
- * @exception IllegalArgumentException if the package name duplicates an
- * existing package either in this class loader or the parent
- * class loader
- */
- protected Package definePackage(String name, Attributes attr, URL sealBase)
- {
- String specTitle = null, specVersion = null, specVendor = null,
- implTitle = null, implVersion = null, implVendor = null;
- if (attr != null) {
- specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
- specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
- specVendor = attr.getValue(Name.SPECIFICATION_VENDOR);
- implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE);
- implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
- implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
- }
- return definePackage(name,
- specTitle, specVersion, specVendor,
- implTitle, implVersion, implVendor,
- sealBase);
- }
-
- /**
- * This method can be overridden to throw a SecurityException if the
- * calling thread is not allowed to define classes in the package
- * specified by the argument. By default, the calling thread is allowed
- * to define classes in any package.
- *
- * @param name the package name
- * @exception SecurityException if the caller cannot define classes in
- * the specified package
- */
- protected void checkPackageDefinition(String name)
- throws SecurityException
- {
- }
-
- /**
- * Finds the resource with the specified name on the URL search path.
- * Returns a URL for the resource, or null if the resource could not
- * be found.
- *
- * @param name the name of the resource
- */
- public URL getLocalResource(String name) {
- Resource res = ucp.getResource(name, true);
- return res != null ? res.getURL() : null;
- }
-
- /**
- * Returns an Enumeration of URLs representing all of the resources
- * on the URL search path having the specified name.
- *
- * @param name the resource name
- */
- public Enumeration getLocalResources(String name) throws IOException {
- final Enumeration e = ucp.getResources(name, true);
-
- return new Enumeration() {
- public Object nextElement() {
- return ((Resource)e.nextElement()).getURL();
- }
- public boolean hasMoreElements() {
- return e.hasMoreElements();
- }
- };
- }
-
- /**
- * Utility method for converting a search path string to an array
- * of directory and JAR file URLs.
- *
- * @param path the search path string
- * @return the resulting array of directory and JAR file URLs
- */
- public static URL[] pathToURLs(String path) {
- StringTokenizer st = new StringTokenizer(path, File.pathSeparator);
- URL[] urls = new URL[st.countTokens()];
- int count = 0;
- while (st.hasMoreTokens()) {
- URL url = fileToURL(new File(st.nextToken()));
- if (url != null) {
- urls[count++] = url;
- }
- }
- if (urls.length != count) {
- URL[] tmp = new URL[count];
- System.arraycopy(urls, 0, tmp, 0, count);
- urls = tmp;
- }
- return urls;
- }
-
- /**
- * Returns the directory or JAR file URL corresponding to the specified
- * local file name.
- *
- * @param file the File object
- * @return the resulting directory or JAR file URL, or null if unknown
- */
- public static URL fileToURL(File file) {
- String name;
- try {
- name = file.getCanonicalPath();
- } catch (IOException e) {
- name = file.getAbsolutePath();
- }
- name = name.replace(File.separatorChar, '/');
- if (!name.startsWith("/")) {
- name = "/" + name;
- }
- // If the file does not exist, then assume that it's a directory
- if (!file.isFile()) {
- name = name + "/";
- }
- try {
- return new URL("file", "", name);
- } catch (MalformedURLException e) {
- throw new IllegalArgumentException("file");
- }
- }
- }
-