package azkaban.project.validator;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Vector;
import java.util.jar.JarFile;
import sun.net.www.protocol.jar.JarURLConnection;
/**
* Workaround for jdk 6 disgrace with open jar files & native libs,
* which is a reason of unrefreshable classloader.
*/
public class ValidatorClassLoader extends URLClassLoader {
protected HashSet<String> setJarFileNames2Close = new HashSet<String>();
public ValidatorClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
public ValidatorClassLoader(URL[] urls) {
super(urls);
}
@Override
public void close() throws ValidatorManagerException {
setJarFileNames2Close.clear();
closeClassLoader(this);
finalizeNativeLibs(this);
cleanupJarFileFactory();
}
/**
* cleanup jar file factory cache
*/
@SuppressWarnings({ "nls", "rawtypes" })
public boolean cleanupJarFileFactory() throws ValidatorManagerException {
boolean res = false;
final Class classJarURLConnection = JarURLConnection.class;
Field f;
try {
f = classJarURLConnection.getDeclaredField("factory");
} catch (NoSuchFieldException e) {
throw new ValidatorManagerException(e);
}
if (f == null) {
return false;
}
f.setAccessible(true);
Object obj;
try {
obj = f.get(null);
} catch (IllegalAccessException e) {
throw new ValidatorManagerException(e);
}
if (obj == null) {
return false;
}
Class classJarFileFactory = obj.getClass();
HashMap fileCache = null;
try {
f = classJarFileFactory.getDeclaredField("fileCache");
f.setAccessible(true);
obj = f.get(null);
if (obj instanceof HashMap) {
fileCache = (HashMap) obj;
}
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new ValidatorManagerException(e);
}
HashMap urlCache = null;
try {
f = classJarFileFactory.getDeclaredField("urlCache");
f.setAccessible(true);
obj = f.get(null);
if (obj instanceof HashMap) {
urlCache = (HashMap) obj;
}
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new ValidatorManagerException(e);
}
if (urlCache != null) {
HashMap urlCacheTmp = (HashMap) urlCache.clone();
Iterator it = urlCacheTmp.keySet().iterator();
while (it.hasNext()) {
obj = it.next();
if (!(obj instanceof JarFile)) {
continue;
}
JarFile jarFile = (JarFile) obj;
if (setJarFileNames2Close.contains(jarFile.getName())) {
try {
jarFile.close();
} catch (IOException e) {
throw new ValidatorManagerException(e);
}
if (fileCache != null) {
fileCache.remove(urlCache.get(jarFile));
}
urlCache.remove(jarFile);
}
}
res = true;
} else if (fileCache != null) {
HashMap fileCacheTmp = (HashMap) fileCache.clone();
Iterator it = fileCacheTmp.keySet().iterator();
while (it.hasNext()) {
Object key = it.next();
obj = fileCache.get(key);
if (!(obj instanceof JarFile)) {
continue;
}
JarFile jarFile = (JarFile) obj;
if (setJarFileNames2Close.contains(jarFile.getName())) {
try {
jarFile.close();
} catch (IOException e) {
throw new ValidatorManagerException(e);
}
fileCache.remove(key);
}
}
res = true;
}
setJarFileNames2Close.clear();
return res;
}
/**
* close jar files of cl
* @param cl
* @return
*/
@SuppressWarnings({ "nls", "rawtypes" })
public boolean closeClassLoader(ClassLoader cl) throws ValidatorManagerException {
boolean res = false;
if (cl == null) {
return res;
}
Class classURLClassLoader = URLClassLoader.class;
Field f = null;
try {
f = classURLClassLoader.getDeclaredField("ucp");
} catch (NoSuchFieldException e) {
throw new ValidatorManagerException(e);
}
if (f != null) {
f.setAccessible(true);
Object obj = null;
try {
obj = f.get(cl);
} catch (IllegalAccessException e) {
throw new ValidatorManagerException(e);
}
if (obj != null) {
final Object ucp = obj;
f = null;
try {
f = ucp.getClass().getDeclaredField("loaders");
} catch (NoSuchFieldException e) {
throw new ValidatorManagerException(e);
}
if (f != null) {
f.setAccessible(true);
ArrayList loaders = null;
try {
loaders = (ArrayList) f.get(ucp);
res = true;
} catch (IllegalAccessException e) {
throw new ValidatorManagerException(e);
}
for (int i = 0; loaders != null && i < loaders.size(); i++) {
obj = loaders.get(i);
f = null;
try {
f = obj.getClass().getDeclaredField("jar");
} catch (NoSuchFieldException e) {
throw new ValidatorManagerException(e);
}
if (f != null) {
f.setAccessible(true);
try {
obj = f.get(obj);
} catch (IllegalAccessException e) {
throw new ValidatorManagerException(e);
}
if (obj instanceof JarFile) {
final JarFile jarFile = (JarFile) obj;
setJarFileNames2Close.add(jarFile.getName());
try {
jarFile.close();
} catch (IOException e) {
throw new ValidatorManagerException(e);
}
}
}
}
}
}
}
return res;
}
/**
* finalize native libraries
* @param cl
* @return
*/
@SuppressWarnings({ "nls", "rawtypes" })
public boolean finalizeNativeLibs(ClassLoader cl) throws ValidatorManagerException {
boolean res = false;
Class classClassLoader = ClassLoader.class;
java.lang.reflect.Field nativeLibraries = null;
try {
nativeLibraries = classClassLoader.getDeclaredField("nativeLibraries");
} catch (NoSuchFieldException e) {
throw new ValidatorManagerException(e);
}
if (nativeLibraries == null) {
return res;
}
nativeLibraries.setAccessible(true);
Object obj = null;
try {
obj = nativeLibraries.get(cl);
} catch (IllegalAccessException e) {
throw new ValidatorManagerException(e);
}
if (!(obj instanceof Vector)) {
return res;
}
res = true;
Vector java_lang_ClassLoader_NativeLibrary = (Vector) obj;
for (Object lib : java_lang_ClassLoader_NativeLibrary) {
java.lang.reflect.Method finalize = null;
try {
finalize = lib.getClass().getDeclaredMethod("finalize", new Class[0]);
} catch (NoSuchMethodException e) {
throw new ValidatorManagerException(e);
}
if (finalize != null) {
finalize.setAccessible(true);
try {
finalize.invoke(lib, new Object[0]);
} catch (IllegalAccessException e) {
throw new ValidatorManagerException(e);
} catch (InvocationTargetException e) {
throw new ValidatorManagerException(e);
}
}
}
return res;
}
}