All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.redhat.ceylon.launcher.Launcher Maven / Gradle / Ivy

There is a newer version: 1.3.3
Show newest version
package com.redhat.ceylon.launcher;

import java.io.File;
import java.io.FileNotFoundException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.List;
import java.util.logging.ConsoleHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;

import com.redhat.ceylon.common.Constants;

public class Launcher {

    public static void main(String[] args) throws Throwable {
        // we don't need to clean up the class loader when run from main because the JVM will either exit, or
        // keep running with daemon threads in which case it will keep needing this classloader open 
        int exit = run(args);
        // WARNING: NEVER CALL EXIT IF WE STILL HAVE DAEMON THREADS RUNNING AND WE'VE NO REASON TO EXIT WITH A NON-ZERO CODE
        if(exit != 0)
            System.exit(exit);
    }

    public static int run(String... args) throws Throwable {
        return run(false, args);
    }

    public static int run(boolean cleanupClassLoader, String... args) throws Throwable {
        Java7Checker.check();
        CeylonClassLoader loader = getClassLoader();
        try{
            return runInJava7Checked(loader, args);
        }finally{
            if(cleanupClassLoader)
                loader.clearCacheButNotWithThisNameToKeepSpringBootHappy();
        }
    }
    
    // FIXME: perhaps we should clear all the properties we set in there on exit?
    // this may not work for run, if they leave threads running 
    public static int runInJava7Checked(CeylonClassLoader loader, String... args) throws Throwable {
        // If the --sysrep option was set on the command line we set the corresponding system property
        String ceylonSystemRepo = LauncherUtil.getArgument(args, "--sysrep", false);
        if (ceylonSystemRepo != null) {
            System.setProperty(Constants.PROP_CEYLON_SYSTEM_REPO, ceylonSystemRepo);
        }

        ClassLoader ccl = Thread.currentThread().getContextClassLoader();
        try{
            // This is mostly required by CeylonTool.getPluginLoader(), and perhaps by jboss modules
            Thread.currentThread().setContextClassLoader(loader);
            
            // We actually need to construct and set a new class path for the compiler
            // which doesn't use the actual class path used by the JVM but it constructs
            // it's own list looking at the arguments passed on the command line or
            // at the system property "env.class.path" which we will be using here.
            String cp = CeylonClassLoader.getClassPathAsString();
            System.setProperty("env.class.path", cp);

            // Find the main tool class
            String verbose = null;
            Class mainClass = loader.loadClass("com.redhat.ceylon.common.tools.CeylonTool");

            // Set up the arguments for the tool
            Object mainTool = mainClass.newInstance();
            Integer result;
            Method setupMethod = mainClass.getMethod("setup", args.getClass());
            try {
                result = (Integer)setupMethod.invoke(mainTool, (Object)args);
            } catch (InvocationTargetException e) {
                throw e.getCause();
            }
            if (result == 0 /* SC_OK */) {
                try {
                    Method toolGetter = mainClass.getMethod("getTools");
                    Object[] tools = (Object[]) toolGetter.invoke(mainTool);
                    // just use the first one since they share args
                    if(tools != null && tools.length > 0){
                        Method verboseGetter = tools[0].getClass().getMethod("getVerbose");
                        verbose = (String)verboseGetter.invoke(tools[0]);
                    }
                } catch (Exception ex) {
                    // Probably doesn't have a --verbose option
                }

                //boolean verbose = hasArgument(args, "--verbose") && getArgument(args, "--verbose", true) == null;
                initGlobalLogger(verbose);

                try{
                    if (hasVerboseFlag(verbose, "loader")) {
                        Logger log = Logger.getLogger("com.redhat.ceylon.log.loader");
                        log.info("Current directory is '" + LauncherUtil.absoluteFile(new File(".")).getPath() + "'");
                        log.info("Ceylon home directory is '" + LauncherUtil.determineHome() + "'");
                        for (File f : CeylonClassLoader.getClassPath()) {
                            log.info("path = " + f + " (" + (f.exists() ? "OK" : "Not found!") + ")");
                        }
                    }

                    // And finally execute the tool
                    Method execMethod = mainClass.getMethod("execute");
                    try {
                        result = (Integer)execMethod.invoke(mainTool);
                    } catch (InvocationTargetException e) {
                        throw e.getCause();
                    }
                }finally{
                    // make sure we reset it, otherwise it will keep a reference to the CeylonClassLoader
                    LogManager.getLogManager().reset();
                }
            }

            return result.intValue();
        }finally{
            // be sure to restore it to avoid memory leaks
            Thread.currentThread().setContextClassLoader(ccl);
        }
    }

    public static CeylonClassLoader getClassLoader() throws ClassLoaderSetupException {
        try{
            // Create the class loader that knows where to find all the Ceylon dependencies
            CeylonClassLoader ceylonClassLoader = CeylonClassLoader.newInstance();

            // Set some important system properties
            initGlobalProperties();

            return ceylonClassLoader;
        }catch(URISyntaxException e){
            throw new ClassLoaderSetupException(e);
        }catch(MalformedURLException e){
            throw new ClassLoaderSetupException(e);
        }catch(FileNotFoundException e){
            throw new ClassLoaderSetupException(e);
        }
    }

    public static void initGlobalProperties() throws URISyntaxException {
        File ceylonHome = LauncherUtil.determineHome();
        initGlobalProperties(ceylonHome);
    }

    public static void initGlobalProperties(File ceylonHome) throws URISyntaxException {
        System.setProperty(Constants.PROP_CEYLON_HOME_DIR, ceylonHome.getAbsolutePath());
        System.setProperty(Constants.PROP_CEYLON_SYSTEM_REPO, LauncherUtil.determineRepo(ceylonHome).getAbsolutePath());
        System.setProperty(Constants.PROP_CEYLON_SYSTEM_VERSION, LauncherUtil.determineSystemVersion());
    }

    public static void initGlobalLogger(String verbose) {
        try {
            //if no log Manager specified use JBoss LogManager
            String logManager = System.getProperty("java.util.logging.manager");
            if (logManager == null) {
                System.setProperty("java.util.logging.manager", "org.jboss.logmanager.LogManager");
            }

            if (verbose != null) {
                String[] flags = verbose.split(",");
                for (String flag : flags) {
                    flag = flag.trim();
                    if ("all".equals(flag) || flag.isEmpty()) {
                        initLogger(Logger.getLogger(""), true);
                    } else if (flag.matches("^[a-z]+$")) {
                        initLogger(Logger.getLogger("com.redhat.ceylon.log." + flag), true);
                    }
                }
            } else {
                initLogger(Logger.getLogger(""), false);
            }
        } catch (Throwable ex) {
            System.err.println("Warning: log configuration failed: " + ex.getMessage());
        }
    }

    private static void initLogger(Logger logger, boolean verbose) {
        boolean handlersExists = false;
        for (Handler handler : logger.getHandlers()) {
            handlersExists = true;

            //TODO Should we remove this hack? If handler are configured then levels should be too.
            // This is a hack, but at least it works. With a property file our log
            // formatter has to be in the boot class path. This way it doesn't.
            if (handler instanceof ConsoleHandler) {
                handler.setFormatter(CeylonLogFormatter.INSTANCE);
                if (verbose) {
                    handler.setLevel(Level.ALL);
                }
            }
        }
        if (verbose) {
            //TODO do not configure root logger, make it flags aware
            logger.setLevel(Level.ALL);
            if (handlersExists == false) {
                ConsoleHandler handler = new ConsoleHandler();
                handler.setFormatter(CeylonLogFormatter.INSTANCE);
                handler.setLevel(Level.ALL);
                logger.addHandler(handler);
            }
        }
    }
    
    // Returns true if one of the argument passed matches one of the flags given to
    // --verbose=... on the command line or if one of the flags is "all"
    private static boolean hasVerboseFlag(String verbose, String flag) {
        if (verbose == null) {
            return false;
        }
        if (verbose.isEmpty()) {
            return true;
        }
        List lst = Arrays.asList(verbose.split(","));
        if (lst.contains("all")) {
            return true;
        }
        return lst.contains(flag);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy