/* * @(#)RMIClassLoader.java 1.40 04/05/18 * * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */ package java.rmi.server; import java.net.MalformedURLException; import java.net.URL; import java.util.Iterator; import sun.misc.Service; /** * RMIClassLoader comprises static methods to support * dynamic class loading with RMI. Included are methods for loading * classes from a network location (one or more URLs) and obtaining * the location from which an existing class should be loaded by * remote parties. These methods are used by the RMI runtime when * marshalling and unmarshalling classes contained in the arguments * and return values of remote method calls, and they also may be * invoked directly by applications in order to mimic RMI's dynamic * class loading behavior. * *

The implementation of the following static methods * *

* * is provided by an instance of {@link RMIClassLoaderSpi}, the * service provider interface for those methods. When one of the * methods is invoked, its behavior is to delegate to a corresponding * method on the service provider instance. The details of how each * method delegates to the provider instance is described in the * documentation for each particular method. * *

The service provider instance is chosen as follows: * *

* * @version 1.40, 04/05/18 * @author Ann Wollrath * @author Peter Jones * @author Laird Dornin * @see RMIClassLoaderSpi * @since JDK1.1 */ public class RMIClassLoader { /** "default" provider instance */ private static final RMIClassLoaderSpi defaultProvider = newDefaultProviderInstance(); /** provider instance */ private static final RMIClassLoaderSpi provider = (RMIClassLoaderSpi) java.security.AccessController.doPrivileged( new java.security.PrivilegedAction() { public Object run() { return initializeProvider(); } }); /* * Disallow anyone from creating one of these. */ private RMIClassLoader() {} /** * Loads the class with the specified name. * *

This method delegates to {@link #loadClass(String,String)}, * passing null as the first argument and * name as the second argument. * * @param name the name of the class to load * * @return the Class object representing the loaded class * * @throws MalformedURLException if a provider-specific URL used * to load classes is invalid * * @throws ClassNotFoundException if a definition for the class * could not be found at the codebase location * * @deprecated replaced by loadClass(String,String) method * @see #loadClass(String,String) */ @Deprecated public static Class loadClass(String name) throws MalformedURLException, ClassNotFoundException { return loadClass((String) null, name); } /** * Loads a class from a codebase URL. * * If codebase is null, then this method * will behave the same as {@link #loadClass(String,String)} with a * null codebase and the given class name. * *

This method delegates to the * {@link RMIClassLoaderSpi#loadClass(String,String,ClassLoader)} * method of the provider instance, passing the result of invoking * {@link URL#toString} on the given URL (or null if * codebase is null) as the first argument, * name as the second argument, * and null as the third argument. * * @param codebase the URL to load the class from, or null * * @param name the name of the class to load * * @return the Class object representing the loaded class * * @throws MalformedURLException if codebase is * null and a provider-specific URL used * to load classes is invalid * * @throws ClassNotFoundException if a definition for the class * could not be found at the specified URL */ public static Class loadClass(URL codebase, String name) throws MalformedURLException, ClassNotFoundException { return provider.loadClass( codebase != null ? codebase.toString() : null, name, null); } /** * Loads a class from a codebase URL path. * *

This method delegates to the * {@link RMIClassLoaderSpi#loadClass(String,String,ClassLoader)} * method of the provider instance, passing codebase * as the first argument, name as the second argument, * and null as the third argument. * * @param codebase the list of URLs (separated by spaces) to load * the class from, or null * * @param name the name of the class to load * * @return the Class object representing the loaded class * * @throws MalformedURLException if codebase is * non-null and contains an invalid URL, or if * codebase is null and a provider-specific * URL used to load classes is invalid * * @throws ClassNotFoundException if a definition for the class * could not be found at the specified location * * @since 1.2 */ public static Class loadClass(String codebase, String name) throws MalformedURLException, ClassNotFoundException { return provider.loadClass(codebase, name, null); } /** * Loads a class from a codebase URL path, optionally using the * supplied loader. * * This method should be used when the caller would like to make * available to the provider implementation an additional contextual * class loader to consider, such as the loader of a caller on the * stack. Typically, a provider implementation will attempt to * resolve the named class using the given defaultLoader, * if specified, before attempting to resolve the class from the * codebase URL path. * *

This method delegates to the * {@link RMIClassLoaderSpi#loadClass(String,String,ClassLoader)} * method of the provider instance, passing codebase * as the first argument, name as the second argument, * and defaultLoader as the third argument. * * @param codebase the list of URLs (separated by spaces) to load * the class from, or null * * @param name the name of the class to load * * @param defaultLoader additional contextual class loader * to use, or null * * @return the Class object representing the loaded class * * @throws MalformedURLException if codebase is * non-null and contains an invalid URL, or if * codebase is null and a provider-specific * URL used to load classes is invalid * * @throws ClassNotFoundException if a definition for the class * could not be found at the specified location * * @since 1.4 */ public static Class loadClass(String codebase, String name, ClassLoader defaultLoader) throws MalformedURLException, ClassNotFoundException { return provider.loadClass(codebase, name, defaultLoader); } /** * Loads a dynamic proxy class (see {@link java.lang.reflect.Proxy}) * that implements a set of interfaces with the given names * from a codebase URL path. * *

The interfaces will be resolved similar to classes loaded via * the {@link #loadClass(String,String)} method using the given * codebase. * *

This method delegates to the * {@link RMIClassLoaderSpi#loadProxyClass(String,String[],ClassLoader)} * method of the provider instance, passing codebase * as the first argument, interfaces as the second argument, * and defaultLoader as the third argument. * * @param codebase the list of URLs (space-separated) to load * classes from, or null * * @param interfaces the names of the interfaces for the proxy class * to implement * * @param defaultLoader additional contextual class loader * to use, or null * * @return a dynamic proxy class that implements the named interfaces * * @throws MalformedURLException if codebase is * non-null and contains an invalid URL, or * if codebase is null and a provider-specific * URL used to load classes is invalid * * @throws ClassNotFoundException if a definition for one of * the named interfaces could not be found at the specified location, * or if creation of the dynamic proxy class failed (such as if * {@link java.lang.reflect.Proxy#getProxyClass(ClassLoader,Class[])} * would throw an IllegalArgumentException for the given * interface list) * * @since 1.4 */ public static Class loadProxyClass(String codebase, String[] interfaces, ClassLoader defaultLoader) throws ClassNotFoundException, MalformedURLException { return provider.loadProxyClass(codebase, interfaces, defaultLoader); } /** * Returns a class loader that loads classes from the given codebase * URL path. * *

The class loader returned is the class loader that the * {@link #loadClass(String,String)} method would use to load classes * for the same codebase argument. * *

This method delegates to the * {@link RMIClassLoaderSpi#getClassLoader(String)} method * of the provider instance, passing codebase as the argument. * *

If there is a security manger, its checkPermission * method will be invoked with a * RuntimePermission("getClassLoader") permission; * this could result in a SecurityException. * The provider implementation of this method may also perform further * security checks to verify that the calling context has permission to * connect to all of the URLs in the codebase URL path. * * @param codebase the list of URLs (space-separated) from which * the returned class loader will load classes from, or null * * @return a class loader that loads classes from the given codebase URL * path * * @throws MalformedURLException if codebase is * non-null and contains an invalid URL, or * if codebase is null and a provider-specific * URL used to identify the class loader is invalid * * @throws SecurityException if there is a security manager and the * invocation of its checkPermission method fails, or * if the caller does not have permission to connect to all of the * URLs in the codebase URL path * * @since 1.3 */ public static ClassLoader getClassLoader(String codebase) throws MalformedURLException, SecurityException { return provider.getClassLoader(codebase); } /** * Returns the annotation string (representing a location for * the class definition) that RMI will use to annotate the class * descriptor when marshalling objects of the given class. * *

This method delegates to the * {@link RMIClassLoaderSpi#getClassAnnotation(Class)} method * of the provider instance, passing cl as the argument. * * @param cl the class to obtain the annotation for * * @return a string to be used to annotate the given class when * it gets marshalled, or null * * @throws NullPointerException if cl is null * * @since 1.2 */ /* * REMIND: Should we say that the returned class annotation will or * should be a (space-separated) list of URLs? */ public static String getClassAnnotation(Class cl) { return provider.getClassAnnotation(cl); } /** * Returns the canonical instance of the default provider * for the service provider interface {@link RMIClassLoaderSpi}. * If the system property java.rmi.server.RMIClassLoaderSpi * is not defined, then the RMIClassLoader static * methods * *

* * will use the canonical instance of the default provider * as the service provider instance. * *

If there is a security manager, its * checkPermission method will be invoked with a * RuntimePermission("setFactory") permission; this * could result in a SecurityException. * *

The default service provider instance implements * {@link RMIClassLoaderSpi} as follows: * *

* *

The {@link RMIClassLoaderSpi#getClassAnnotation(Class) * getClassAnnotation} method returns a String * representing the codebase URL path that a remote party should * use to download the definition for the specified class. The * format of the returned string is a path of URLs separated by * spaces. * * The codebase string returned depends on the defining class * loader of the specified class: * *

* *

For the implementations of the methods described below, * which all take a String parameter named * codebase that is a space-separated list of URLs, * each invocation has an associated codebase loader that * is identified using the codebase argument in * conjunction with the current thread's context class loader (see * {@link Thread#getContextClassLoader()}). When there is a * security manager, this provider maintains an internal table of * class loader instances (which are at least instances of {@link * java.net.URLClassLoader}) keyed by the pair of their parent * class loader and their codebase URL path (an ordered list of * URLs). If the codebase argument is null, * the codebase URL path is the value of the system property * java.rmi.server.codebase or possibly an * earlier cached value. For a given codebase URL path passed as the * codebase argument to an invocation of one of the * below methods in a given context, the codebase loader is the * loader in the table with the specified codebase URL path and * the current thread's context class loader as its parent. If no * such loader exists, then one is created and added to the table. * The table does not maintain strong references to its contained * loaders, in order to allow them and their defined classes to be * garbage collected when not otherwise reachable. In order to * prevent arbitrary untrusted code from being implicitly loaded * into a virtual machine with no security manager, if there is no * security manager set, the codebase loader is just the current * thread's context class loader (the supplied codebase URL path * is ignored, so remote class loading is disabled). * *

The {@link RMIClassLoaderSpi#getClassLoader(String) * getClassLoader} method returns the codebase loader for the * specified codebase URL path. If there is a security manager, * then if the calling context does not have permission to connect * to all of the URLs in the codebase URL path, a * SecurityException will be thrown. * *

The {@link * RMIClassLoaderSpi#loadClass(String,String,ClassLoader) * loadClass} method attempts to load the class with the * specified name as follows: * *

* * If the defaultLoader argument is * non-null, it first attempts to load the class with the * specified name using the * defaultLoader, such as by evaluating * *
     *     Class.forName(name, false, defaultLoader)
     * 
* * If the class is successfully loaded from the * defaultLoader, that class is returned. If an * exception other than ClassNotFoundException is * thrown, that exception is thrown to the caller. * *

Next, the loadClass method attempts to load the * class with the specified name using the codebase * loader for the specified codebase URL path. * If there is a security manager, then the calling context * must have permission to connect to all of the URLs in the * codebase URL path; otherwise, the current thread's context * class loader will be used instead of the codebase loader. * *

* *

The {@link * RMIClassLoaderSpi#loadProxyClass(String,String[],ClassLoader) * loadProxyClass} method attempts to return a dynamic proxy * class with the named interface as follows: * *

* *

If the defaultLoader argument is * non-null and all of the named interfaces can be * resolved through that loader, then, * *

* *

Otherwise, if all of the named interfaces can be resolved * through the codebase loader, then, * *

* *

Otherwise, a ClassNotFoundException is thrown * for one of the named interfaces that could not be resolved. * *

* *
* * @return the canonical instance of the default service provider * * @throws SecurityException if there is a security manager and the * invocation of its checkPermission method fails * * @since 1.4 */ public static RMIClassLoaderSpi getDefaultProviderInstance() { SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new RuntimePermission("setFactory")); } return defaultProvider; } /** * Returns the security context of the given class loader. * * @param loader a class loader from which to get the security context * * @return the security context * * @deprecated no replacement. As of the Java 2 platform v1.2, RMI no * longer uses this method to obtain a class loader's security context. * @see java.lang.SecurityManager#getSecurityContext() */ @Deprecated public static Object getSecurityContext(ClassLoader loader) { return sun.rmi.server.LoaderHandler.getSecurityContext(loader); } /** * Creates an instance of the default provider class. */ private static RMIClassLoaderSpi newDefaultProviderInstance() { return new RMIClassLoaderSpi() { public Class loadClass(String codebase, String name, ClassLoader defaultLoader) throws MalformedURLException, ClassNotFoundException { return sun.rmi.server.LoaderHandler.loadClass( codebase, name, defaultLoader); } public Class loadProxyClass(String codebase, String[] interfaces, ClassLoader defaultLoader) throws MalformedURLException, ClassNotFoundException { return sun.rmi.server.LoaderHandler.loadProxyClass( codebase, interfaces, defaultLoader); } public ClassLoader getClassLoader(String codebase) throws MalformedURLException { return sun.rmi.server.LoaderHandler.getClassLoader(codebase); } public String getClassAnnotation(Class cl) { return sun.rmi.server.LoaderHandler.getClassAnnotation(cl); } }; } /** * Chooses provider instance, following above documentation. * * This method assumes that it has been invoked in a privileged block. */ private static RMIClassLoaderSpi initializeProvider() { /* * First check for the system property being set: */ String providerClassName = System.getProperty("java.rmi.server.RMIClassLoaderSpi"); if (providerClassName != null) { if (providerClassName.equals("default")) { return defaultProvider; } try { Class providerClass = Class.forName(providerClassName, false, ClassLoader.getSystemClassLoader()); return (RMIClassLoaderSpi) providerClass.newInstance(); } catch (ClassNotFoundException e) { throw new NoClassDefFoundError(e.getMessage()); } catch (IllegalAccessException e) { throw new IllegalAccessError(e.getMessage()); } catch (InstantiationException e) { throw new InstantiationError(e.getMessage()); } catch (ClassCastException e) { Error error = new LinkageError( "provider class not assignable to RMIClassLoaderSpi"); error.initCause(e); throw error; } } /* * Next look for a provider configuration file intalled: */ Iterator iter = Service.providers(RMIClassLoaderSpi.class, ClassLoader.getSystemClassLoader()); if (iter.hasNext()) { try { return (RMIClassLoaderSpi) iter.next(); } catch (ClassCastException e) { Error error = new LinkageError( "provider class not assignable to RMIClassLoaderSpi"); error.initCause(e); throw error; } } /* * Finally, return the canonical instance of the default provider. */ return defaultProvider; } }