Class PreferredClassProvider

java.lang.Object
java.rmi.server.RMIClassLoaderSpi
net.jini.loader.pref.PreferredClassProvider
Direct Known Subclasses:
RequireDlPermProvider

public class PreferredClassProvider extends RMIClassLoaderSpi
An RMIClassLoader provider that supports preferred classes.

See the RMIClassLoader specification for information about how to install and configure the RMIClassLoader service provider.

PreferredClassProvider uses instances of PreferredClassLoader to load classes from codebase URL paths supplied to RMIClassLoader.loadClass methods.

PreferredClassProvider does not enforce DownloadPermission by default, but a subclass can configure it to do so by passing true as the argument to the protected constructor.

By overriding the getClassAnnotation(ClassLoader) method, a subclass can also configure the class annotations to be used for classes defined by the system class loader, its ancestor class loaders, and any class loader that is not an instance of ClassAnnotation or URLClassLoader.

Common Terms and Behaviors

The following section defines terms and describes behaviors common to how PreferredClassProvider implements the abstract methods of RMIClassLoaderSpi. Where applicable, these definitions and descriptions are relative to the instance of PreferredClassProvider on which a method is invoked and the context in which it is invoked.

The annotation string for a class loader is determined by the following procedure:

  • If the loader is the system class loader or an ancestor of the system class loader (including the bootstrap class loader), the annotation string is the result of invoking getClassAnnotation(ClassLoader) with the loader.
  • Otherwise, if the loader is an instance of ClassAnnotation, the annotation string is the result of invoking getClassAnnotation on the loader.
  • Otherwise, if the loader is an instance of URLClassLoader, the annotation string is a space-separated list of the URLs returned by an invocation of getURLs on the loader.
  • Otherwise, the annotation string is the result of invoking getClassAnnotation(ClassLoader) with the loader.
The annotation URL path for a class loader is the path of URLs obtained by parsing the annotation string for the loader as a list of URLs separated by spaces, where each URL is parsed as with the URL(String) constructor; if such parsing would result in a MalformedURLException, then the annotation URL path for the loader is only defined to the extent that it is not equal to any other path of URLs.

A PreferredClassProvider maintains an internal table of class loader instances indexed by keys that comprise a path of URLs and a parent class loader. The table does not strongly reference the class loader instances, in order to allow them (and the classes they have defined) to be garbage collected when they are not otherwise reachable.

The methods loadClass, loadProxyClass, and getClassLoader, which each have a String parameter named codebase, have the following behaviors in common:

  • codebase may be null. If it is not null, it is interpreted as a path of URLs by parsing it as a list of URLs separated by spaces, where each URL is parsed as with the URL(String) constructor; this could result in a MalformedURLException. This path of URLs is the codebase URL path for the invocation.
  • A class loader known as the codebase loader is chosen based on codebase and the current thread's context class loader as follows. If codebase is null, then the codebase loader is the current thread's context class loader. Otherwise, for each non-null loader starting with the current thread's context class loader and continuing with each successive parent class loader, if the codebase URL path is equal to the loader's annotation URL path, then the codebase loader is that loader. If no such matching loader is found, then the codebase loader is the loader in this PreferredClassProvider's internal table with the codebase URL path as the key's path of URLs and the current thread's context class loader as the key's parent class loader. If no such entry exists in the table, then one is created by invoking createClassLoader with the codebase URL path, the current thread's context class loader, and the boolean requireDlPerm value that this PreferredClassProvider was constructed with; the created loader is added to the table, and it is chosen as the codebase loader.
  • The current security context has permission to access the codebase loader if it has the appropriate permission for each of the URLs in the codebase loader's annotation URL path, where the appropriate permission for a URL is defined as follows. If the result of invoking openConnection().getPermission() on the URL object is not a FilePermission or if it is a FilePermission whose name does not contain a directory separator, then that permission is the appropriate permission. If it is a FilePermission whose name contains a directory separator, then the appropriate permission is a FilePermission with action "read" and the same name except with the last path segment replaced with "-" (that is, permission to read all files in the same directory and all subdirectories).

When PreferredClassProvider attempts to load a class (or interface) named N using class loader L, it does so in a manner equivalent to evaluating the following expression:

        Class.forName(N, false, L)
 
In particular, the case of N being the binary name of an array class is supported.
Since:
2.0

This implementation uses the Logger named net.jini.loader.pref.PreferredClassProvider to log information at the following levels:

Level Description
FINE class loading failures
HANDLED exceptions caught during class loading operations
FINE invocations of loadClass and loadProxyClass
FINEST detailed activity of loadClass and loadProxyClass implementations
Author:
Sun Microsystems, Inc.
  • Constructor Details

    • PreferredClassProvider

      public PreferredClassProvider()
      Creates a new PreferredClassProvider.

      This constructor is used by the RMIClassLoader service provider location mechanism when PreferredClassProvider is configured as the RMIClassLoader provider class.

      If there is a security manager, its checkCreateClassLoader method is invoked; this could result in a SecurityException.

      DownloadPermission is not enforced by the created provider.

      Throws:
      SecurityException - if there is a security manager and the invocation of its checkCreateClassLoader method fails
    • PreferredClassProvider

      protected PreferredClassProvider(boolean requireDlPerm)
      Creates a new PreferredClassProvider.

      This constructor is used by subclasses to control whether or not DownloadPermission is enforced.

      If there is a security manager, its checkCreateClassLoader method is invoked; this could result in a SecurityException.

      Parameters:
      requireDlPerm - if true, the class loaders created by the provider will only define classes with a CodeSource that is granted DownloadPermission
      Throws:
      SecurityException - if there is a security manager and the invocation of its checkCreateClassLoader method fails
  • Method Details

    • loadClass

      public Class loadClass(String codebase, String name, ClassLoader defaultLoader) throws MalformedURLException, ClassNotFoundException
      Provides the implementation for RMIClassLoaderSpi.loadClass(String, String, ClassLoader).

      PreferredClassProvider implements this method as follows:

      If name is the binary name of an array class (of one or more dimensions) with a primitive element type, this method returns the Class for that array class.

      Otherwise, if defaultLoader is not null and any of the following conditions are true:

      • There is no security manager.
      • The codebase loader is not the current thread's context class loader and the current security context does not have permission to access the codebase loader.
      • codebase is null.
      • The specified codebase URL path is equal to the annotation URL path of defaultLoader.
      • The codebase loader is not an instance of PreferredClassLoader.
      • The codebase loader is an instance of PreferredClassLoader and an invocation of isPreferredResource on the codebase loader with the class name described below as the first argument and true as the second argument returns false. If name is the binary name of an array class (of one or more dimensions) with a element type that is a reference type, the class name passed to isPreferredResource is the binary name of that element type; otherwise, the class name passed to isPreferredResource is name. This invocation is only done if none of the previous conditions are true. If isPreferredResource throws an IOException, this method throws a ClassNotFoundException.
      then this method attempts to load the class with the specified name using defaultLoader. If this attempt succeeds, this method returns the resulting Class; if it throws a ClassNotFoundException, this method proceeds as follows.

      Otherwise, this method attempts to load the class with the specified name using the codebase loader, if there is a security manager and the current security context has permission to access the codebase loader, or using the current thread's context class loader otherwise. If this attempt succeeds, this method returns the resulting Class; if it throws a ClassNotFoundException, this method throws a ClassNotFoundException.

      Specified by:
      loadClass in class RMIClassLoaderSpi
      Parameters:
      codebase - the codebase URL path as a space-separated list of URLs, or null
      name - the binary name of the class to load
      defaultLoader - additional contextual class loader to use, or null
      Returns:
      the Class object representing the loaded class
      Throws:
      MalformedURLException - if codebase is non-null and contains an invalid URL
      ClassNotFoundException - if a definition for the class could not be loaded
    • getClassAnnotation

      public String getClassAnnotation(Class cl)
      Provides the implementation for RMIClassLoaderSpi.getClassAnnotation(Class).

      PreferredClassProvider implements this method as follows:

      If cl is an array class (of one or more dimensions) with a primitive element type, this method returns null.

      Otherwise, this method returns the annotation string for the defining class loader of cl, except that if the annotation string would be determined by an invocation of URLClassLoader.getURLs on that loader and the current security context does not have the permissions necessary to connect to each URL returned by that invocation (where the permission to connect to a URL is determined by invoking openConnection().getPermission() on the URL object), this method returns the result of invoking getClassAnnotation(ClassLoader) with the loader instead.

      Specified by:
      getClassAnnotation in class RMIClassLoaderSpi
      Parameters:
      cl - the class to obtain the annotation string for
      Returns:
      the annotation string for the class, or null
    • getClassAnnotation

      protected String getClassAnnotation(ClassLoader loader)
      Returns the annotation string for the specified class loader.

      This method is invoked in order to determine the annotation string for the system class loader, an ancestor of the system class loader, any class loader that is not an instance of ClassAnnotation or URLClassLoader, or (for an invocation of getClassAnnotation(Class)) a URLClassLoader for which the current security context does not have the permissions necessary to connect to all of its URLs.

      PreferredClassProvider implements this method as follows:

      This method returns the value of the system property "java.rmi.server.codebase" (or possibly an earlier cached value).

      Parameters:
      loader - the class loader to obtain the annotation string for
      Returns:
      the annotation string for the class loader, or null
    • getClassLoader

      public ClassLoader getClassLoader(String codebase) throws MalformedURLException
      Provides the implementation for RMIClassLoaderSpi.getClassLoader(String).

      PreferredClassProvider implements this method as follows:

      If there is a security manager, its checkPermission method is invoked with a RuntimePermission("getClassLoader") permission; this could result in a SecurityException. Also, if there is a security manager, the codebase loader is not the current thread's context class loader, and the current security context does not have permission to access the codebase loader, this method throws a SecurityException.

      This method returns the codebase loader if there is a security manager, or the current thread's context class loader otherwise.

      Specified by:
      getClassLoader in class RMIClassLoaderSpi
      Parameters:
      codebase - the codebase URL path as a space-separated list of URLs, or null
      Returns:
      a class loader for the specified codebase URL path
      Throws:
      MalformedURLException - if codebase is non-null and contains an invalid URL
      SecurityException - if there is a security manager and the invocation of its checkPermission method fails, or if the current security context does not have the permissions necessary to connect to all of the URLs in the codebase URL path
    • loadProxyClass

      public Class loadProxyClass(String codebase, String[] interfaceNames, ClassLoader defaultLoader) throws MalformedURLException, ClassNotFoundException
      Provides the implementation of RMIClassLoaderSpi.loadProxyClass(String, String[], ClassLoader).

      PreferredClassProvider implements this method as follows:

      If defaultLoader is not null and any of the following conditions are true:

      • There is no security manager.
      • The codebase loader is not the current thread's context class loader and the current security context does not have permission to access the codebase loader.
      • codebase is null.
      • The specified codebase URL path is equal to the annotation URL path of defaultLoader.
      • The codebase loader is not an instance of PreferredClassLoader.
      • The codebase loader is an instance of PreferredClassLoader and an invocation of isPreferredResource on the codebase loader for each element of interfaces, with the element as the first argument and true as the second argument, all return false. These invocations are only done if none of the previous conditions are true. If any invocation of isPreferredResource throws an IOException, this method throws a ClassNotFoundException.
      then this method attempts to load all of the interfaces named by the elements of interfaces using defaultLoader. If all of the interfaces are loaded successfully, then
      • If all of the loaded interfaces are public: if there is a security manager, the codebase loader is the current thread's context class loader or the current security context has permission to access the codebase loader, and the annotation URL path for the codebase loader is not equal to the annotation URL path for defaultLoader, this method first attempts to get a dynamic proxy class (using Proxy.getProxyClass) that is defined by the codebase loader and that implements all of the interfaces, and if this attempt succeeds, this method returns the resulting Class. Otherwise, this method attempts to get a dynamic proxy class that is defined by defaultLoader and that implements all of the interfaces. If that attempt succeeds, this method returns the resulting Class; if it throws an IllegalArgumentException, this method throws a ClassNotFoundException.
      • If all of the non-public interfaces are defined by the same class loader: this method attempts to get a dynamic proxy class that is defined by that loader and that implements all of the interfaces. If this attempt succeeds, this method returns the resulting Class; if it throws an IllegalArgumentException, this method throws a ClassNotFoundException.
      • Otherwise (if there are two or more non-public interfaces defined by different class loaders): this method throws a LinkageError.
      If any of the attempts to load one of the interfaces throws a ClassNotFoundException, this method proceeds as follows.

      Otherwise, this method attempts to load all of the interfaces named by the elements of interfaces using the codebase loader, if there is a security manager and the current security context has permission to access the codebase loader, or using the current thread's context class loader otherwise. If all of the interfaces are loaded successfully, then

      • If all of the loaded interfaces are public: this method attempts to get a dynamic proxy class that is defined by the loader used to load the interfaces and that implements all of the interfaces. If this attempt succeeds, this method returns the resulting Class; if it throws an IllegalArgumentException, this method throws a ClassNotFoundException.
      • If all of the non-public interfaces are defined by the same class loader: this method attempts to get a dynamic proxy class that is defined by that loader and that implements all of the interfaces. If this attempt succeeds, this method returns the resulting Class; if it throws an IllegalArgumentException, this method throws a ClassNotFoundException.
      • Otherwise (if there are two or more non-public interfaces defined by different class loaders): this method throws a LinkageError.
      If any of the attempts to load one of the interfaces throws a ClassNotFoundException, this method throws a ClassNotFoundException.
      Specified by:
      loadProxyClass in class RMIClassLoaderSpi
      Parameters:
      codebase - the codebase URL path as a space-separated list of URLs, or null
      interfaceNames - the binary names of the interfaces for the proxy class to implement
      defaultLoader - additional contextual class loader to use, or null
      Returns:
      a dynamic proxy class that implements the named interfaces
      Throws:
      MalformedURLException - if codebase is non-null and contains an invalid URL
      ClassNotFoundException - if a definition for one of the named interfaces could not be loaded, or if creation of the dynamic proxy class failed (such as if Proxy.getProxyClass would throw an IllegalArgumentException for the given interface list)
    • createClassLoader

      protected ClassLoader createClassLoader(URL[] urls, ClassLoader parent, boolean requireDlPerm)
      Creates the class loader for this PreferredClassProvider to use to load classes from the specified path of URLs with the specified delegation parent.

      PreferredClassProvider implements this method as follows:

      This method creates a new instance of PreferredClassLoader that loads classes and resources from urls, delegates to parent, and enforces DownloadPermission if requireDlPerm is true. The created loader uses a restricted security context to ensure that the URL retrieval operations undertaken by the loader cannot exercise a permission that is not implied by the permissions necessary to access the loader as a codebase loader for the specified path of URLs.

      Parameters:
      urls - the path of URLs to load classes and resources from
      parent - the parent class loader for delegation
      requireDlPerm - if true, the loader must only define classes with a CodeSource that is granted DownloadPermission
      Returns:
      the created class loader
      Since:
      2.1