|
|||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | ||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |
public interface SharedClassURLClasspathHelper
Shared Class Helper API that stores and finds classes using URL classpaths.
- Description
A SharedClassURLClasspathHelper is obtained by calling getSharedClassURLClasspathHelper(ClassLoader, URL[]) on a SharedClassHelperFactory.
The SharedClassURLClasspathHelper is designed for ClassLoaders which load classes using a classpath comprised of a sequence of URLs.
It is assumed that classpaths are searched left-to-right and can only be modified in predicatable ways (see below).
- Usage
The ClassLoader should call findSharedClass after asking its parent (if one exists).
If findSharedClass does not return null, the ClassLoader should call defineClass on the byte[] returned.
The ClassLoader should call storeSharedClass immediately after a class has been defined, but NOT if the class being defined was loaded from the shared cache.
The ClassLoader MUST keep the helper up-to-date with regards to classpath changes. If its classpath is appended to, it should call addClasspathEntry(URL) on the helper.
If its classpath is modified in any other way, it should call setClasspath(URL[]) (see below).
The ClassLoader is responsible for coordinating the creation and use of partition Strings, if partitions are required (see section below).
Classes can only be stored using URLs which have "file" or "jar" protocols and which refer to existing resources.
- Using Classpaths
When a findSharedClass request is made from a SharedClassURLClasspathHelper, the shared cache will decide whether or not to return a class by comparing the caller's classpath to the classpath or URL that the class was stored against. Classpaths do not have to explicitly match.
For example, a class c1 loaded from c.jar and stored using classpath a.jar;b.jar;c.jar;d.jar may be found using the following classpaths:
a.jar;c.jar ... (we know that a.jar does not contain a c1)
b.jar;c.jar ... (we know that b.jar does not contain a c1)
b.jar;a.jar;c.jar ... (likewise in whatever order)
c.jar ...
However, it will not be found using the following claspath:
d.jar;a.jar;b.jar;c.jar ... (we have no idea whether a different c1 exists in d.jar or not)
To ensure that findSharedClass returns correct results, it will only return classes which are found for entries upto and including the rightmost
"confirmed" entry. The definition of a confirmed entry is that it has been opened and read by the ClassLoader. Entries are confirmed by calling
storeSharedClass (every entry upto and including the index used is confirmed) or by calling confirmAllEntries(). The latter should only be used if
the classpath is guaranteed not to change (but can still be appended to).
Note that if findSharedClass returns null for whatever reason (even though the cache has the required class), calling storeSharedClass(...)
on the class loaded from disk is the correct thing to do. This will confirm the classpath entry and the cache will not store a duplicate class.
In this way, classes can be shared as effectively as possible between classloaders using different classpaths or URL helper types.
Note that two identical classes are never stored twice in the class cache, but many entries may exist for the same class.
Eg. If class X is stored from a.jar and also from b.jar, the class will exist once in the cache, but will have 2 entries.
- Modifying Classpaths
It is possible that the classpath of a ClassLoader may change after it is initially set. There is a jar extension mechanism, for example,
that allows a jar to define a lookup order of other jars within its manifest. If this causes the classpath to change, then the helper
MUST be informed of the update.
Once an initial classpath has been set in getSharedClassURLClasspathHelper, classpath entries in it are "confirmed" as classes are stored in the cache
from these entries. Eg. Given classpath a.jar;b.jar;c.jar;d.jar, if a class is stored from b.jar then both a.jar and b.jar are confirmed
because the ClassLoader must have opened both of them.
Once classpath entries are confirmed, they cannot be changed, so setClasspath may only make changes to parts of the classpath which are not yet confirmed.
Eg. In the above case, a.jar;b.jar;x.jar;c.jar is acceptable, wheras a.jar;x.jar;b.jar;c.jar;d.jar is not. If an attempt is made to make
changes to confirmed classpath entries, a CannotSetClasspathException is thrown.
- Dynamic Cache Updates
Since the shared cache is persistent beyond the lifetime of a JVM, classes in the shared cache may become out-of-date ("stale").
Classes in the cache are automatically kept up-to-date by default:
If findSharedClass is called for a class which exists in the cache but which has been updated on the filesystem since it was stored,
then the old version in the cache is automatically marked stale and findSharedClass will return null.
The ClassLoader should then store the updated version of the class.
Note that jar/zip updates will cause all cache entries loaded from that jar/zip to be marked stale.
Since a classpath has a specific search order, an update to a jar/zip will also cause ALL classes
loaded from URLs to the right of that entry to also become stale.
This is because the updated zip/jar may now contain classes which "hide" other classes to the right of it in the classpath.
Eg. Class c1 is loaded from c.jar and is stored in the cache using classpath a.jar;b.jar;c.jar.
a.jar is then updated to include a version of c1 and findSharedClass is called for c1.
It is essential that findSharedClass does not then return the version of c1 in c.jar.
It will detect the change, return null and c1 from a.jar should be then stored.
(This behaviour can be disabled using the correct command-line option, but this is not recommended. See -Xshareclasses:help)
It is also assumed that once a jar/zip has been opened, the ClassLoader will maintain a read lock on that file during its lifetime,
preventing its modification. This prevents the cache from having to constantly check for updates.
However, it is understood that non-existent jars/zips on a classpath can only be locked if/when they exist.
- Partitions
A partition may be used when finding or storing a class, which allows modified versions of the same class
to be stored in the cache, effectively creating "partitions" in the cache.
Partitions are designed for bytecode modification such as the use of Aspects. It is the responsibility of the ClassLoader
to create partitions which describe the type of modification performed on the class bytes.
If a class is updated on the filesystem and automatic dynamic updates are enabled, then all versions of the class across
all partitions will be marked stale.
- Class Metadata
A ClassLoader may create metadata when loading and defining classes, such as a jar manifest or security data. None of this metadata may be stored in the cache, so if a ClassLoader is finding classes in the shared cache, it must load any metadata that it needs from disk before defining the classes.
Example:
For static metadata specific to URL classpath entries (such as Manifest/CodeSource information), a suggested solution is to create
a local array to cache the metadata when it is loaded from disk. When findSharedClass is called and an index is returned, look to see
if metadata is already cached in the local array for that index. If it is, define the class... if not, load the class from disk to obtain
the metadata, cache it in the local array, define the class and then call storeSharedClass on it (it doesn't matter if it is already in the cache).
All future results from findSharedClass for that classpath entry can then use the cached metdata.
If findSharedClass returns null, then load the class from disk, cache the metadata from the entry anyway, define the class and store it.
- Security
A SharedClassHelper will only allow classes to be stored in the cache which were defined by the ClassLoader that owns the SharedClassHelper.
If a SecurityManager is installed, SharedClassPermissions must be used to permit read/write access to the shared class cache.
Permissions are granted by ClassLoader classname in the java.policy file and are fixed when the SharedClassHelper is created.
Note also that if the createClassLoader RuntimePermission is not granted, ClassLoaders cannot be created
which in turn means that SharedClassHelpers cannot be created.
- Efficient use of the SharedClassURLClasspathHelper
Here are some recommendations on using the SharedClassURLClasspathHelper:
1) It is best not to start with a zero length classpath and gradually grow it. Each classpath change causes a new classpath to be added to the
cache and reduces the number of optimizations that can be made when matching classpaths.
2) If the ClassLoader will never call setClasspath, then use confirmAllEntries immediately after obtaining the helper. This will ensure that
findSharedClass will never return null due to unconfirmed entries.
- Compatibility with other SharedClassHelpers
Classes stored using the SharedClassURLClasspathHelper can be retrieved using the SharedClassURLHelper and vice-versa. This is also true for partitions which can be used across these two helpers.
SharedClassURLClasspathHelper
,
SharedClassHelperFactory
,
SharedClassPermission
Nested Class Summary | |
---|---|
static interface |
SharedClassURLClasspathHelper.IndexHolder
Interface which allows an index to be returned from findSharedClass calls. |
Method Summary | |
---|---|
void |
addClasspathEntry(java.net.URL cpe)
Update the helper's classpath by appending a URL (see "Usage" above). |
void |
confirmAllEntries()
Confirms all entries in the current classpath. |
byte[] |
findSharedClass(java.lang.String className,
SharedClassURLClasspathHelper.IndexHolder indexFoundAt)
Find a class in the shared cache using the class name given (implicitly using the caller's classpath). |
byte[] |
findSharedClass(java.lang.String partition,
java.lang.String className,
SharedClassURLClasspathHelper.IndexHolder indexFoundAt)
Find a class in the shared cache using the class name and partition given (implicitly using the caller's classpath). |
void |
setClasspath(java.net.URL[] newClasspath)
Update the helper's classpath with a new classpath. |
boolean |
storeSharedClass(java.lang.Class clazz,
int foundAtIndex)
Store a class in the shared cache using the caller's URL classpath. |
boolean |
storeSharedClass(java.lang.String partition,
java.lang.Class clazz,
int foundAtIndex)
Store a class in the shared cache using the caller's URL classpath and with a user-defined partition. |
Methods inherited from interface com.ibm.oti.shared.SharedClassHelper |
---|
getSharingFilter, setSharingFilter |
Methods inherited from interface com.ibm.oti.shared.SharedHelper |
---|
getClassLoader |
Method Detail |
---|
byte[] findSharedClass(java.lang.String className, SharedClassURLClasspathHelper.IndexHolder indexFoundAt)
See "Using Classpaths" above for rules on when a class will be found
Null is returned if the class cannot be found, if it is stale (see "Dynamic Cache Updates" above)
or if it is found for an unconfirmed entry (see "Using Classpaths" above).
className
- String.
The name of the class to be foundindexFoundAt
- IndexHolder.
The index in the caller ClassLoader's classpath at which the class was found.
This parameter can be null if this data is not needed.
byte[] findSharedClass(java.lang.String partition, java.lang.String className, SharedClassURLClasspathHelper.IndexHolder indexFoundAt)
See "Finding Classes" above for rules on when a class will be found
Null is returned if the class cannot be found, if it is stale (see "Dynamic Cache Updates" above)
or if it is found for an unconfirmed entry (see "Using Classpaths" above).
partition
- String.
User-defined partition if finding modified bytecode (see "Partitions" above).
Passing null is equivalent of calling non-partition findSharedClass call.className
- String.
The name of the class to be foundindexFoundAt
- IndexHolder.
The index in the caller ClassLoader's classpath at which the class was found.
This parameter can be null if this data is not needed.
boolean storeSharedClass(java.lang.Class clazz, int foundAtIndex)
The class being stored must have been defined by the caller ClassLoader and must exist in the URL location specified.
Returns true if the class is stored successfully or false otherwise.
Will return false if the class being stored was not defined by the caller ClassLoader.
Also returns false if the URL at foundAtIndex is not a file URL or if the resource it refers to does not exist.
clazz
- Class.
The class to store in the shared cachefoundAtIndex
- int.
The index in the caller's classpath where the class was loaded from (first entry is 0).
boolean storeSharedClass(java.lang.String partition, java.lang.Class clazz, int foundAtIndex)
The class being stored must have been defined by the caller ClassLoader and must exist in the URL location specified.
Returns true if the class is stored successfully or false otherwise.
Will return false if the class being stored was not defined by the caller ClassLoader.
Also returns false if the URL at foundAtIndex is not a file URL or if the resource it refers to does not exist.
partition
- String.
User-defined partition if storing modified bytecode (see "Partitions" above).
Passing null is equivalent of calling non-partition storeSharedClass call.clazz
- Class.
The class to store in the shared cachefoundAtIndex
- int.
The index in the caller's classpath where the class was loaded from (first entry is 0).
void addClasspathEntry(java.net.URL cpe)
Note: It is ESSENTIAL that the helper's classpath is kept up-to-date with the classloader's.
cpe
- URL.
The classpath entry to append to the classpathvoid setClasspath(java.net.URL[] newClasspath) throws CannotSetClasspathException
This function is useful for ClassLoaders that compute their classpath lazily. The initial classpath
is passed to the constructor optimistically, but if the classloader discovers a change while reading
an entry, it can update the classpath using this function.
Note: It is ESSENTIAL that the helper's classpath is kept up-to-date with the classloader's.
The classpath passed to this function must be exactly the same as the original
classpath upto and including the rightmost entry that classes have been loaded from (the righmost "confirmed" entry).
Throws a CannotSetClasspathException if this is not the case (see "Modifying Classpaths" above).
Once the classpath has been updated, any indexes passed to storeSharedClass and returned from findSharedClass correspond to the new classpath.
newClasspath
- The new URL classpath array
CannotSetClasspathException
void confirmAllEntries()
Note that if all entries are confirmed, setClasspath cannot be used to modify the classpath, only to append new entries.
(see "Efficient use..." above).
|
|||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | ||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |