001/* 002 * (C) Copyright 2012 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Stephane Lacoin 018 */ 019package org.nuxeo.osgi.util.jar; 020 021import java.io.IOException; 022import java.lang.reflect.Field; 023import java.lang.reflect.InvocationTargetException; 024import java.lang.reflect.Method; 025import java.net.URL; 026import java.net.URLClassLoader; 027import java.util.List; 028import java.util.Map; 029import java.util.jar.JarFile; 030 031public class URLJarFileIntrospector { 032 033 protected Method factoryGetMethod; 034 035 protected Method factoryCloseMethod; 036 037 protected Field jarField; 038 039 protected Method getJarFileMethod; 040 041 Field ucpField; 042 043 Field lmapField; 044 045 Field loadersField; 046 047 Field jarFileFactoryField; 048 049 Object factory; 050 051 public URLJarFileIntrospector() throws URLJarFileIntrospectionError { 052 try { 053 ucpField = URLClassLoader.class.getDeclaredField("ucp"); 054 ucpField.setAccessible(true); 055 Class<?> ucpClass = loadClass("sun.misc.URLClassPath"); 056 lmapField = ucpClass.getDeclaredField("lmap"); 057 lmapField.setAccessible(true); 058 loadersField = ucpClass.getDeclaredField("loaders"); 059 loadersField.setAccessible(true); 060 Class<?> jarLoaderClass = loadClass("sun.misc.URLClassPath$JarLoader"); 061 jarField = jarLoaderClass.getDeclaredField("jar"); 062 jarField.setAccessible(true); 063 getJarFileMethod = jarLoaderClass.getDeclaredMethod("getJarFile", new Class<?>[] { URL.class }); 064 getJarFileMethod.setAccessible(true); 065 Class<?> jarURLConnectionClass = loadClass("sun.net.www.protocol.jar.JarURLConnection"); 066 jarFileFactoryField = jarURLConnectionClass.getDeclaredField("factory"); 067 jarFileFactoryField.setAccessible(true); 068 factory = jarFileFactoryField.get(null); 069 Class<?> factoryClass = loadClass("sun.net.www.protocol.jar.JarFileFactory"); 070 factoryGetMethod = factoryClass.getMethod("get", new Class<?>[] { URL.class }); 071 factoryGetMethod.setAccessible(true); 072 factoryCloseMethod = factoryClass.getMethod("close", new Class<?>[] { JarFile.class }); 073 factoryCloseMethod.setAccessible(true); 074 } catch (NoSuchFieldException | SecurityException | ClassNotFoundException | NoSuchMethodException 075 | IllegalArgumentException | IllegalAccessException cause) { 076 throw new URLJarFileIntrospectionError("Cannot introspect url class loader jar files", cause); 077 } 078 } 079 080 protected Object fetchFactory() throws URLJarFileIntrospectionError { 081 try { 082 return jarFileFactoryField.get(null); 083 } catch (IllegalArgumentException | IllegalAccessException cause) { 084 throw new URLJarFileIntrospectionError("Cannot access to factory", cause); 085 } 086 } 087 088 protected static Class<?> loadClass(String name) throws ClassNotFoundException { 089 return URLJarFileIntrospector.class.getClassLoader().loadClass(name); 090 } 091 092 public JarFileCloser newJarFileCloser(ClassLoader loader) throws URLJarFileIntrospectionError { 093 return new URLJarFileCloser(this, loader); 094 } 095 096 protected URLClassLoaderCloser newURLClassLoaderCloser(URLClassLoader loader) throws URLJarFileIntrospectionError { 097 try { 098 Object ucp = ucpField.get(loader); 099 Map<?, ?> index = (Map<?, ?>) lmapField.get(ucp); 100 List<?> loaders = (List<?>) loadersField.get(ucp); 101 return new URLClassLoaderCloser(this, index, loaders); 102 } catch (IllegalArgumentException | IllegalAccessException cause) { 103 throw new URLJarFileIntrospectionError("Cannot unwrap url class loader fields", cause); 104 } 105 } 106 107 public void close(URL location) throws IOException { 108 JarFile jar = null; 109 try { 110 jar = (JarFile) factoryGetMethod.invoke(factory, new Object[] { location }); 111 factoryCloseMethod.invoke(factory, jar); 112 } catch (IllegalAccessException e) { 113 throw new RuntimeException("Cannot use reflection on jar file factory", e); 114 } catch (InvocationTargetException e) { 115 throw new RuntimeException("Cannot use reflection on jar file factory", e); 116 } 117 jar.close(); 118 } 119}