Package

Source Code of JarMain

/**
* Copyright (c) 2010-2012 Engine Yard, Inc.
* Copyright (c) 2007-2009 Sun Microsystems, Inc.
* This source code is available under the MIT license.
* See the file LICENSE.txt for details.
*/

import java.io.File;
import java.io.IOException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class JarMain implements Runnable {
   
    static final String MAIN = "/" + JarMain.class.getName().replace('.', '/') + ".class";

    final boolean debug = isDebug();
   
    protected final String[] args;
    protected final String archive;
    private final String path;
   
    protected File extractRoot;

    protected URLClassLoader classLoader;

    JarMain(String[] args) {
        this.args = args;
        URL mainClass = getClass().getResource(MAIN);
        try {
            this.path = mainClass.toURI().getSchemeSpecificPart();
        }
        catch (URISyntaxException e) {
            throw new RuntimeException(e);
        }
        archive = this.path.replace("!" + MAIN, "").replace("file:", "");
       
        Runtime.getRuntime().addShutdownHook(new Thread(this));
    }
   
    protected URL[] extractArchive() throws Exception {
        final JarFile jarFile = new JarFile(archive);
        try {
            Map<String, JarEntry> jarNames = new HashMap<String, JarEntry>();
            for (Enumeration<JarEntry> e = jarFile.entries(); e.hasMoreElements(); ) {
                JarEntry entry = e.nextElement();
                String extractPath = getExtractEntryPath(entry);
                if ( extractPath != null ) jarNames.put(extractPath, entry);
            }

            extractRoot = File.createTempFile("jruby", "extract");
            extractRoot.delete(); extractRoot.mkdirs();

            final List<URL> urls = new ArrayList<URL>();
            for (Map.Entry<String, JarEntry> e : jarNames.entrySet()) {
                URL entryURL = extractEntry(e.getValue(), e.getKey());
                if (entryURL != null) urls.add( entryURL );
            }
            return (URL[]) urls.toArray(new URL[urls.size()]);
        }
        finally {
            jarFile.close();
        }
    }

    protected String getExtractEntryPath(final JarEntry entry) {
        final String name = entry.getName();
        if ( name.startsWith("META-INF/lib") && name.endsWith(".jar") ) {
            return name.substring(name.lastIndexOf("/") + 1);
        }
        return null; // do not extract entry
    }
   
    protected URL extractEntry(final JarEntry entry, final String path) throws Exception {
        final File file = new File(extractRoot, path);
        if ( entry.isDirectory() ) {
            file.mkdirs();
            return null;
        }
        final String entryPath = entryPath(entry.getName());
        final InputStream entryStream;
        try {
            entryStream = new URI("jar", entryPath, null).toURL().openStream();
        }
        catch (IllegalArgumentException e) {
            // TODO gems '%' file name "encoding" ?!
            debug("failed to open jar:" + entryPath + " skipping entry: " + entry.getName(), e);
            return null;
        }
        final File parent = file.getParentFile();
        if ( parent != null ) parent.mkdirs();
        FileOutputStream outStream = new FileOutputStream(file);
        final byte[] buf = new byte[65536];
        try {
            int bytesRead = 0;
            while ((bytesRead = entryStream.read(buf)) != -1) {
                outStream.write(buf, 0, bytesRead);
            }
        }
        finally {
            entryStream.close();
            outStream.close();
            file.deleteOnExit();
        }
        if (false) debug(entry.getName() + " extracted to " + file.getPath());
        return file.toURI().toURL();
    }

    protected String entryPath(String name) {
        if ( ! name.startsWith("/") ) name = "/" + name;
        return path.replace(MAIN, name);
    }

    protected Object newScriptingContainer(final URL[] jars) throws Exception {
        System.setProperty("org.jruby.embed.class.path", "");
        classLoader = new URLClassLoader(jars);
        Class scriptingContainerClass = Class.forName("org.jruby.embed.ScriptingContainer", true, classLoader);
        Object scriptingContainer = scriptingContainerClass.newInstance();
        debug("scripting container class loader urls: " + Arrays.toString(jars));
        invokeMethod(scriptingContainer, "setArgv", (Object) args);
        invokeMethod(scriptingContainer, "setClassLoader", new Class[] { ClassLoader.class }, classLoader);
        return scriptingContainer;
    }
   
    protected int launchJRuby(final URL[] jars) throws Exception {
        final Object scriptingContainer = newScriptingContainer(jars);
        debug("invoking " + archive + " with: " + Arrays.deepToString(args));
        Object outcome = invokeMethod(scriptingContainer, "runScriptlet", launchScript());
        return ( outcome instanceof Number ) ? ( (Number) outcome ).intValue() : 0;
    }

    protected String launchScript() {
        return
        "begin\n" +
        "  require 'META-INF/init.rb'\n" +
        "  require 'META-INF/main.rb'\n" +
        "  0\n" +
        "rescue SystemExit => e\n" +
        "  e.status\n" +
        "end";
    }
   
    protected int start() throws Exception {
        final URL[] jars = extractArchive();
        return launchJRuby(jars);
    }

    protected void debug(String msg) {
        debug(msg, null);
    }

    protected void debug(String msg, Throwable t) {
        if (debug) System.out.println(msg);
        if (debug && t != null) t.printStackTrace(System.out);
    }

    protected void warn(String msg) {
        System.out.println("WARNING: " + msg);
    }
   
    protected void delete(File f) {
        try {
          if (f.isDirectory() && !isSymlink(f)) {
              File[] children = f.listFiles();
              for (int i = 0; i < children.length; i++) {
                  delete(children[i]);
              }
          }
          f.delete();
        } catch (IOException e) {
            System.err.println("error: " + e.toString());
        }
    }

    protected boolean isSymlink(File file) throws IOException {
      if (file == null)
        throw new NullPointerException("File must not be null");
      File canon;
      if (file.getParent() == null) {
        canon = file;
      } else {
        File canonDir = file.getParentFile().getCanonicalFile();
        canon = new File(canonDir, file.getName());
      }
      return !canon.getCanonicalFile().equals(canon.getAbsoluteFile());
    }

    public void run() {
        // If the URLClassLoader isn't closed, on Windows, temp JARs won't be cleaned up
        try {
            invokeMethod(classLoader, "close");
        }
        catch(NoSuchMethodException e) { } // We're not being run on Java >= 7
        catch(Exception e) {
            System.err.println("error: " + e.toString());
        }

        if ( extractRoot != null ) delete(extractRoot);
    }

    public static void main(String[] args) {
        doStart(new JarMain(args));
    }

    protected static void doStart(final JarMain main) {
        try {
            int exit = main.start();
            if(isSystemExitEnabled()) System.exit(exit);
        } catch (Exception e) {
            System.err.println("error: " + e.toString());
            Throwable t = e;
            while (t.getCause() != null && t.getCause() != t) {
                t = t.getCause();
            }
            if (isDebug()) {
                t.printStackTrace();
            }
            System.exit(1);
        }
    }
   
    protected static Object invokeMethod(final Object self, final String name, final Object... args)
        throws NoSuchMethodException, IllegalAccessException, Exception {
       
        final Class[] signature = new Class[args.length];
        for ( int i = 0; i < args.length; i++ ) signature[i] = args[i].getClass();
        return invokeMethod(self, name, signature, args);
    }

    protected static Object invokeMethod(final Object self, final String name, final Class[] signature, final Object... args)
        throws NoSuchMethodException, IllegalAccessException, Exception {
        Method method = self.getClass().getDeclaredMethod(name, signature);
        try {
            return method.invoke(self, args);
        }
        catch (InvocationTargetException e) {
            Throwable target = e.getTargetException();
            if (target instanceof Exception) {
                throw (Exception) target;
            }
            throw e;
        }
    }
   
    static boolean isDebug() {
        return Boolean.getBoolean("warbler.debug");
    }

    /**
     * if warbler.skip_system_exit system property is defined, we will not
     * call System.exit in the normal flow. System.exit can cause problems
     * for wrappers like procrun
     */
    private static boolean isSystemExitEnabled(){
        return System.getProperty("warbler.skip_system_exit") == null; //omission enables System.exit use
    }

}
TOP

Related Classes of JarMain

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.