Author: andygumbrecht
Date: Thu Jul 14 15:13:14 2011
New Revision: 1146747

URL: http://svn.apache.org/viewvc?rev=1146747&view=rev
Log:
Revert breaking change and improve comments on clearSunJarFileFactoryCacheImpl 
to avoid the same.

Due to several different implementation changes in various JDK releases
the 'item' can be one of the following (so far):
1. A URL that points to a file.
2. An ASCII String URI that points to a file
3. A String that is used as a key to a JarFile

Sun/Oracle may continue to change the implementation.
In JDK 7 there are new features to unload jars and classes.

Modified:
    
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/ClassLoaderUtil.java

Modified: 
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/ClassLoaderUtil.java
URL: 
http://svn.apache.org/viewvc/openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/ClassLoaderUtil.java?rev=1146747&r1=1146746&r2=1146747&view=diff
==============================================================================
--- 
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/ClassLoaderUtil.java
 (original)
+++ 
openejb/trunk/openejb3/container/openejb-core/src/main/java/org/apache/openejb/ClassLoaderUtil.java
 Thu Jul 14 15:13:14 2011
@@ -17,6 +17,12 @@
  */
 package org.apache.openejb;
 
+import org.apache.openejb.core.TempClassLoader;
+import org.apache.openejb.util.LogCategory;
+import org.apache.openejb.util.Logger;
+import org.apache.openejb.util.URLs;
+import org.apache.openejb.util.UrlCache;
+
 import java.beans.Introspector;
 import java.io.File;
 import java.io.ObjectInputStream;
@@ -24,26 +30,14 @@ import java.io.ObjectOutputStream;
 import java.io.ObjectStreamClass;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
+import java.net.URI;
 import java.net.URL;
 import java.net.URLClassLoader;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
-import java.util.ArrayList;
-import java.util.ConcurrentModificationException;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.Vector;
+import java.util.*;
 import java.util.jar.JarFile;
 
-import org.apache.openejb.core.TempClassLoader;
-import org.apache.openejb.util.LogCategory;
-import org.apache.openejb.util.Logger;
-import org.apache.openejb.util.UrlCache;
-
 /**
  * @version $Revision$ $Date$
  */
@@ -281,8 +275,7 @@ public class ClassLoaderUtil {
 
     public static URLClassLoader createTempClassLoader(String appId, URL[] 
urls, ClassLoader parent) {
         URLClassLoader classLoader = createClassLoader(appId, urls, parent);
-        TempClassLoader tempClassLoader = new TempClassLoader(classLoader);
-        return tempClassLoader;
+        return new TempClassLoader(classLoader);
     }
 
     /**
@@ -302,41 +295,121 @@ public class ClassLoaderUtil {
         clearSunJarFileFactoryCacheImpl(jarLocation, 5);
     }
 
+    /**
+     * Due to several different implementation changes in various JDK releases 
the code here is not as
+     * straight forward as reflecting debug items in your current runtime. 
There have even been breaking changes
+     * between 1.6 runtime builds, let alone 1.5.
+     * <p/>
+     * If you discover a new issue here please be careful to ensure the 
existing functionality is 'extended' and not
+     * just replaced to match your runtime observations.
+     * <p/>
+     * If you want to look at the mess that leads up to this then follow the 
source code changes made to
+     * the class sun.net.www.protocol.jar.JarFileFactory over several years.
+     *
+     * @param jarLocation String
+     * @param attempt     int
+     */
     private static void clearSunJarFileFactoryCacheImpl(final String 
jarLocation, final int attempt) {
         logger.debug("Clearing Sun JarFileFactory cache for directory " + 
jarLocation);
 
         try {
-            Class<?> jarFileFactory = 
Class.forName("sun.net.www.protocol.jar.JarFileFactory");
+            final Class jarFileFactory = 
Class.forName("sun.net.www.protocol.jar.JarFileFactory");
 
-            synchronized (jarFileFactory) {
+            synchronized (jarFileFactory.this) {
 
                 Field fileCacheField = 
jarFileFactory.getDeclaredField("fileCache");
 
                 fileCacheField.setAccessible(true);
-                Map<Object, JarFile> fileCache = (Map<Object, JarFile>) 
fileCacheField.get(null);
+                Map fileCache = (Map) fileCacheField.get(null);
 
                 Field urlCacheField = 
jarFileFactory.getDeclaredField("urlCache");
                 urlCacheField.setAccessible(true);
+                Map ucf = (Map) urlCacheField.get(null);
+
+                List<URL> urls = new ArrayList<URL>();
+                File file;
+                URL url;
+
+                for (final Object item : fileCache.keySet()) {
+
+                    //Due to several different implementation changes in 
various JDK releases
+                    //the 'item' can be one of the following (so far):
+                    //1. A URL that points to a file.
+                    //2. An ASCII String URI that points to a file
+                    //3. A String that is used as a key to a JarFile
+
+                    url = null;
+
+                    if (item instanceof URL) {
+                        url = (URL) item;
+                    } else if (item instanceof String) {
+
+                        JarFile jf = (JarFile) fileCache.get(item);
+
+                        if (null != jf) {
+                            url = (URL) ucf.get(jf);
+                            jf.close();
+                        } else {
+                            //This may now also be a plain ASCII URI in later 
JDKs
+                            try {
+                                url = new URI((String) item).toURL();
+                            } catch (Exception e) {
+                                logger.warning("Don't know how to handle 
object: " + item.toString() + " of type: " + item.getClass().getCanonicalName() 
+ " from Sun JarFileFactory cache, skipping");
+                                continue;
+                            }
+                        }
 
-                Map<JarFile, Object> ucf = (Map<JarFile, Object>) 
urlCacheField.get(null);
+                    } else {
+                        logger.warning("Don't know how to handle object: " + 
item.toString() + " of type: " + item.getClass().getCanonicalName() + " in Sun 
JarFileFactory cache, skipping");
+                        continue;
+                    }
 
-                List<Object> removedKeys = new ArrayList<Object>();
-                for (Map.Entry<Object, JarFile> entry : fileCache.entrySet()) {
-                    if (isParent(jarLocation, new 
File(entry.getValue().getName()))) {
-                        removedKeys.add(entry.getKey());
+                    file = null;
+                    try {
+                        file = URLs.toFile(url);
+                    } catch (IllegalArgumentException e) {
+                        //unknown kind of url
+                        return;
+                    }
+                    if (null != file && null != url && isParent(jarLocation, 
file)) {
+                        urls.add(url);
                     }
                 }
 
-                for(Object key : removedKeys) {
-                    JarFile jarFile = fileCache.remove(key);
-                    if(jarFile != null) {
-                        ucf.remove(jarFile);
-                        try {
-                            jarFile.close();
-                        } catch (Throwable e) {
-                            //Ignore
+                JarFile jarFile;
+                String key;
+                for (final URL jar : urls) {
+
+                    //Fudge together a sun.net.www.protocol.jar.JarFileFactory 
compatible key option...
+                    key = ("file:///" + new 
File(URI.create(jar.toString())).getAbsolutePath().replace('\\', '/'));
+                    jarFile = (JarFile) fileCache.remove(key);
+
+                    if (jarFile == null) {
+
+                        //Try next known option...
+                        key = jar.toExternalForm();
+                        jarFile = (JarFile) fileCache.remove(key);
+
+                        if (jarFile == null) {
+
+                            //Try next known option...
+                            jarFile = (JarFile) fileCache.remove(jar);
+
+                            if (jarFile == null) {
+                                //To be continued...
+                                //If you find another 'fileCache.remove(?)' 
option then add it here.
+                                continue;
+                            }
                         }
                     }
+
+                    ucf.remove(jarFile);
+
+                    try {
+                        jarFile.close();
+                    } catch (Throwable e) {
+                        //Ignore
+                    }
                 }
             }
         } catch (ConcurrentModificationException e) {
@@ -374,8 +447,8 @@ public class ClassLoaderUtil {
      * @param clazz     the name of the class containing the cache field
      * @param fieldName the name of the cache field
      */
-    public static void clearSunSoftCache(Class clazz, String fieldName) {
-        synchronized (clazz) {
+    public static void clearSunSoftCache(final Class clazz, String fieldName) {
+        synchronized (clazz.this) {
             try {
                 Field field = clazz.getDeclaredField(fieldName);
                 field.setAccessible(true);


Reply via email to