All,
Attached you will find a patch for RIVER-147 and RIVER-265. Note that
RIVER-147 represent a new protected method on a class in the net.jini
namespace. To me this a public API change in the net.jini namespace so
please all have a good look at it and tell what you think of it.
The enhancements represent the minimal specification of what is referred
to in RIVER-147, but the enhancements represent all that I needed to
support codebase evolution for Seven.
RIVER-265 represents an internal change that is a direct consequence of
the functionality provided by RIVER-147.
Regards,
--
Mark Brouwer
Index: ../src/net/jini/loader/pref/PreferredClassProvider.java
===================================================================
--- ../src/net/jini/loader/pref/PreferredClassProvider.java (revision
657332)
+++ ../src/net/jini/loader/pref/PreferredClassProvider.java Mon May 19
20:27:26 CEST 2008
@@ -354,12 +354,12 @@
* an empty codebase to achieve the same effect anyway.
*/
private void checkLoader(ClassLoader loader, ClassLoader parent,
- URL[] urls)
+ String codebase, URL[] urls)
{
SecurityManager sm = System.getSecurityManager();
if ((sm != null) && (loader != null) && (loader != parent)) {
- assert urlsMatchLoaderAnnotation(urls, loader);
+ assert isClassDefinedInClassLoader(codebase, loader);
if (loader.getClass() == PreferredClassLoader.class) {
((PreferredClassLoader) loader).checkPermissions();
@@ -481,7 +481,6 @@
}
URL[] codebaseURLs = pathToURLs(codebase); // may be null
- // throws MalformedURLException
/*
* Process array class names.
@@ -509,7 +508,7 @@
SecurityManager sm = System.getSecurityManager();
if (defaultLoader != null &&
(sm == null || codebaseURLs == null ||
- urlsMatchLoaderAnnotation(codebaseURLs, defaultLoader)))
+ isClassDefinedInClassLoader(codebase, defaultLoader)))
{
try {
Class c = Class.forName(name, false, defaultLoader);
@@ -532,7 +531,8 @@
logger.log(Level.FINEST,
"(thread context class loader: {0})", contextLoader);
}
- ClassLoader codebaseLoader = lookupLoader(codebaseURLs, contextLoader);
+ ClassLoader codebaseLoader = lookupLoader(codebase, codebaseURLs,
+ contextLoader);
/*
* Try remaining defaultLoader cases that don't require
@@ -560,7 +560,8 @@
SecurityException secEx = null;
if (sm != null) {
try {
- checkLoader(codebaseLoader, contextLoader, codebaseURLs);
+ checkLoader(codebaseLoader, contextLoader, codebase,
+ codebaseURLs);
} catch (SecurityException e) {
secEx = e;
}
@@ -769,6 +770,37 @@
}
/**
+ * Indicates whether a class with the specified codebase annotation is
+ * defined within the specified class loader, for the purpose of deciding
+ * whether the class originated from the <code>defaultLoader</code> or that
+ * the class is considered a 'class boomerang'.
+ * <p>
+ * This implementation will consider a class to be defined in the specified
+ * class loader when the codebase annotation for that class loader matches
+ * the <code>codebase</code> argument passed in.
+ *
+ * @param codebase the codebase URL path as a space-separated list
+ * of URLs, or <code>null</code>
+ * @param loader class loader for which must be verified whether it
+ * represents the class loader from which a class with the codebase
+ * as in <code>codebase</code> could have originated
+ * @return <code>true</code> when the class is defined within the class
+ * loader passed in, <code>false</code> otherwise
+ *
+ * @since 2.2
+ */
+ protected boolean isClassDefinedInClassLoader(String codebase,
+ ClassLoader loader) {
+ try {
+ return Arrays.equals(pathToURLs(codebase),
+ getLoaderAnnotationURLs(loader));
+ }
+ catch (MalformedURLException e) {
+ return false;
+ }
+ }
+
+ /**
* 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
@@ -886,7 +918,6 @@
{
checkInitialized();
URL[] codebaseURLs = pathToURLs(codebase); // may be null
- // throws MalformedURLException
ClassLoader contextLoader = getRMIContextClassLoader();
SecurityManager sm = System.getSecurityManager();
@@ -896,8 +927,9 @@
return contextLoader;
}
- ClassLoader codebaseLoader = lookupLoader(codebaseURLs, contextLoader);
- checkLoader(codebaseLoader, contextLoader, codebaseURLs);
+ ClassLoader codebaseLoader = lookupLoader(codebase, codebaseURLs,
+ contextLoader);
+ checkLoader(codebaseLoader, contextLoader, codebase, codebaseURLs);
return codebaseLoader;
}
@@ -1059,7 +1091,6 @@
}
URL[] codebaseURLs = pathToURLs(codebase); // may be null
- // throws MalformedURLException
/*
* Determine the codebase loader.
@@ -1069,7 +1100,8 @@
logger.log(Level.FINEST,
"(thread context class loader: {0})", contextLoader);
}
- ClassLoader codebaseLoader = lookupLoader(codebaseURLs, contextLoader);
+ ClassLoader codebaseLoader = lookupLoader(codebase, codebaseURLs,
+ contextLoader);
/*
* Check permission to access the codebase loader.
@@ -1078,7 +1110,8 @@
SecurityException secEx = null;
if (sm != null) {
try {
- checkLoader(codebaseLoader, contextLoader, codebaseURLs);
+ checkLoader(codebaseLoader, contextLoader, codebase,
+ codebaseURLs);
} catch (SecurityException e) {
secEx = e;
}
@@ -1094,7 +1127,7 @@
codebaseURLs == null;
if (!tryDL) {
codebaseMatchesDL =
- urlsMatchLoaderAnnotation(codebaseURLs, defaultLoader);
+ isClassDefinedInClassLoader(codebase, defaultLoader);
tryDL = codebaseMatchesDL ||
!(codebaseLoader instanceof PreferredClassLoader) ||
!interfacePreferred((PreferredClassLoader) codebaseLoader,
@@ -1320,18 +1353,6 @@
return pathToURLs(getLoaderAnnotation(loader, false));
}
- /**
- * Returns true if the specified path of URLs is equal to the
- * annotation URLs of the specified loader, and false otherwise.
- **/
- private boolean urlsMatchLoaderAnnotation(URL[] urls, ClassLoader loader) {
- try {
- return Arrays.equals(urls, getLoaderAnnotationURLs(loader));
- } catch (MalformedURLException e) {
- return false;
- }
- }
-
/*
* Load Class objects for the names in the interfaces array from
* the given class loader.
@@ -1389,9 +1410,11 @@
* specified string is null.
*
* @param path the string path to be converted to an array of urls
- * @return 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.
+ * @return the string path converted to an array of URLs, or
+ * <code>null</code> when <code>path</code> is <code>null</code>
+ * @throws MalformedURLException if the string path of URLs contains a
+ * mal-formed URL which can not be converted into a
+ * <code>URL</code> object.
*/
private static URL[] pathToURLs(String path) throws MalformedURLException {
if (path == null) {
@@ -1457,7 +1480,7 @@
* 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
+ * A simple example of a class boomerang occurs 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
@@ -1480,42 +1503,30 @@
* loader for a loader that has an export path which matches the
* parameter path.
*/
- private ClassLoader findOriginLoader(final URL[] pathURLs,
+ private ClassLoader findOriginLoader(final String codebase,
final ClassLoader parent)
{
return (ClassLoader) AccessController.doPrivileged(
new PrivilegedAction() {
public Object run() {
- return findOriginLoader0(pathURLs, parent);
+ return findOriginLoader0(codebase, parent);
}
});
}
- private ClassLoader findOriginLoader0(URL[] pathURLs, ClassLoader parent) {
+ private ClassLoader findOriginLoader0(String codebase, ClassLoader parent)
{
for (ClassLoader ancestor = parent;
ancestor != null;
ancestor = ancestor.getParent())
{
- URL[] ancestorURLs;
- try {
- ancestorURLs = getLoaderAnnotationURLs(ancestor);
- } catch (MalformedURLException e) {
- // this ancestor's annotation must not match pathURLs
- continue;
- }
-
- /* check if found a matching ancestor loader */
- if (Arrays.equals(pathURLs, ancestorURLs)) {
+ // check if found a matching ancestor loader
+ if (isClassDefinedInClassLoader(codebase, ancestor)) {
if (logger.isLoggable(Level.FINEST)) {
logger.log(Level.FINEST,
"using an existing ancestor class loader " +
"which serves the requested codebase urls: {0}, " +
- "urls: {1}",
- new Object[] {
- ancestor,
- (ancestorURLs != null ?
- Arrays.asList(ancestorURLs) : null)
- });
+ "codebase: {1}",
+ new Object[] {ancestor, codebase});
}
return ancestor;
@@ -1529,8 +1540,8 @@
* and the given parent class loader. A new class loader instance
* will be created and returned if no match is found.
*/
- private ClassLoader lookupLoader(final URL[] urls,
- final ClassLoader parent)
+ private ClassLoader lookupLoader(String codebase, URL[] urls,
+ ClassLoader parent)
{
/*
* If the requested codebase is null, then PreferredClassProvider
@@ -1555,7 +1566,6 @@
* return parent;
* }
*/
-
synchronized (loaderTable) {
/*
* Take this opportunity to remove from the table entries
@@ -1564,8 +1574,7 @@
Object ref;
while ((ref = refQueue.poll()) != null) {
if (ref instanceof LoaderKey) {
- LoaderKey key = (LoaderKey) ref;
- loaderTable.remove(key);
+ loaderTable.remove((LoaderKey) ref);
} else if (ref instanceof LoaderEntry) {
LoaderEntry entry = (LoaderEntry) ref;
if (!entry.removed) { // ignore entries removed below
@@ -1582,9 +1591,8 @@
LoaderEntry entry = (LoaderEntry) loaderTable.get(key);
ClassLoader loader;
- if (entry == null ||
- (loader = (ClassLoader) entry.get()) == null)
- {
+ if (entry == null || (loader = (ClassLoader) entry.get()) == null) {
+
/*
* If entry was in table but it's weak reference was cleared,
* remove it from the table and mark it as explicitly cleared,
@@ -1612,19 +1620,17 @@
* context restricted to the permissions necessary to
* load classes from its codebase URL path.
*/
- loader = findOriginLoader(urls, parent);
+ loader = findOriginLoader(codebase, parent);
if (loader == null) {
loader = createClassLoader(urls, parent, requireDlPerm);
- }
-
- /*
- * Finally, create an entry to hold the new loader with a
- * weak reference and store it in the table with the key.
- */
+ /*
+ * Finally, create an entry to hold the new loader with a
+ * weak reference and store it in the table with the key.
+ */
- entry = new LoaderEntry(key, loader);
- loaderTable.put(key, entry);
+ loaderTable.put(key, new LoaderEntry(key, loader));
- }
+ }
+ }
return loader;
}
}