/* * Copyright 2010-2015 Institut Pasteur. * * This file is part of Icy. * * Icy is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Icy is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Icy. If not, see <http://www.gnu.org/licenses/>. */ package icy.plugin.classloader; import icy.network.NetworkUtil; import icy.network.URLUtil; import icy.plugin.classloader.exception.JclException; import icy.system.IcyExceptionHandler; import java.io.BufferedInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.JarURLConnection; import java.net.URISyntaxException; import java.net.URL; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.jar.JarEntry; import java.util.logging.Level; import java.util.logging.Logger; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipInputStream; /** * JarResources reads jar files and loads the class content/bytes in a HashMap * * @author Kamran Zafar * @author Stephane Dallongeville */ public class JarResources { // <resourceName, content> map protected Map<String, byte[]> entryContents; // <resourceName, fileName> map protected Map<String, URL> entryUrls; protected boolean collisionAllowed; // keep trace of loaded resource size protected int loadedSize; private static Logger logger = Logger.getLogger(JarResources.class.getName()); /** * Default constructor */ public JarResources() { entryContents = new HashMap<String, byte[]>(); entryUrls = new HashMap<String, URL>(); collisionAllowed = Configuration.suppressCollisionException(); loadedSize = 0; } public URL getResource(String name) { return entryUrls.get(name); } public byte[] getResourceContent(String name) throws IOException { byte content[] = entryContents.get(name); // we load the content if (content == null) { final URL url = entryUrls.get(name); if (url != null) { // load content and return it loadContent(name, url); content = entryContents.get(name); // try // { // // load content and return it // loadContent(name, url); // content = entryContents.get(name); // } // catch (IOException e) // { // // content cannot be loaded, remove the URL entry // // better to not remove it after all, so we know why it failed next time // // entryUrls.remove(name); // // // and throw exception // throw e; // } } } return content; } protected void loadContent(String name, URL url) throws IOException { // only support JAR resource here final byte[] content = loadJarContent(url); setResourceContent(name, content); } /** * Returns an immutable Set of all resources names */ public Set<String> getResourcesName() { return Collections.unmodifiableSet(entryUrls.keySet()); } /** * Returns an immutable Map of all resources */ public Map<String, URL> getResources() { return Collections.unmodifiableMap(entryUrls); } /** * Returns an immutable Map of all loaded jar resources */ public Map<String, byte[]> getLoadedResources() { return Collections.unmodifiableMap(entryContents); } /** * Loads the jar file from a specified File.<br> * This method actually stores all contained URL in the specified JAR file. * * @throws IOException */ public void loadJar(File file) throws IOException { final String filePath = file.getAbsolutePath(); final String urlPrefix = "jar:" + file.toURI().toString() + "!/"; if (logger.isLoggable(Level.FINEST)) logger.finest("Loading jar: " + filePath); // we don't care about JAR specific information so just use ZipFile here final ZipFile zipFile = new ZipFile(file); try { final Enumeration<? extends ZipEntry> entries = zipFile.entries(); while (entries.hasMoreElements()) { final ZipEntry entry = entries.nextElement(); // ignore folder entries if (entry.isDirectory()) continue; final String name = entry.getName(); // ignore manifest file if (name.equals("META-INF/MANIFEST.MF")) continue; if (entryUrls.containsKey(name)) { if (!collisionAllowed) throw new JclException("Class/Resource " + name + " already loaded"); if (logger.isLoggable(Level.FINEST)) logger.finest("Class/Resource " + name + " already loaded; ignoring entry..."); continue; } // add to internal resource HashMap entryUrls.put(name, new URL(urlPrefix + name)); } } finally { try { zipFile.close(); } catch (IOException e) { // not important System.err.println("JarResources.loadJar(" + filePath + ") error:"); IcyExceptionHandler.showErrorMessage(e, false, true); } } } /** * Loads the jar file from a specified URL.<br> * This method actually stores all contained URL in the specified JAR archive. * * @throws IOException */ public void loadJar(URL url) throws IOException { // use file loading if possible if (URLUtil.isFileURL(url)) { File f; try { f = new File(url.toURI()); } catch (URISyntaxException e) { f = new File(url.getPath()); } loadJar(f); return; } if (logger.isLoggable(Level.FINEST)) logger.finest("Loading jar: " + url.toString()); final String urlPrefix = "jar:" + url.toString() + "!/"; BufferedInputStream bis = null; // we don't care about JAR specific information so just use ZipInputStream here ZipInputStream zis = null; try { final InputStream in = url.openStream(); if (in instanceof BufferedInputStream) bis = (BufferedInputStream) in; else bis = new BufferedInputStream(in); zis = new ZipInputStream(bis); ZipEntry entry = null; while ((entry = zis.getNextEntry()) != null) { if (logger.isLoggable(Level.FINEST)) logger.finest(dump(entry)); if (entry.isDirectory()) continue; final String name = entry.getName(); // ignore manifest file if (name.equals("META-INF/MANIFEST.MF")) continue; if (entryUrls.containsKey(name)) { if (!collisionAllowed) throw new JclException("Class/Resource " + name + " already loaded"); if (logger.isLoggable(Level.FINEST)) logger.finest("Class/Resource " + name + " already loaded; ignoring entry..."); continue; } // add to internal resource HashMap entryUrls.put(name, new URL(urlPrefix + name)); } } // catch (NullPointerException e) // { // if (logger.isLoggable(Level.FINEST)) // logger.finest("Done loading."); // } finally { if (zis != null) { try { zis.close(); } catch (IOException e) { // not important System.err.println("JarResources.loadJar(" + url + ") error:"); IcyExceptionHandler.showErrorMessage(e, false, true); } } if (bis != null) { try { bis.close(); } catch (IOException e) { // not important System.err.println("JarResources.loadJar(" + url + ") error:"); IcyExceptionHandler.showErrorMessage(e, false, true); } } } } /** * Load the jar contents from InputStream * * @throws IOException */ protected byte[] loadJarContent(URL url) throws IOException { final JarURLConnection uc = (JarURLConnection) url.openConnection(); final JarEntry jarEntry = uc.getJarEntry(); if (jarEntry != null) { if (logger.isLoggable(Level.FINEST)) logger.finest(dump(jarEntry)); return NetworkUtil.download(uc.getInputStream(), jarEntry.getSize(), null); } throw new IOException("JarResources.loadJarContent(" + url.toString() + ") error:\nEntry not found !"); } protected void setResourceContent(String name, byte content[]) { if (entryContents.containsKey(name)) { if (!collisionAllowed) throw new JclException("Class/Resource " + name + " already loaded"); if (logger.isLoggable(Level.FINEST)) logger.finest("Class/Resource " + name + " already loaded; ignoring entry..."); return; } if (logger.isLoggable(Level.FINEST)) logger.finest("Entry Name: " + name + ", " + "Entry Size: " + content.length); // add to internal resource HashMap entryContents.put(name, content); } /** * For debugging * * @param je * @return String */ private String dump(ZipEntry ze) { StringBuffer sb = new StringBuffer(); if (ze.isDirectory()) sb.append("d "); else sb.append("f "); if (ze.getMethod() == ZipEntry.STORED) sb.append("stored "); else sb.append("deflated "); sb.append(ze.getName()); sb.append("\t"); sb.append("" + ze.getSize()); if (ze.getMethod() == ZipEntry.DEFLATED) sb.append("/" + ze.getCompressedSize()); return (sb.toString()); } }