Hi, I have a few questions for the changes. In OPENEJB-1624, I made some
changes below, since they might be different keys among various JDK, while
not just use a list to host all those keys ? And use the url from jar file
to determine whether the jar file is required to remove from the cache.
Could you please show me what I missed ? I noticed some
ConcurrentModifiedException sometimes, is it the issue you found ?
Also, it looks to me that some thing might be wrong with the current codes :
a. Should not close jar file while iteratoring the fileCache, as from the
design of the methods, only those jar files belong to the specified folder
should be cleared.
b. Since there might be different key types in the fileCache map, it always
use the string key to remove it from the fileCache later, is it correct ?
--->
Map<Object, JarFile> fileCache = (Map<Object, JarFile>)
fileCacheField.get(null);
Map<JarFile, Object> ucf = (Map<JarFile, Object>) urlCacheField.get(null);
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());
}
}
for(Object key : removedKeys) {
JarFile jarFile = fileCache.remove(key);
if(jarFile != null) {
ucf.remove(jarFile);
try {
jarFile.close();
} catch (Throwable e) {
//Ignore
}
}
2011/7/14 <[email protected]>
> 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);
>
>
>
--
Ivan