/*
* Copyright (c) 1996, 2004, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package java.rmi.server;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Iterator;
import java.util.ServiceLoader;
/**
* 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 * *
The service provider instance is chosen as follows: * *
java.rmi.server.RMIClassLoaderSpi
is defined, then if
* its value equals the string "default"
, the provider
* instance will be the value returned by an invocation of the {@link
* #getDefaultProviderInstance()} method, and for any other value, if
* a class named with the value of the property can be loaded by the
* system class loader (see {@link ClassLoader#getSystemClassLoader})
* and that class is assignable to {@link RMIClassLoaderSpi} and has a
* public no-argument constructor, then that constructor will be
* invoked to create the provider instance. If the property is
* defined but any other of those conditions are not true, then an
* unspecified Error
will be thrown to code that attempts
* to use RMIClassLoader
, indicating the failure to
* obtain a provider instance.
*
* META-INF/services/java.rmi.server.RMIClassLoaderSpi
is
* visible to the system class loader, then the contents of that
* resource are interpreted as a provider-configuration file, and the
* first class name specified in that file is used as the provider
* class name. If a class with that name can be loaded by the system
* class loader and that class is assignable to {@link
* RMIClassLoaderSpi} and has a public no-argument constructor, then
* that constructor will be invoked to create the provider instance.
* If the resource is found but a provider cannot be instantiated as
* described, then an unspecified Error
will be thrown to
* code that attempts to use RMIClassLoader
, indicating
* the failure to obtain a provider instance.
*
* 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
*
*
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: * *
* ** * @return the canonical instance of the default service provider * * @throws SecurityException if there is a security manager and the * invocation of itsThe {@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: * ** *
* *- If the class loader is the system class loader (see * {@link ClassLoader#getSystemClassLoader}), a parent of the * system class loader such as the loader used for installed * extensions, or the bootstrap class loader (which may be * represented by
null
), then the value of the *java.rmi.server.codebase
property (or possibly an * earlier cached value) is returned, or *null
is returned if that property is not set. * *- Otherwise, if the class loader is an instance of *
URLClassLoader
, then the returned string is a * space-separated list of the external forms of the URLs returned * by invoking thegetURLs
methods of the loader. If * theURLClassLoader
was created by this provider to * service an invocation of itsloadClass
or *loadProxyClass
methods, then no permissions are * required to get the associated codebase string. If it is an * arbitrary otherURLClassLoader
instance, then if * there is a security manager, itscheckPermission
* method will be invoked once for each URL returned by the *getURLs
method, with the permission returned by * invokingopenConnection().getPermission()
on each * URL; if any of those invocations throws a *SecurityException
or anIOException
, * then the value of thejava.rmi.server.codebase
* property (or possibly an earlier cached value) is returned, or *null
is returned if that property is not set. * *- Finally, if the class loader is not an instance of *
URLClassLoader
, then the value of the *java.rmi.server.codebase
property (or possibly an * earlier cached value) is returned, or *null
is returned if that property is not set. * *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 thecodebase
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 thecodebase
argument isnull
, * 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 * specifiedname
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 thanClassNotFoundException
is * thrown, that exception is thrown to the caller. * *Next, the
loadClass
method attempts to load the * class with the specifiedname
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, * ** *
* *- if all of the resolved interfaces are
public
, * then it first attempts to obtain a dynamic proxy class (using * {@link * java.lang.reflect.Proxy#getProxyClass(ClassLoader,Class[]) * Proxy.getProxyClass}) for the resolved interfaces defined in * the codebase loader; if that attempt throws an *IllegalArgumentException
, it then attempts to * obtain a dynamic proxy class for the resolved interfaces * defined in thedefaultLoader
. If both attempts * throwIllegalArgumentException
, then this method * throws aClassNotFoundException
. If any other * exception is thrown, that exception is thrown to the caller. * *- if all of the non-
public
resolved interfaces * are defined in the same class loader, then it attempts to * obtain a dynamic proxy class for the resolved interfaces * defined in that loader. * *- otherwise, a
LinkageError
is thrown (because a * class that implements all of the specified interfaces cannot be * defined in any loader). * *Otherwise, if all of the named interfaces can be resolved * through the codebase loader, then, * *
* *
* *- if all of the resolved interfaces are
public
, * then it attempts to obtain a dynamic proxy class for the * resolved interfaces in the codebase loader. If the attempt * throws anIllegalArgumentException
, then this * method throws aClassNotFoundException
. * *- if all of the non-
public
resolved interfaces * are defined in the same class loader, then it attempts to * obtain a dynamic proxy class for the resolved interfaces * defined in that loader. * *- otherwise, a
LinkageError
is thrown (because a * class that implements all of the specified interfaces cannot be * defined in any loader). * *Otherwise, a
ClassNotFoundException
is thrown * for one of the named interfaces that could not be resolved. * *
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 extends RMIClassLoaderSpi> providerClass =
Class.forName(providerClassName, false,
ClassLoader.getSystemClassLoader())
.asSubclass(RMIClassLoaderSpi.class);
return 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 installed:
*/
Iterator