public class PreferredClassProvider extends RMIClassLoaderSpi
RMIClassLoader
provider that supports preferred
classes.
See the RMIClassLoader
specification and
ClassLoading
for information
about how to install and configure the RMIClassLoader
service provider.
PreferredClassProvider
uses instances of PreferredClassLoader
to load classes from codebase URI paths
supplied to RMIClassLoader.loadClass
methods. In
previous releases only codebase URL paths were permitted.
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
.
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:
getClassAnnotation(ClassLoader)
with the loader.
ClassAnnotation
, the annotation string is the result of invoking
getClassAnnotation
on
the loader.
URLClassLoader
, the annotation string is a space-separated list of
the URLs returned by an invocation of getURLs
on the loader.
getClassAnnotation(ClassLoader)
with the loader.
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 URIs and a parent class loader. In previous releases keys utilised
URL
, but now utilise Uri
by default. The following property
-Dnet.jini.loader.codebaseAnnotation=URL
may be set from the command line to revert to URL
. 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 behavioural difference between Uri
and URL
when used
in ClassLoader index keys is subtle, URL
remote links rely on DNS to resolve
domain names to IP addresses, for this reason, when using strict URL
codebase annotations, the IP address of each codebase at the time they're resolved
is part of the codebase annotations identity. Uri
identity on the other hand is
determined by RFC3986 normalization and is more flexible the codebase server
to change its IP address or be replicated by other codebase servers
different IP addresses, provided they can be reached by their domain name
address.
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. It is recommended that
URLs be compliant with RFC3986 Syntax. Prior to parsing, any file path
separators converted to '/' and any illegal characters are percentage escaped,
Uri
is used to parse each URL
in compliance with RFC3986, in addition file URL paths are
converted to upper case for case insensitive file systems. The array of
RFC3986 normalised Uris along with the current threads context ClassLoader
is used to locate the correct ClassLoader. After normalisation is complete,
each URL is parsed with the URL(String)
constructor; this could
result in a MalformedURLException
. This path of URLs is the
codebase URL path for the invocation.
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 Uri RFC3986 normalised path is equal to the loader's annotation
Uri RFC3986 normalised 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 Uri RFC3986 normalised 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.
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(In particular, the case ofN
, false,L
)
N
being the binary
name of an array class is supported.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
|
Modifier | Constructor and Description |
---|---|
|
PreferredClassProvider()
Creates a new
PreferredClassProvider . |
protected |
PreferredClassProvider(boolean requireDlPerm)
Creates a new
PreferredClassProvider . |
Modifier and Type | Method and Description |
---|---|
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. |
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.
|
ClassLoader |
getClassLoader(String codebase)
Provides the implementation for
RMIClassLoaderSpi.getClassLoader(String) . |
Class |
loadClass(String codebase,
String name,
ClassLoader defaultLoader)
Provides the implementation for
RMIClassLoaderSpi.loadClass(String,String,ClassLoader) . |
Class |
loadProxyClass(String codebase,
String[] interfaceNames,
ClassLoader defaultLoader)
Provides the implementation of
RMIClassLoaderSpi.loadProxyClass(String,String[],ClassLoader) . |
public PreferredClassProvider()
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.
SecurityException
- if there is a security manager and
the invocation of its checkCreateClassLoader
method failsprotected PreferredClassProvider(boolean requireDlPerm)
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
.
requireDlPerm
- if true
, the class loaders
created by the provider will only define classes with a CodeSource
that is granted DownloadPermission
SecurityException
- if there is a security manager and
the invocation of its checkCreateClassLoader
method failspublic Class loadClass(String codebase, String name, ClassLoader defaultLoader) throws MalformedURLException, ClassNotFoundException
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:
codebase
is null
.
defaultLoader
.
PreferredClassLoader
.
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
.
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
.
loadClass
in class RMIClassLoaderSpi
codebase
- the codebase URL path as a space-separated list
of URLs, or null
name
- the binary name of the class to loaddefaultLoader
- additional contextual class loader
to use, or null
Class
object representing the loaded classMalformedURLException
- if codebase
is
non-null
and contains an invalid URLClassNotFoundException
- if a definition for the class
could not be loadedpublic String getClassAnnotation(Class cl)
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.
getClassAnnotation
in class RMIClassLoaderSpi
cl
- the class to obtain the annotation string fornull
protected String getClassAnnotation(ClassLoader 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).
loader
- the class loader to obtain the annotation string
fornull
public ClassLoader getClassLoader(String codebase) throws MalformedURLException
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.
getClassLoader
in class RMIClassLoaderSpi
codebase
- the codebase URL path as a space-separated list
of URLs, or null
MalformedURLException
- if codebase
is
non-null
and contains an invalid URLSecurityException
- 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 pathpublic Class loadProxyClass(String codebase, String[] interfaceNames, ClassLoader defaultLoader) throws MalformedURLException, ClassNotFoundException
RMIClassLoaderSpi.loadProxyClass(String,String[],ClassLoader)
.
PreferredClassProvider
implements this method
as follows:
If defaultLoader
is not null
and
any of the following conditions are true:
codebase
is null
.
defaultLoader
.
PreferredClassLoader
.
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
.
interfaces
using
defaultLoader
. If all of the interfaces are
loaded successfully, then
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
.
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
.
public
interfaces defined by different class loaders): this method
throws a LinkageError
.
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
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
.
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
.
public
interfaces defined by different class loaders): this method
throws a LinkageError
.
ClassNotFoundException
, this method throws a
ClassNotFoundException
.loadProxyClass
in class RMIClassLoaderSpi
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 implementdefaultLoader
- additional contextual class loader to use,
or null
MalformedURLException
- if codebase
is
non-null
and contains an invalid URLClassNotFoundException
- 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)protected ClassLoader createClassLoader(URL[] urls, ClassLoader parent, boolean requireDlPerm)
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.
urls
- the path of URLs to load classes and resources fromparent
- the parent class loader for delegationrequireDlPerm
- if true
, the loader must only
define classes with a CodeSource
that is granted
DownloadPermission
Copyright 2007-2013, multiple authors.
Licensed under the Apache License, Version 2.0, see the NOTICE file for attributions.