/*
* freenet - JarClassLoader.java Copyright © 2007 David Roden
*
* This program 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 2 of the License, or (at your option) any later
* version.
*
* This program 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
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package freenet.support;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import freenet.support.io.FileUtil;
/**
* Class loader that loads classes from a JAR file. The JAR file gets copied
* to a temporary location; requests for classes and resources from this class
* loader are then satisfied from this local copy.
*
* @author David Roden
* @version $Id$
*/
public class JarClassLoader extends ClassLoader implements Closeable {
/** The temporary jar file. */
private JarFile tempJarFile;
/**
* Constructs a new jar class loader that loads classes from the jar file
* with the given name in the local file system.
*
* @param fileName
* The name of the jar file
* @throws IOException
* if an I/O error occurs
*/
public JarClassLoader(String fileName) throws IOException {
this(new File(fileName));
}
/**
* Constructs a new jar class loader that loads classes from the specified
* URL.
*
* @param fileUrl
* The URL to load the jar file from
* @param length
* The length of the jar file if known, -1
* otherwise
* @throws IOException
* if an I/O error occurs
*/
public JarClassLoader(URL fileUrl, long length) throws IOException {
copyFileToTemp(fileUrl.openStream(), length);
}
/**
* Constructs a new jar class loader that loads classes from the specified
* file.
*
* @param file
* The file to load classes from
* @throws IOException
* if an I/O error occurs
*/
public JarClassLoader(File file) throws IOException {
tempJarFile = new JarFile(file);
}
/**
* Copies the contents of the input stream (which are supposed to be the
* contents of a jar file) to a temporary location.
*
* @param inputStream
* The input stream to read from
* @param length
* The length of the stream if known, -1 if the
* length is not known
* @throws IOException
* if an I/O error occurs
*/
private void copyFileToTemp(InputStream inputStream, long length) throws IOException {
File tempFile = File.createTempFile("jar-", ".tmp");
FileOutputStream fileOutputStream = new FileOutputStream(tempFile);
FileUtil.copy(inputStream, fileOutputStream, length);
fileOutputStream.close();
tempFile.deleteOnExit();
tempJarFile = new JarFile(tempFile);
}
/**
* {@inheritDoc}
*
* This method searches the temporary copy of the jar file for an entry * that is specified by the given class name. * * @see java.lang.ClassLoader#findClass(java.lang.String) */ @Override protected Class> findClass(String name) throws ClassNotFoundException { try { String pathName = transformName(name); JarEntry jarEntry = tempJarFile.getJarEntry(pathName); if (jarEntry != null) { long size = jarEntry.getSize(); InputStream jarEntryInputStream = tempJarFile.getInputStream(jarEntry); ByteArrayOutputStream classBytesOutputStream = new ByteArrayOutputStream((int) size); FileUtil.copy(jarEntryInputStream, classBytesOutputStream, size); classBytesOutputStream.close(); jarEntryInputStream.close(); byte[] classBytes = classBytesOutputStream.toByteArray(); Class> clazz = defineClass(name, classBytes, 0, classBytes.length); return clazz; } throw new ClassNotFoundException("could not find jar entry for class " + name); } catch (IOException e) { throw new ClassNotFoundException(e.getMessage(), e); } } /** * {@inheritDoc} * * @see java.lang.ClassLoader#findResource(java.lang.String) */ @Override protected URL findResource(String name) { /* compatibility code. remove when all plugins are fixed. */ if (name.startsWith("/")) { name = name.substring(1); } try { return new URL("jar:" + new File(tempJarFile.getName()).toURI().toURL() + "!/" + name); } catch (MalformedURLException e) { } return null; } /** * Transforms the class name into a file name that can be used to locate * an entry in the jar file. * * @param name * The name of the class * @return The path name of the entry in the jar file */ private String transformName(String name) { return name.replace('.', '/') + ".class"; } public void close() throws IOException { tempJarFile.close(); } }