Package com.jdotsoft.jarloader

Source Code of com.jdotsoft.jarloader.JarClassLoader$JarEntryInfo

/*
* File: JarClassLoader.java
*
* Copyright (C) 2008-2010 JDotSoft. All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* 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., 51 Franklin Street, Fifth Floor, Boston,
* MA  02110-1301  USA
*
* Visit jdotsoft.com for commercial license.
*
* $Id: JarClassLoader.java,v 1.24 2010/02/18 17:19:30 mg Exp $
*/
package com.jdotsoft.jarloader;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;

import javax.swing.LookAndFeel;
import javax.swing.UIDefaults;
import javax.swing.UIManager;

/**
* This classloader loads classes, native libraries and resources from the top
* JAR and from JARs inside top JAR. The loading process looks through JARs
* hierarchy and allows their tree structure, i.e. nested JARs.
* <p>
* This class delegates class loading to the parent class loader and
* successfully loads classes, native libraries and resources when it works not
* in a JAR environment.
* <p>
* Create a <code>Launcher</code> class to use this class loader and start its
* main() method to start your application <code>com.mycompany.MyApp</code>
* <code>
<pre>
public class Launcher {

    public static void main(String[] args) {
        JarClassLoader jcl = new JarClassLoader();
        System.out.println("Starting TestMain...");
        try {
            jcl.invokeMain("com.mycompany.MyApp", args);
        } catch (Throwable e) {
            e.printStackTrace();
        }
    } // main()
   
} // class Launcher
</pre>
* </code>
* <p>
* An application could be started from a command line using its main class e.g.
* <code>TestMain.main()</code> or from <code>Launcher.main()</code> similar to
* the the above example. The application behavior in both cases is identical if
* resources are loaded from a file system. Starting from
* <code>Launcher.main()</code> is required only to start the application from a
* JAR file which contains other JARs or native libraries.
* <p>
* Special handling is required for loading external LaF classes. Call the
* method <code>JarClassLoader.loadLookAndFeel();</code> to preload UI classes.
* <p>
* Known issues: temporary files with loaded native libraries are not deleted on
* application exit because JVM does not close handles to them. The loader
* attempts to delete them on next launch. The list of these temporary files is
* preserved in the "[user.home]/.JarClassLoader" file.
* <p>
* See also discussion "How load library from jar file?"
* http://discuss.develop.com
* /archives/wa.exe?A2=ind0302&L=advanced-java&D=0&P=4549 Unfortunately, the
* native method java.lang.ClassLoader$NativeLibrary.unload() is package
* accessed in a package accessed inner class. Moreover, it's called from
* finalizer. This does not allow releasing the native library handle and delete
* the temporary library file. Option to explore: use JNI function
* UnregisterNatives(). See also native code in
* ...\jdk\src\share\native\java\lang\ClassLoader.c
*
* @version $Revision: 1.24 $
*/
public class JarClassLoader extends ClassLoader {

  /**
   * VM parameter to turn on debugging logging to file or console. <br>
   * Specify <code>-DJarClassLoader.logger=[filename]</code> in the command
   * line for logging on into the file or to console if the filename is
   * specified as a "console".
   */
  public static final String KEY_LOGGER = "JarClassLoader.logger";
  public static final String CONSOLE = "console";

  private PrintStream logger;
  private List<JarFile> lstJarFile;
  private Set<File> hsNativeFile;
  private Map<String, Class<?>> hmClass;
  private ProtectionDomain pd;

  /**
   * Default constructor. Defines system class loader as a parent class
   * loader.
   */
  public JarClassLoader() {
    this(ClassLoader.getSystemClassLoader());
  }

  /**
   * Constructor
   *
   * @param parent
   *            class loader parent
   */
  public JarClassLoader(ClassLoader parent) {
    super(parent);
    String sLogger = System.getProperty(KEY_LOGGER);
    if (sLogger != null) {
      if (sLogger.equals(CONSOLE)) {
        this.logger = System.out;
      } else {
        try {
          this.logger = new PrintStream(sLogger);
        } catch (FileNotFoundException e) {
          throw new RuntimeException(
              "JarClassLoader: cannot create log file: " + e);
        }
      }
    }
    this.hmClass = new HashMap<String, Class<?>>();
    this.lstJarFile = new ArrayList<JarFile>();
    this.hsNativeFile = new HashSet<File>();

    String sUrlTopJAR = null;
    try {
      this.pd = getClass().getProtectionDomain();
      CodeSource cs = this.pd.getCodeSource();
      URL urlTopJAR = cs.getLocation();
      // URL.getFile() returns "/C:/my%20dir/MyApp.jar"
      sUrlTopJAR = URLDecoder.decode(urlTopJAR.getFile(), "UTF-8");
      log("Loading from top JAR: %s", sUrlTopJAR);
      loadJar(new JarFile(sUrlTopJAR)); // throws if not JAR
    } catch (IOException e) {
      // Expected exception: loading NOT from JAR.
      log("Not a JAR: %s %s", sUrlTopJAR, e.toString());
      return;
    }
    Runtime.getRuntime().addShutdownHook(new Thread() {
      @Override
      public void run() {
        shutdown();
      }
    });
    UIManager.addPropertyChangeListener(new PropertyChangeListener() {
      // @Override - commented out to comply with Java 1.5
      public void propertyChange(PropertyChangeEvent evt) {
        if ("lookAndFeel".equals(evt.getPropertyName())) {
          try {
            loadLookAndFeel();
          } catch (ClassNotFoundException e) {
            throw new RuntimeException("Failure to load LaF "
                + evt.getNewValue(), e);
          }
        }
      }
    });
  } // JarClassLoader()

  // --------------------------------separator--------------------------------
  static int ______INIT;

  /**
   * Using temp files (one per inner JAR/DLL) solves many issues: 1. There are
   * no ways to load JAR defined in a JarEntry directly into the JarFile
   * object. 2. Cannot use memory-mapped files because they are using nio
   * channels, which are not supported by JarFile ctor. 3. JarFile object
   * keeps opened JAR files handlers for fast access. 4. Resource in a
   * jar-in-jar does not have well defined URL. Making temp file with JAR
   * solves this problem. 5. Similar issues with native libraries:
   * <code>ClassLoader.findLibrary()</code> accepts ONLY string with absolute
   * path to the file with native library.
   *
   * @param inf
   *            JAR entry information
   * @return temporary file object presenting JAR entry
   * @throws JarClassLoaderException
   */
  private File createTempFile(JarEntryInfo inf)
      throws JarClassLoaderException {
    byte[] a_by = getJarBytes(inf);
    try {
      File file = File.createTempFile(inf.getName() + ".", null);
      file.deleteOnExit();
      BufferedOutputStream os = new BufferedOutputStream(
          new FileOutputStream(file));
      os.write(a_by);
      os.close();
      return file;
    } catch (IOException e) {
      throw new JarClassLoaderException("Cannot create temp file for "
          + inf.jarEntry, e);
    }
  } // createTempFile()

  /**
   * Loads specified JAR
   *
   * @param jarFile
   *            JAR file
   */
  private void loadJar(JarFile jarFile) {
    this.lstJarFile.add(jarFile);
    try {
      Enumeration<JarEntry> en = jarFile.entries();
      final String EXT_JAR = ".jar";
      while (en.hasMoreElements()) {
        JarEntry je = en.nextElement();
        if (je.isDirectory()) {
          continue;
        }
        String s = je.getName().toLowerCase(); // JarEntry name
        if (s.lastIndexOf(EXT_JAR) == s.length() - EXT_JAR.length()) {
          JarEntryInfo inf = new JarEntryInfo(jarFile, je);
          File file = createTempFile(inf);
          log("Loading inner JAR: %s from temp file %s",
              inf.jarEntry, getFilename4Log(file));
          try {
            loadJar(new JarFile(file));
          } catch (IOException e) {
            throw new JarClassLoaderException(
                "Cannot load inner JAR " + inf.jarEntry, e);
          }
        }
      }
    } catch (JarClassLoaderException e) {
      throw new RuntimeException("ERROR on loading InnerJAR: "
          + e.getMessageAll());
    }
  } // loadJar()

  private JarEntryInfo findJarEntry(String sName) {
    for (JarFile jarFile : this.lstJarFile) {
      JarEntry jarEntry = jarFile.getJarEntry(sName);
      if (jarEntry != null) {
        return new JarEntryInfo(jarFile, jarEntry);
      }
    }
    return null;
  } // findJarEntry()

  private List<JarEntryInfo> findJarEntries(String sName) {
    List<JarEntryInfo> lst = new ArrayList<JarEntryInfo>();
    for (JarFile jarFile : this.lstJarFile) {
      JarEntry jarEntry = jarFile.getJarEntry(sName);
      if (jarEntry != null) {
        lst.add(new JarEntryInfo(jarFile, jarEntry));
      }
    }
    return lst;
  } // findJarEntries()

  /**
   * Finds native library entry.
   *
   * @param sLib
   *            Library name. For example for the name "Native" the Windows
   *            system returns entry for "Native.dll", the Linux system
   *            returns entry for "libNative.so". The path to the entry is
   *            ignored, i.e the library could be in any location in the JAR:
   *            "lib/Native.dll" or "bin/Native.dll" or any.
   * @return Native library entry
   */
  private JarEntryInfo findJarNativeEntry(String sLib) {
    String sName = System.mapLibraryName(sLib);
    for (JarFile jarFile : this.lstJarFile) {
      Enumeration<JarEntry> en = jarFile.entries();
      while (en.hasMoreElements()) {
        JarEntry je = en.nextElement();
        if (je.isDirectory()) {
          continue;
        }
        // Example: sName is "Native.dll"
        String sEntry = je.getName(); // "lib/Native.dll"
       
        // Blacklist
        if(sEntry.endsWith(".png")) {
          continue;
        }
       
        if (sEntry.lastIndexOf(sName) == sEntry.length()
            - sName.length()) {
          return new JarEntryInfo(jarFile, je);
        }
      }
    }
    return null;
  } // findJarNativeEntry()

  /**
   * Loads class from a JAR and searches for all jar-in-jar.
   *
   * @param sClassName
   *            class to load
   * @return loaded class
   * @throws JarClassLoaderException
   */
  private Class<?> findJarClass(String sClassName)
      throws JarClassLoaderException {
    // http://java.sun.com/developer/onlineTraining/Security/Fundamentals
    // /magercises/ClassLoader/solution/FileClassLoader.java
    Class<?> c = this.hmClass.get(sClassName);
    if (c != null) {
      return c;
    }
    // Char '/' works for Win32 and Unix.
    String sName = sClassName.replace('.', '/') + ".class";
    JarEntryInfo inf = findJarEntry(sName);
    if (inf != null) {
      byte[] a_by = getJarBytes(inf);
      try {
        c = defineClass(sClassName, a_by, 0, a_by.length, this.pd);
      } catch (ClassFormatError e) {
        throw new JarClassLoaderException(null, e);
      }
    }
    if (c == null) {
      throw new JarClassLoaderException(sClassName);
    }
    this.hmClass.put(sClassName, c);
    return c;
  } // findJarClass()

  // --------------------------------separator--------------------------------
  static int ______SHUTDOWN;

  /**
   * Called on shutdown for temporary files cleanup
   */
  private void shutdown() {
    // All inner JAR temporary files are marked at the time of creation
    // as deleteOnExit(). These files are not deleted if they are not
    // closed.
    for (JarFile jarFile : this.lstJarFile) {
      try {
        jarFile.close();
      } catch (IOException e) {
        // Ignore. In the worst case temp files will accumulate.
      }
    }
    // JVM does not close handles to native libraries files
    // and temp files even marked closeOnExit() are not deleted.
    // Use special file with list of native libraries temp files
    // to delete them on next application run.
    String sPersistentFile = System.getProperty("user.home")
        + File.separator + ".JarClassLoader";
    deleteOldNative(sPersistentFile);
    persistNewNative(sPersistentFile);
  } // shutdown()

  /**
   * Deletes temporary files listed in the file. The method is called on
   * shutdown().
   *
   * @param sPersistentFile
   *            file name with temporary files list
   */
  private void deleteOldNative(String sPersistentFile) {
    BufferedReader reader = null;
    try {
      reader = new BufferedReader(new FileReader(sPersistentFile));
      String sLine;
      while ((sLine = reader.readLine()) != null) {
        File file = new File(sLine);
        if (!file.exists()) {
          continue; // already deleted; from command line?
        }
        if (!file.delete()) {
          // Cannot delete, will try next time.
          this.hsNativeFile.add(file);
        }
      }
    } catch (IOException e) {
      // Ignore. In the worst case temp files will accumulate.
    } finally {
      if (reader != null) {
        try {
          reader.close();
        } catch (IOException e) {
        }
      }
    }
  } // deleteOldNative()

  /**
   * Creates file with temporary files list. This list will be used to delete
   * temporary files on the next application launch. The method is called from
   * shutdown().
   *
   * @param sPersistentFile
   *            file name with temporary files list
   */
  private void persistNewNative(String sPersistentFile) {
    BufferedWriter writer = null;
    try {
      writer = new BufferedWriter(new FileWriter(sPersistentFile));
      for (File fileNative : this.hsNativeFile) {
        writer.write(fileNative.getCanonicalPath());
        writer.newLine();

        // The temporary file with native library is marked
        // as deleteOnExit() but VM does not close it and it remains
        // open.
        // Attempt to explicitly delete the file fails with "false"
        // because VM does not release the file handle.
        fileNative.delete(); // returns "false"
      }
    } catch (IOException e) {
      // Ignore. In the worst case temp files will accumulate.
    } finally {
      if (writer != null) {
        try {
          writer.close();
        } catch (IOException e) {
        }
      }
    }
  } // persistNewNative()

  // --------------------------------separator--------------------------------
  static int ______ACCESS;

  /**
   * Checks how the application was loaded: from JAR or file system.
   *
   * @return true if application was started from JAR
   */
  public boolean isLaunchedFromJar() {
    return (this.lstJarFile.size() > 0);
  } // isLaunchedFromJar()

  /**
   * Returns the name of the jar file main class, or null if no "Main-Class"
   * manifest attributes was defined.
   *
   * @return main class declared in JAR's manifest
   */
  public String getManifestMainClass() {
    Attributes attr = null;
    if (isLaunchedFromJar()) {
      try {
        // The first element in array is the top level JAR
        Manifest m = this.lstJarFile.get(0).getManifest();
        attr = m.getMainAttributes();
      } catch (IOException e) {
      }
    }
    return (attr == null ? null : attr.getValue(Attributes.Name.MAIN_CLASS));
  }

  /**
   * Invokes main() method on class with provided parameters.
   *
   * @param sClass
   *            class name in form "MyClass" for default package or
   *            "com.abc.MyClass" for class in some package
   *
   * @param args
   *            arguments for the main() method or null
   *
   * @throws Throwable
   *             wrapper for many exceptions thrown while
   *             <p>
   *             (1) main() method lookup: ClassNotFoundException,
   *             SecurityException, NoSuchMethodException
   *             <p>
   *             (2) main() method launch: IllegalArgumentException,
   *             IllegalAccessException (disabled)
   *             <p>
   *             (3) Actual cause of InvocationTargetException
   *
   *             See
   *             {@link "http://java.sun.com/developer/Books/javaprogramming/JAR/api/jarclassloader.html"}
   *             and
   *             {@link "http://java.sun.com/developer/Books/javaprogramming/JAR/api/example-1dot2/JarClassLoader.java"}
   */
  public void invokeMain(String sClass, String[] args) throws Throwable {
    Class<?> clazz = loadClass(sClass);
    log("Launch: %s.main(); Loader: %s", sClass, clazz.getClassLoader());
    Method method = clazz.getMethod("main",
        new Class<?>[] { String[].class });

    boolean bValidModifiers = false;
    boolean bValidVoid = false;

    if (method != null) {
      method.setAccessible(true); // Disable IllegalAccessException
      int nModifiers = method.getModifiers(); // main() must be
      // "public static"
      bValidModifiers = Modifier.isPublic(nModifiers)
          && Modifier.isStatic(nModifiers);

      Class<?> clazzRet = method.getReturnType(); // main() must be "void"
      bValidVoid = (clazzRet == void.class);
    }
    if (method == null || !bValidModifiers || !bValidVoid) {
      throw new NoSuchMethodException("The main() method in class \""
          + sClass + "\" not found.");
    }

    // Invoke method.
    // Crazy cast "(Object)args" because param is: "Object... args"
    try {
      method.invoke(null, (Object) args);
    } catch (InvocationTargetException e) {
      throw e.getTargetException();
    }
  } // invokeMain()

  // --------------------------------separator--------------------------------
  static int ______OVERRIDE;

  /**
   * ClassLoader JavaDoc encourages overriding findClass(String) in derived
   * class rather than overriding this method. This does not work for loading
   * classes from a JAR. Default implementation of loadClass() is able to load
   * a class from a JAR without calling findClass(). This will "infect" the
   * loaded class with a system class loader. The system class loader will be
   * used to load all dependent classes and will fail for jar-in-jar classes.
   *
   * See also: http://www.cs.purdue.edu/homes/jv/smc/pubs/liang-oopsla98.pdf
   */
  @Override
  protected synchronized Class<?> loadClass(String sClassName,
      boolean bResolve) throws ClassNotFoundException {
    log("Loading: %s (resolve=%b)", sClassName, bResolve);
    Class<?> c = null;
    try {
      // Step 1. Load from JAR.
      if (isLaunchedFromJar()) {
        try {
          c = findJarClass(sClassName);
          log("Loaded %s from JAR by %s", sClassName, getClass()
              .getName());
          return c;
        } catch (JarClassLoaderException e) {
          if (e.getCause() == null) {
            log("Not found %s in JAR by %s", e.getMessage(),
                getClass().getName());
          } else {
            log("Error %s in JAR by %s", e.getCause(), getClass()
                .getName());
          }
          // keep looking
        }
      }
      // Step 2. Load by parent (usually system) class loader.
      // Call findSystemClass() AFTER attempt to find in a JAR.
      // If it called BEFORE it will load class-in-jar using
      // SystemClassLoader and "infect" it with SystemClassLoader.
      // The SystemClassLoader will be used to load all dependent
      // classes. SystemClassLoader will fail to load a class from
      // jar-in-jar and to load dll-in-jar.
      try {
        // No need to call findLoadedClass(sClassName)
        // because it's called inside:
        ClassLoader cl = getParent();
        c = cl.loadClass(sClassName);
        log("Loaded %s by %s", sClassName, cl.getClass().getName());
        return c;
      } catch (ClassNotFoundException e) {
        // keep looking
      }
      // What else?
      throw new ClassNotFoundException("Failure to load: " + sClassName);
    } finally {
      if (c != null && bResolve) {
        resolveClass(c);
      }
    }
  } // loadClass()

  /**
   * @see java.lang.ClassLoader#getResource(java.lang.String)
   */
  @Override
  public URL getResource(String sName) {
    if (isLaunchedFromJar()) {
      JarEntryInfo inf = findJarEntry(sName);
      return inf == null ? null : inf.getURL();
    }
    return getParent().getResource(sName);
  } // getResource()

  /**
   * @see java.lang.ClassLoader#getResources(java.lang.String)
   */
  @Override
  public Enumeration<URL> getResources(String sName) throws IOException {
    if (isLaunchedFromJar()) {
      List<JarEntryInfo> lstJarEntry = findJarEntries(sName);
      List<URL> lstURL = new ArrayList<URL>();
      for (JarEntryInfo inf : lstJarEntry) {
        URL url = inf.getURL();
        if (url != null) {
          lstURL.add(url);
        }
      }
      return Collections.enumeration(lstURL);
    }
    return getParent().getResources(sName);
  } // getResources()

  /**
   * @see java.lang.ClassLoader#getResourceAsStream(java.lang.String)
   */
  @Override
  public InputStream getResourceAsStream(String sName) {
    if (isLaunchedFromJar()) {
      JarEntryInfo inf = findJarEntry(sName);
      if (inf != null) {
        try {
          return inf.jarFile.getInputStream(inf.jarEntry);
        } catch (IOException e) {
        }
      }
      return null;
    }
    return getParent().getResourceAsStream(sName);
  } // getResourceAsStream()

  /**
   * @see java.lang.ClassLoader#findLibrary(java.lang.String)
   */
  @Override
  protected String findLibrary(String sLib) {
    JarEntryInfo inf = findJarNativeEntry(sLib);
    if (inf != null) {
      try {
        File fileNative = createTempFile(inf);
        log("Loading native library: %s from temp file %s",
            inf.jarEntry, getFilename4Log(fileNative));
        this.hsNativeFile.add(fileNative);
        return fileNative.getAbsolutePath();
      } catch (JarClassLoaderException e) {
        log("Failure to load native library %s: %s", sLib, e.toString());
      }
    }
    return null;
  } // findLibrary()

  // --------------------------------separator--------------------------------
  static int ______HELPERS;

  /**
   * Read JAR entry and returns byte array of this JAR entry. This is a helper
   * method to load JAR entry into temporary file.
   *
   * @param inf
   *            JAR entry information object
   * @return byte array for the specified JAR entry
   * @throws JarClassLoaderException
   */
  private static byte[] getJarBytes(JarEntryInfo inf)
      throws JarClassLoaderException {
    DataInputStream dis = null;
    byte[] a_by = null;
    try {
      long lSize = inf.jarEntry.getSize();
      if (lSize <= 0 || lSize >= Integer.MAX_VALUE) {
        throw new JarClassLoaderException("Invalid size " + lSize
            + " for entry " + inf.jarEntry);
      }
      a_by = new byte[(int) lSize];
      InputStream is = inf.jarFile.getInputStream(inf.jarEntry);
      dis = new DataInputStream(is);
      dis.readFully(a_by);
    } catch (IOException e) {
      throw new JarClassLoaderException(null, e);
    } finally {
      if (dis != null) {
        try {
          dis.close();
        } catch (IOException e) {
        }
      }
    }
    return a_by;
  } // getJarBytes()

  /**
   * Call this method after UIManager.setLookAndFeel(..) to preload non
   * standard UI classes. <br>
   * At runtime some JComponent class tries to load LaF UI class. The JVM uses
   * the JComponent's class loader, which is system class loader to load UI
   * class and fails because LaF classes reside in an external JAR. A
   * workaround is to preload LaF classes explicitly. <br>
   * See details
   * https://lists.xcf.berkeley.edu/lists/advanced-java/2001-January
   * /015374.html
   *
   * @throws ClassNotFoundException If the class could not be found.
   */
  public static void loadLookAndFeel() throws ClassNotFoundException {
    LookAndFeel laf = UIManager.getLookAndFeel();
    if (laf == null) {
      return; // never null
    }
    ClassLoader cl = laf.getClass().getClassLoader();
    if (cl == null) {
      return; // null for system class loader (?)
    }

    // Does not work: cl.getClass().equals(JarClassLoader.class)
    if (cl.getClass().getName().equals(JarClassLoader.class.getName())) {
      UIDefaults uidef = UIManager.getDefaults();
      Enumeration<?> en = uidef.keys();
      while (en.hasMoreElements()) {
        String sClass = (String) en.nextElement();
        if (sClass.endsWith("UI")) {
          Object obj = uidef.get(sClass);
          // If the obj is java.lang.String load the class,
          // otherwise it's already loaded java.lang.Class
          if (obj instanceof String) {
            Class<?> clazz = cl.loadClass((String) obj);
            uidef.put(clazz.getName(), clazz);
          }
        }
      }
    }
  } // loadLookAndFeel()

  private String getFilename4Log(File file) {
    if (this.logger != null) {
      try {
        // In form "C:\Documents and Settings\..."
        return file.getCanonicalPath();
      } catch (IOException e) {
        // In form "C:\DOCUME~1\..."
        return file.getAbsolutePath();
      }
    }
    return null;
  } // getFilename4Log()

  private void log(String sMsg, Object... obj) {
    if (this.logger != null) {
      this.logger.printf("JarClassLoader: " + sMsg + "\n", obj);
    }
  } // log()

  /**
   * Inner class with JAR entry information. Keeps JAR file and entry object.
   */
  private static class JarEntryInfo {
    JarFile jarFile;
    JarEntry jarEntry;

    JarEntryInfo(JarFile jarFile, JarEntry jarEntry) {
      this.jarFile = jarFile;
      this.jarEntry = jarEntry;
    }

    URL getURL() {
      try {
        return new URL("jar:file:" + this.jarFile.getName() + "!/"
            + this.jarEntry);
      } catch (MalformedURLException e) {
        return null;
      }
    }

    String getName() {
      return this.jarEntry.getName().replace('/', '_');
    }

    @Override
    public String toString() {
      return "JAR: " + this.jarFile.getName() + " ENTRY: " + this.jarEntry;
    }
  } // inner class JarEntryInfo

  /**
   * Inner class to handle specific for the JarClassLoader exceptions
   */
  @SuppressWarnings("serial")
  private static class JarClassLoaderException extends Exception {
    JarClassLoaderException(String sMsg) {
      super(sMsg);
    }

    JarClassLoaderException(String sMsg, Throwable eCause) {
      super(sMsg, eCause);
    }

    String getMessageAll() {
      StringBuilder sb = new StringBuilder();
      for (Throwable e = this; e != null; e = e.getCause()) {
        if (sb.length() > 0) {
          sb.append(" / ");
        }
        String sMsg = e.getMessage();
        if (sMsg == null || sMsg.length() == 0) {
          sMsg = e.getClass().getSimpleName();
        }
        sb.append(sMsg);
      }
      return sb.toString();
    }
  } // inner class JarClassLoaderException

} // class JarClassLoader
TOP

Related Classes of com.jdotsoft.jarloader.JarClassLoader$JarEntryInfo

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.
.js','ga'); ga('create', 'UA-20639858-1', 'auto'); ga('send', 'pageview');