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