net.jini.loader.pref
Class PreferredClassProvider

java.lang.Object
  extended by java.rmi.server.RMIClassLoaderSpi
      extended by 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:

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:

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
Author:
Sun Microsystems, Inc.
Implementation Specifics:

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

Level Description
FAILED class loading failures
HANDLED exceptions caught during class loading operations
FINE invocations of loadClass and loadProxyClass
FINEST detailed activity of loadClass and loadProxyClass implementations

Nested Class Summary
private  class PreferredClassProvider.LoaderEntry
          Loader table value: a weak reference to a class loader.
private  class PreferredClassProvider.LoaderEntryHolder
           
private  class PreferredClassProvider.LoaderKey
          Loader table key: a codebase URL path and a weak reference to a parent class loader (possibly null).
 
Field Summary
private  Map classLoaderPerms
          Map to hold permissions needed to check the URLs of URLClassLoader objects.
private static String codebaseProperty
          value of "java.rmi.server.codebase" property, as cached at class initialization time.
private static Permission getClassLoaderPermission
           
private  boolean initialized
          true if constructor has completed successfully
private  Map<PreferredClassProvider.LoaderKey,PreferredClassProvider.LoaderEntryHolder> loaderTable
          table mapping codebase URL path and context class loader pairs to class loader instances.
private static Map localLoaders
          table of "local" class loaders
private static Logger logger
          provider logger
private static Map pathToURLsCache
          map from weak(key=string) to [URL[], soft(key)]
private static String PRIMITIVE_TYPES
          encodings for primitive array class element types
private  ReferenceQueue refQueue
          reference queue for cleared class loader entries
private  boolean requireDlPerm
          download from codebases with no dl perm allowed?
 
Constructor Summary
  PreferredClassProvider()
          Creates a new PreferredClassProvider.
protected PreferredClassProvider(boolean requireDlPerm)
          Creates a new PreferredClassProvider.
 
Method Summary
private  void checkInitialized()
           
private  void checkLoader(ClassLoader loader, ClassLoader parent, URL[] urls)
           
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.
private  ClassLoader findOriginLoader(URL[] pathURLs, ClassLoader parent)
          Return the origin class loader for the pathURLs or null if the origin loader was not present in the delegation hierarchy.
private  ClassLoader findOriginLoader0(URL[] pathURLs, ClassLoader parent)
           
 String getClassAnnotation(Class cl)
          Provides the implementation for RMIClassLoaderSpi.getClassAnnotation(Class).
protected  String getClassAnnotation(ClassLoader loader)
          Returns the annotation string for the specified class loader.
private static ClassLoader getClassLoader(Class c)
           
 ClassLoader getClassLoader(String codebase)
          Provides the implementation for RMIClassLoaderSpi.getClassLoader(String).
private  String getLoaderAnnotation(ClassLoader loader, boolean check)
          Returns the annotation string for the specified class loader (possibly null).
private  URL[] getLoaderAnnotationURLs(ClassLoader loader)
          Returns an array of URLs corresponding to the annotation string for the specified class loader, or null if the annotation string is null.
private static String getProxySuccessLogMessage(SecurityManager sm, SecurityException secEx)
           
private static ClassLoader getRMIContextClassLoader()
          Return the class loader to be used as the parent for an RMI class loader used in the current execution context.
private  boolean interfacePreferred(PreferredClassLoader codebaseLoader, String[] interfaceNames, String codebase)
          Returns true if at least one of the specified interface names is preferred for the specified class loader; returns false if none of them are preferred.
private static boolean isLocalLoader(ClassLoader loader)
          Return true if the given loader is the system class loader or its parent (i.e. the loader for installed extensions) or the null class loader
 Class loadClass(String codebase, String name, ClassLoader defaultLoader)
          Provides the implementation for RMIClassLoaderSpi.loadClass(String,String,ClassLoader).
private  Class loadProxyClass(String[] interfaceNames, ClassLoader interfaceLoader, String interfaceLoaderName, ClassLoader otherLoader, boolean tryOtherLoaderFirst)
          Attempts to load the specified interfaces by name using the specified loader, and if that is successful, attempts to get a dynamic proxy class that implements those interfaces.
 Class loadProxyClass(String codebase, String[] interfaceNames, ClassLoader defaultLoader)
          Provides the implementation of RMIClassLoaderSpi.loadProxyClass(String,String[],ClassLoader).
private  ClassLoader loadProxyInterfaces(String[] interfaces, ClassLoader loader, Class[] classObjs, boolean[] useNonpublic)
           
private  ClassLoader lookupLoader(URL[] urls, ClassLoader parent)
          Look up the class loader for the given codebase URL path and the given parent class loader.
private static URL[] pathToURLs(String path)
          Convert a string containing a space-separated list of URLs into a corresponding array of URL objects, throwing a MalformedURLException if any of the URLs are invalid.
private  boolean urlsMatchLoaderAnnotation(URL[] urls, ClassLoader loader)
          Returns true if the specified path of URLs is equal to the annotation URLs of the specified loader, and false otherwise.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

PRIMITIVE_TYPES

private static final String PRIMITIVE_TYPES
encodings for primitive array class element types

See Also:
Constant Field Values

requireDlPerm

private final boolean requireDlPerm
download from codebases with no dl perm allowed?


initialized

private final boolean initialized
true if constructor has completed successfully


logger

private static final Logger logger
provider logger


getClassLoaderPermission

private static final Permission getClassLoaderPermission

codebaseProperty

private static String codebaseProperty
value of "java.rmi.server.codebase" property, as cached at class initialization time. It may contain malformed URLs.


localLoaders

private static final Map localLoaders
table of "local" class loaders


loaderTable

private final Map<PreferredClassProvider.LoaderKey,PreferredClassProvider.LoaderEntryHolder> loaderTable
table mapping codebase URL path and context class loader pairs to class loader instances. Entries hold class loaders with weak references, so this table does not prevent loaders from being garbage collected.


refQueue

private final ReferenceQueue refQueue
reference queue for cleared class loader entries


classLoaderPerms

private final Map classLoaderPerms
Map to hold permissions needed to check the URLs of URLClassLoader objects.


pathToURLsCache

private static Map pathToURLsCache
map from weak(key=string) to [URL[], soft(key)]

Constructor Detail

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 Detail

checkInitialized

private void checkInitialized()

checkLoader

private void checkLoader(ClassLoader loader,
                         ClassLoader parent,
                         URL[] urls)

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:

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

getLoaderAnnotation

private String getLoaderAnnotation(ClassLoader loader,
                                   boolean check)
Returns the annotation string for the specified class loader (possibly null). If check is true and the annotation would be determined from an invocation of URLClassLoader.getURLs() on the loader, only return the true annotation if the current security context has permission to connect to all of the URLs.


isLocalLoader

private static boolean isLocalLoader(ClassLoader loader)
Return true if the given loader is the system class loader or its parent (i.e. the loader for installed extensions) or the null class loader


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:

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 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 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)

getProxySuccessLogMessage

private static String getProxySuccessLogMessage(SecurityManager sm,
                                                SecurityException secEx)

loadProxyClass

private Class loadProxyClass(String[] interfaceNames,
                             ClassLoader interfaceLoader,
                             String interfaceLoaderName,
                             ClassLoader otherLoader,
                             boolean tryOtherLoaderFirst)
                      throws ClassNotFoundException
Attempts to load the specified interfaces by name using the specified loader, and if that is successful, attempts to get a dynamic proxy class that implements those interfaces. If tryOtherLoaderFirst is true, attempts to get the proxy class defined by the specified other loader first, and if that fails, falls back to get the proxy class defined by the same loader used to load the interfaces; otherwise, only attempts to get the proxy class defined by the same loader used to load the interfaces. Throws a ClassNotFoundException if attempting to load any of the interfaces throws a ClassNotFoundException. Throws IllegalArgumentException if the final attempt to get a proxy class throws an IllegalArgumentException.

Throws:
ClassNotFoundException

interfacePreferred

private boolean interfacePreferred(PreferredClassLoader codebaseLoader,
                                   String[] interfaceNames,
                                   String codebase)
                            throws ClassNotFoundException
Returns true if at least one of the specified interface names is preferred for the specified class loader; returns false if none of them are preferred. Throws ClassNotFoundException if PreferredClassLoader.isPreferredResource throws IOException (although isPreferredResource isn't necessarily invoked for all of the specified names, because this method short circuits on the first invocation that returns true). The codebase argument is the original codebase URL path passed to loadProxyClass, which typically but not necessarily equals the loader's import URL path.

Throws:
ClassNotFoundException

getLoaderAnnotationURLs

private URL[] getLoaderAnnotationURLs(ClassLoader loader)
                               throws MalformedURLException
Returns an array of URLs corresponding to the annotation string for the specified class loader, or null if the annotation string is null.

Throws:
MalformedURLException

urlsMatchLoaderAnnotation

private boolean urlsMatchLoaderAnnotation(URL[] urls,
                                          ClassLoader loader)
Returns true if the specified path of URLs is equal to the annotation URLs of the specified loader, and false otherwise.


loadProxyInterfaces

private ClassLoader loadProxyInterfaces(String[] interfaces,
                                        ClassLoader loader,
                                        Class[] classObjs,
                                        boolean[] useNonpublic)
                                 throws ClassNotFoundException
Throws:
ClassNotFoundException

pathToURLs

private static URL[] pathToURLs(String path)
                         throws MalformedURLException
Convert a string containing a space-separated list of URLs into a corresponding array of URL objects, throwing a MalformedURLException if any of the URLs are invalid. This method returns null if the specified string is null.

Parameters:
path - the string path to be converted to an array of urls
Returns:
the string path converted to an array of URLs, or null
Throws:
MalformedURLException - if the string path of urls contains a mal-formed url which can not be converted into a url object.

getRMIContextClassLoader

private static ClassLoader getRMIContextClassLoader()
Return the class loader to be used as the parent for an RMI class loader used in the current execution context.


findOriginLoader

private ClassLoader findOriginLoader(URL[] pathURLs,
                                     ClassLoader parent)
Return the origin class loader for the pathURLs or null if the origin loader was not present in the delegation hierarchy. Preferred classes introduces the "class boomerang" problem to RMI class loading. A class boomerang occurs when a class which is marked preferred is accessible from the codebase of a virtual machine (VM) and is loaded by that VM. Since the VM has a copy of the class in its own resources and the class is "returning to its origin" the class should not be preferred. If the class is preferred, it will be loaded in a class loader for the local codebase annotation. As a result, the class' type will not be compatible with types defined from the local definition of the class file in the relevant VM. A boomerang can also occur when a new child loader for a URL path is created but an ancestor of the new class loader has the same URL path as the new class loader. In such cases the new class loader should not be created. The incoming class should be loaded from the origin ancestor instead. A simple example of a class boomerang occurs when when a VM makes a remote method call to itself. Suppose an object whose class was loaded locally in that VM and is preferred in the codebase of the VM is passed in the call. When the VM receives its own call, an instance of the unmarshalled parameter will not be assignable to instances that were defined by local classes (i.e. never unmarshalled). In order to work around class boomerang problems, the preferred class provider lookupLoader algorithm is different from the analogous algorithm in LoaderHandler. To avoid boomerangs, the lookupLoader method of this class attempts to locate the "origin" class loader of an incoming class in a remote method call. Loading classes from their origin loader instead of in a preferred circumvents type compatibility conflicts. To find origin loaders, the lookupLoader method calls findOriginLoader() before locating or creating new PreferredClassLoaders. An origin loader is found by searching up the delegation hierarchy above the parent (context) class loader for a loader that has an export path which matches the parameter path.


findOriginLoader0

private ClassLoader findOriginLoader0(URL[] pathURLs,
                                      ClassLoader parent)

lookupLoader

private ClassLoader lookupLoader(URL[] urls,
                                 ClassLoader parent)
Look up the class loader for the given codebase URL path and the given parent class loader. A new class loader instance will be created and returned if no match is found.


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

getClassLoader

private static ClassLoader getClassLoader(Class c)


Copyright 2007-2010, multiple authors.
Licensed under the Apache License, Version 2.0, see the NOTICE file for attributions.