Package freenet.support

Source Code of freenet.support.JarClassLoader

/*
* 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.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.jar.Attributes.Name;
import java.util.zip.ZipEntry;

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 <a href="mailto:dr@ina-germany.de">David Roden</a>
* @version $Id$
*/
public class JarClassLoader extends ClassLoader implements Closeable {
  private static volatile boolean logMINOR;
  static {
    Logger.registerClass(JarClassLoader.class);
  }

  /** 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, <code>-1</code>
   *            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, <code>-1</code> 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}
   * <p>
   * 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();

        definePackage(name);
         
        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}
   * <p>
   * Finds the resource within this jar only. If it isn't found within the jar, getResourceAsStream()
   * will look elsewhere.
   */
  @Override
  protected URL findResource(String name) {
    /* FIXME compatibility code. remove when all plugins are fixed. */
    if (name.startsWith("/")) {
      name = name.substring(1);
    }
    try {
      if(tempJarFile.getJarEntry(name)==null) {
        return null;
      }

      return new URL("jar:" + new File(tempJarFile.getName()).toURI().toURL() + "!/" + name);
    } catch (MalformedURLException e) {
    }
    return null;
  }
 
  /**
   * {@inheritDoc}
   * <p>
   * If the resource is found in this jar, opens the stream using ZipEntry's,
   * so when tempJarFile is closed, so are all the streams, hence we can delete
   * the jar on Windows.
   *
   * @see java.lang.ClassLoader#getResourceAsStream(java.lang.String)
   */
  @Override
  public InputStream getResourceAsStream(String name) {
    if(logMINOR) Logger.minor(this, "Requested resource: " + name, new Exception("debug"));
    URL url = getResource(name);
    if (url == null)
      return null;
    if(logMINOR) Logger.minor(this, "Found resource at URL: " + url);

    // If the resource is not from our jar, return it as normal
    URL localUrl = findResource(name);
    if (localUrl == null || !url.toString().equals(localUrl.toString()))
      try {
        return url.openStream();
      } catch (IOException e) {
        return null;
      }

    // If the resource is from our jar, open InputStream explicitly from the jar
    // so that we can close() all opened streams later and let the jar file
    // to be deleted on Windows

    /* FIXME compatibility code. remove when all plugins are fixed. */
    if (name.startsWith("/")) {
      name = name.substring(1);
    }

    ZipEntry entry = tempJarFile.getEntry(name);
    try {
      return entry != null ? tempJarFile.getInputStream(entry) : null;
    } catch (IOException 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";
  }
 
  protected Package definePackage(String name) throws IllegalArgumentException {
    Package pkg = null;
    int i = name.lastIndexOf('.');
    if (i != -1) {
      String pkgname = name.substring(0, i);
      pkg = getPackage(pkgname);
      if (pkg == null) {
        try {
          Manifest man = tempJarFile.getManifest();
          if(man == null) throw new IOException();
          pkg = definePackage(pkgname, man);
        } catch (IOException e) {
          pkg = definePackage(pkgname, null, null, null, null, null, null, null);
        }
      }
    }
    return pkg;
  }

  protected Package definePackage(String name, Manifest man) throws IllegalArgumentException {
    String path = name.replace('.', '/').concat("/");
    String specTitle = null, specVersion = null, specVendor = null;
    String implTitle = null, implVersion = null, implVendor = null;
    String sealed = null;
    URL sealBase = null;

    Attributes attr = man.getAttributes(path);
    if (attr != null) {
      specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
      specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
      specVendor = attr.getValue(Name.SPECIFICATION_VENDOR);
      implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE);
      implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
      implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
      sealed = attr.getValue(Name.SEALED);
    }
    attr = man.getMainAttributes();
    if (attr != null) {
      if (specTitle == null) {
        specTitle = attr.getValue(Name.SPECIFICATION_TITLE);
      }
      if (specVersion == null) {
        specVersion = attr.getValue(Name.SPECIFICATION_VERSION);
      }
      if (specVendor == null) {
        specVendor = attr.getValue(Name.SPECIFICATION_VENDOR);
      }
      if (implTitle == null) {
        implTitle = attr.getValue(Name.IMPLEMENTATION_TITLE);
      }
      if (implVersion == null) {
        implVersion = attr.getValue(Name.IMPLEMENTATION_VERSION);
      }
      if (implVendor == null) {
        implVendor = attr.getValue(Name.IMPLEMENTATION_VENDOR);
      }
      if (sealed == null) {
        sealed = attr.getValue(Name.SEALED);
      }
    }
    return definePackage(name, specTitle, specVersion, specVendor, implTitle, implVersion, implVendor, sealBase);
  }
 
  @Override
  public void close() throws IOException {
    tempJarFile.close();
  }
}
TOP

Related Classes of freenet.support.JarClassLoader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.