package org.basex.util; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Vector; import java.util.jar.JarFile; /** * Custom class loader for loading jar files. This class is needed because JDK * does not offer a fine and easy way to delete open jars. The source code was * taken from * http://snipplr.com/view/24224/class-loader-which-close-opened-jar-files/ and * slightly modified. * * @author Vitali Yemialyanchyk, www.stopka.us * @author BaseX Team 2005-12, BSD License * @author Rositsa Shadura */ public final class JarLoader extends URLClassLoader { /** Jar files to close. */ private final HashSet<String> setJarFileNames2Close = new HashSet<String>(); /** * Constructor. * @param urls of jars to be loaded. */ public JarLoader(final URL[] urls) { super(urls); } /** * Closes the class loader. */ public void close() { setJarFileNames2Close.clear(); closeClassLoader(this); finalizeNativeLibs(this); cleanupJarFileFactory(); } /** * Cleans up jar file factory cache. * @return result */ @SuppressWarnings("unchecked") boolean cleanupJarFileFactory() { boolean res = false; Class<?> classJarURLConnection = null; try { classJarURLConnection = Class.forName("sun.net.www.protocol.jar.JarURLConnection"); } catch(final ClassNotFoundException ex) { Util.errln(ex); } if(classJarURLConnection == null) { return res; } Field f = null; try { f = classJarURLConnection.getDeclaredField("factory"); } catch(final NoSuchFieldException ex) { Util.errln(ex); } if(f == null) { return res; } f.setAccessible(true); Object obj = null; try { obj = f.get(null); } catch(final IllegalAccessException ex) { Util.errln(ex); } if(obj == null) { return res; } final Class<?> classJarFileFactory = obj.getClass(); HashMap<Object, Object> fileCache = null; try { f = classJarFileFactory.getDeclaredField("fileCache"); f.setAccessible(true); obj = f.get(null); if(obj instanceof HashMap) { fileCache = (HashMap<Object, Object>) obj; } } catch(final NoSuchFieldException ex) { Util.errln(ex); } catch(final IllegalAccessException ex) { Util.errln(ex); } HashMap<Object, Object> urlCache = null; try { f = classJarFileFactory.getDeclaredField("urlCache"); f.setAccessible(true); obj = f.get(null); if(obj instanceof HashMap) { urlCache = (HashMap<Object, Object>) obj; } } catch(final NoSuchFieldException ex) { Util.errln(ex); } catch(final IllegalAccessException ex) { Util.errln(ex); } if(urlCache != null) { final HashMap<Object, Object> urlCacheTmp = (HashMap<Object, Object>) urlCache.clone(); for(final Object o : urlCacheTmp.keySet()) { obj = o; if(!(obj instanceof JarFile)) { continue; } final JarFile jarFile = (JarFile) obj; if(setJarFileNames2Close.contains(jarFile.getName())) { try { jarFile.close(); } catch(final IOException ex) { Util.errln(ex); } if(fileCache != null) { fileCache.remove(urlCache.get(jarFile)); } urlCache.remove(jarFile); } } res = true; } else if(fileCache != null) { // urlCache := null final HashMap<Object, Object> fileCacheTmp = (HashMap<Object, Object>) fileCache.clone(); for(final Object key : fileCacheTmp.keySet()) { obj = fileCache.get(key); if(!(obj instanceof JarFile)) { continue; } final JarFile jarFile = (JarFile) obj; if(setJarFileNames2Close.contains(jarFile.getName())) { try { jarFile.close(); } catch(final IOException ex) { Util.errln(ex); } fileCache.remove(key); } } res = true; } setJarFileNames2Close.clear(); return res; } /** * Closes jar files of class loader. * @param cl class loader * @return result */ @SuppressWarnings("unchecked") boolean closeClassLoader(final ClassLoader cl) { boolean res = false; if(cl == null) { return res; } final Class<?> classURLClassLoader = URLClassLoader.class; Field f = null; try { f = classURLClassLoader.getDeclaredField("ucp"); } catch(final NoSuchFieldException ex) { Util.errln(ex); } if(f != null) { f.setAccessible(true); Object obj = null; try { obj = f.get(cl); } catch(final IllegalAccessException ex) { Util.errln(ex); } if(obj != null) { final Object ucp = obj; f = null; try { f = ucp.getClass().getDeclaredField("loaders"); } catch(final NoSuchFieldException ex) { Util.errln(ex); } if(f != null) { f.setAccessible(true); ArrayList<Object> loaders = null; try { loaders = (ArrayList<Object>) f.get(ucp); res = true; } catch(final IllegalAccessException ex) { Util.errln(ex); } for(int i = 0; loaders != null && i < loaders.size(); i++) { obj = loaders.get(i); f = null; try { f = obj.getClass().getDeclaredField("jar"); } catch(final NoSuchFieldException ex) { Util.errln(ex); } if(f != null) { f.setAccessible(true); try { obj = f.get(obj); } catch(final IllegalAccessException ex) { Util.errln(ex); } if(obj instanceof JarFile) { final JarFile jarFile = (JarFile) obj; setJarFileNames2Close.add(jarFile.getName()); try { jarFile.close(); } catch(final IOException ex) { Util.errln(ex); } } } } } } } return res; } /** * Finalizes native libraries. * @param cl class loader * @return result */ @SuppressWarnings("unchecked") static boolean finalizeNativeLibs(final ClassLoader cl) { boolean res = false; final Class<?> classClassLoader = ClassLoader.class; Field nativeLibraries = null; try { nativeLibraries = classClassLoader.getDeclaredField("nativeLibraries"); } catch(final NoSuchFieldException ex) { Util.errln(ex); } if(nativeLibraries == null) { return res; } nativeLibraries.setAccessible(true); Object obj = null; try { obj = nativeLibraries.get(cl); } catch(final IllegalAccessException ex) { Util.errln(ex); } if(!(obj instanceof Vector)) { return res; } res = true; final Vector<Object> nativeLib = (Vector<Object>) obj; for(final Object lib : nativeLib) { Method finalize = null; try { finalize = lib.getClass().getDeclaredMethod("finalize", new Class[0]); } catch(final NoSuchMethodException ex) { Util.errln(ex); } if(finalize != null) { finalize.setAccessible(true); try { finalize.invoke(lib); } catch(final IllegalAccessException ex) { Util.errln(ex); } catch(final InvocationTargetException ex) { Util.errln(ex); } } } return res; } }