/*
* Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.rmi.server;
import java.net.MalformedURLException;
import java.net.URL;
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.
*
*
The implementation of the following static methods * *
The service provider instance is chosen as follows: * *
"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.
*
* @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
*
* @since 1.3
*/
public static ClassLoader getClassLoader(String codebase)
throws MalformedURLException
{
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
*
*
The default service provider instance implements * {@link RMIClassLoaderSpi} as follows: * *
* ** * @return the canonical instance of the default service provider * * @since 1.4 */ public static RMIClassLoaderSpi getDefaultProviderInstance() { return defaultProvider; } /** * Always returns null. * * @param loader a class loader from which to get the security context * * @return null * * @deprecated no replacement. This method has no purpose in the absence * of a Security Manager. */ @Deprecated public static Object getSecurityContext(ClassLoader loader) { return null; } /** * 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); @SuppressWarnings("deprecation") RMIClassLoaderSpi result = providerClass.newInstance(); return result; } 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) { throw new LinkageError( "provider class not assignable to RMIClassLoaderSpi", e); } } /* * Next look for a provider configuration file installed: */ IteratorThe {@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 * {@systemProperty java.rmi.server.codebase} property (or possibly an * earlier cached value) is returned, or *nullis 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 thegetURLsmethods of the loader. * *Finally, if the class loader is not an instance of *
URLClassLoader, then the value of the *java.rmi.server.codebaseproperty (or possibly an * earlier cached value) is returned, or *nullis returned if that property is not set. * *For the implementations of the methods described below, * which all take a
Stringparameter named *codebasethat is a space-separated list of URLs, * thecodebaseargument is ignored. Class loading * proceeds using the the current thread's context class loader * (see {@link Thread#getContextClassLoader()}), which is also * considered to be the codebase loader, irrespective of any * value passed as thecodebaseargument. * *The {@link RMIClassLoaderSpi#getClassLoader(String) * getClassLoader} method returns the current thread's * context class loader. * *
The {@link * RMIClassLoaderSpi#loadClass(String,String,ClassLoader) * loadClass} method attempts to load the class with the * specified name as follows: * *
* * If the* *defaultLoaderargument is * non-null, it first attempts to load the class with the * specifiednameusing the *defaultLoader, as if by evaluating * ** Class.forName(name, false, defaultLoader) ** * If the class is successfully loaded from the *defaultLoader, that class is returned. If an * exception other thanClassNotFoundExceptionis * thrown, that exception is thrown to the caller. * *Next, the
loadClassmethod attempts to load the * class with the specifiednameusing the current * thread's context class 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
defaultLoaderargument is * non-nulland 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-
publicresolved 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
LinkageErroris 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-
publicresolved 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
LinkageErroris thrown (because a * class that implements all of the specified interfaces cannot be * defined in any loader). * *Otherwise, a
ClassNotFoundExceptionis thrown * for one of the named interfaces that could not be resolved. * *