001package org.nuxeo.osgi.util.jar; 002 003import java.io.IOException; 004import java.lang.reflect.Field; 005import java.lang.reflect.InvocationTargetException; 006import java.lang.reflect.Method; 007import java.net.URL; 008import java.net.URLClassLoader; 009import java.util.List; 010import java.util.Map; 011import java.util.jar.JarFile; 012 013public class URLJarFileIntrospector { 014 015 protected Method factoryGetMethod; 016 017 protected Method factoryCloseMethod; 018 019 protected Field jarField; 020 021 protected Method getJarFileMethod; 022 023 Field ucpField; 024 025 Field lmapField; 026 027 Field loadersField; 028 029 Field jarFileFactoryField; 030 031 Object factory; 032 033 public URLJarFileIntrospector() throws URLJarFileIntrospectionError { 034 try { 035 ucpField = URLClassLoader.class.getDeclaredField("ucp"); 036 ucpField.setAccessible(true); 037 Class<?> ucpClass = loadClass("sun.misc.URLClassPath"); 038 lmapField = ucpClass.getDeclaredField("lmap"); 039 lmapField.setAccessible(true); 040 loadersField = ucpClass.getDeclaredField("loaders"); 041 loadersField.setAccessible(true); 042 Class<?> jarLoaderClass = loadClass("sun.misc.URLClassPath$JarLoader"); 043 jarField = jarLoaderClass.getDeclaredField("jar"); 044 jarField.setAccessible(true); 045 getJarFileMethod = jarLoaderClass.getDeclaredMethod("getJarFile", new Class<?>[] { URL.class }); 046 getJarFileMethod.setAccessible(true); 047 Class<?> jarURLConnectionClass = loadClass("sun.net.www.protocol.jar.JarURLConnection"); 048 jarFileFactoryField = jarURLConnectionClass.getDeclaredField("factory"); 049 jarFileFactoryField.setAccessible(true); 050 factory = jarFileFactoryField.get(null); 051 Class<?> factoryClass = loadClass("sun.net.www.protocol.jar.JarFileFactory"); 052 factoryGetMethod = factoryClass.getMethod("get", new Class<?>[] { URL.class }); 053 factoryGetMethod.setAccessible(true); 054 factoryCloseMethod = factoryClass.getMethod("close", new Class<?>[] { JarFile.class }); 055 factoryCloseMethod.setAccessible(true); 056 } catch (NoSuchFieldException | SecurityException | ClassNotFoundException | NoSuchMethodException 057 | IllegalArgumentException | IllegalAccessException cause) { 058 throw new URLJarFileIntrospectionError("Cannot introspect url class loader jar files", cause); 059 } 060 } 061 062 protected Object fetchFactory() throws URLJarFileIntrospectionError { 063 try { 064 return jarFileFactoryField.get(null); 065 } catch (IllegalArgumentException | IllegalAccessException cause) { 066 throw new URLJarFileIntrospectionError("Cannot access to factory", cause); 067 } 068 } 069 070 protected static Class<?> loadClass(String name) throws ClassNotFoundException { 071 return URLJarFileIntrospector.class.getClassLoader().loadClass(name); 072 } 073 074 public JarFileCloser newJarFileCloser(ClassLoader loader) throws URLJarFileIntrospectionError { 075 return new URLJarFileCloser(this, loader); 076 } 077 078 protected URLClassLoaderCloser newURLClassLoaderCloser(URLClassLoader loader) throws URLJarFileIntrospectionError { 079 try { 080 Object ucp = ucpField.get(loader); 081 Map<?, ?> index = (Map<?, ?>) lmapField.get(ucp); 082 List<?> loaders = (List<?>) loadersField.get(ucp); 083 return new URLClassLoaderCloser(this, index, loaders); 084 } catch (IllegalArgumentException | IllegalAccessException cause) { 085 throw new URLJarFileIntrospectionError("Cannot unwrap url class loader fields", cause); 086 } 087 } 088 089 public void close(URL location) throws IOException { 090 JarFile jar = null; 091 try { 092 jar = (JarFile) factoryGetMethod.invoke(factory, new Object[] { location }); 093 factoryCloseMethod.invoke(factory, jar); 094 } catch (IllegalAccessException e) { 095 throw new RuntimeException("Cannot use reflection on jar file factory", e); 096 } catch (InvocationTargetException e) { 097 throw new RuntimeException("Cannot use reflection on jar file factory", e); 098 } 099 jar.close(); 100 } 101}