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;
        }
     }

Reply via email to