org.columba.core.main.Bootstrap.java Source code

Java tutorial

Introduction

Here is the source code for org.columba.core.main.Bootstrap.java

Source

// The contents of this file are subject to the Mozilla Public License Version
// 1.1
//(the "License"); you may not use this file except in compliance with the
//License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
//
//Software distributed under the License is distributed on an "AS IS" basis,
//WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
//for the specific language governing rights and
//limitations under the License.
//
//The Original Code is "The Columba Project"
//
//The Initial Developers of the Original Code are Frederik Dietz and Timo
// Stich.
//Portions created by Frederik Dietz and Timo Stich are Copyright (C) 2003.
//
//All Rights Reserved.
package org.columba.core.main;

import java.io.File;
import java.io.FilenameFilter;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLClassLoader;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import javax.swing.JPopupMenu;
import javax.swing.RepaintManager;
import javax.swing.SwingUtilities;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.ParseException;
import org.columba.core.association.AssociationStore;
import org.columba.core.backgroundtask.BackgroundTaskManager;
import org.columba.core.base.OSInfo;
import org.columba.core.component.ComponentManager;
import org.columba.core.config.Config;
import org.columba.core.config.DefaultConfigDirectory;
import org.columba.core.config.SaveConfig;
import org.columba.core.desktop.ColumbaDesktop;
import org.columba.core.gui.base.DebugRepaintManager;
import org.columba.core.gui.frame.FrameManager;
import org.columba.core.gui.profiles.Profile;
import org.columba.core.gui.profiles.ProfileManager;
import org.columba.core.gui.themes.ThemeSwitcher;
import org.columba.core.gui.trayicon.ColumbaTrayIcon;
import org.columba.core.gui.util.FontProperties;
import org.columba.core.gui.util.StartUpFrame;
import org.columba.core.logging.Logging;
import org.columba.core.plugin.PluginManager;
import org.columba.core.resourceloader.GlobalResourceLoader;
import org.columba.core.scripting.service.ServiceManager;
import org.columba.core.shutdown.ShutdownManager;
import org.columba.core.util.StackProfiler;
import org.columba.core.versioninfo.VersionInfo;
import org.frapuccino.swing.ActiveWindowTracker;

import sun.misc.URLClassPath;

public class Bootstrap {

    private static final Logger LOG = Logger.getLogger("org.columba.core.main"); //$NON-NLS-1$

    private static final String RESOURCE_PATH = "org.columba.core.i18n.global"; //$NON-NLS-1$

    // TODO @author hubms have this flags, until the speed of the entitymanager
    // is improved
    public static boolean ENABLE_TAGS = false;

    private String path;

    private boolean showSplashScreen = true;

    public void run(String args[]) throws Exception {

        addNativeJarsToClasspath();
        setLibraryPath();

        // For the Mac ScreenBarMenus to work, this must be declared before
        // *ANY* AWT / Swing gets initialised. Do *NOT* move it to plugin init
        // location because that is too late...
        if (OSInfo.isMac()) {
            System.setProperty("apple.laf.useScreenMenuBar", "true");
            System.setProperty("com.apple.mrj.application.apple.menu.about.name", "Columba");
        }

        Logging.createDefaultHandler();
        registerCommandLineArguments();

        StackProfiler profiler = new StackProfiler();
        profiler.push("main");
        profiler.push("config");
        profiler.push("profile");
        // prompt user for profile
        Profile profile = ProfileManager.getInstance().getProfile(path);
        profiler.pop("profile");

        // initialize configuration with selected profile
        DefaultConfigDirectory.getInstance().setCurrentPath(profile.getLocation());
        new Config(profile.getLocation());
        profiler.pop("config");

        // if user doesn't overwrite logger settings with commandline arguments
        // just initialize default logging
        // Logging.createDefaultHandler();
        Logging.createDefaultFileHandler(DefaultConfigDirectory.getDefaultPath());

        for (int i = 0; i < args.length; i++) {
            LOG.info("arg[" + i + "]=" + args[i]);
        }

        SessionController.passToRunningSessionAndExit(args);

        // enable debugging of repaint manager to track down swing gui
        // access from outside the awt-event dispatcher thread

        if (Logging.DEBUG)
            RepaintManager.setCurrentManager(new DebugRepaintManager());

        // use heavy-weight popups to ensure they are always on top
        JPopupMenu.setDefaultLightWeightPopupEnabled(false);

        // keep track of active windows (used by dialogs which don't have a
        // direct parent)
        ActiveWindowTracker.class.getClass();

        // show splash screen
        StartUpFrame frame = null;
        if (showSplashScreen) {
            frame = new StartUpFrame();
            frame.setVisible(true);
        }

        // register protocol handler
        System.setProperty("java.protocol.handler.pkgs",
                "org.columba.core.url|" + System.getProperty("java.protocol.handler.pkgs", ""));

        profiler.push("i18n");
        // load user-customised language pack
        GlobalResourceLoader.loadLanguage();
        profiler.pop("i18n");

        SaveConfig task = new SaveConfig();
        BackgroundTaskManager.getInstance().register(task);
        ShutdownManager.getInstance().register(task);

        profiler.push("plugins core");
        initPlugins();
        profiler.pop("plugins core");

        profiler.push("components");
        // init all components
        ComponentManager.getInstance().init();
        ComponentManager.getInstance().registerCommandLineArguments();
        profiler.pop("components");

        // set Look & Feel
        ThemeSwitcher.setTheme();

        // initialize platform-dependant services
        initPlatformServices();

        // init font configuration
        new FontProperties();

        // set application wide font
        FontProperties.setFont();

        //       handle commandline parameters
        if (handleCoreCommandLineParameters(args)) {
            System.exit(0);
        }

        // handle the commandline arguments of the modules
        ComponentManager.getInstance()
                .handleCommandLineParameters(ColumbaCmdLineParser.getInstance().getParsedCommandLine());

        profiler.push("plugins external");
        // now load all available plugins
        // PluginManager.getInstance().initExternalPlugins();
        profiler.pop("plugins external");

        profiler.push("frames");

        // restore frames of last session
        if (ColumbaCmdLineParser.getInstance().getRestoreLastSession()) {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    FrameManager.getInstance().openStoredViews();
                }
            });
        }

        /* initialize services before dismissing the splash screen */
        ServiceManager.getInstance().initServices();

        // register shutdown manager
        ShutdownManager.getInstance().register(new Runnable() {
            public void run() {

                ServiceManager.getInstance().stopServices();
                ServiceManager.getInstance().disposeServices();
            }
        });

        profiler.pop("frames");

        // Add the tray icon to the System tray
        // ColumbaTrayIcon.getInstance().addToSystemTray(
        // FrameManager.getInstance().getActiveFrameMediator()
        // .getFrameMediator());

        profiler.push("tagging");

        // initialize tagging
        if (ENABLE_TAGS) {
            AssociationStore.getInstance().init();
            // register for cleanup
            ShutdownManager.getInstance().register(AssociationStore.getInstance());
        }

        profiler.pop("tagging");

        // hide splash screen
        if (frame != null) {
            frame.setVisible(false);
        }

        // call the postStartups of the modules
        // e.g. check for default mailclient
        ComponentManager.getInstance().postStartup();

        /* everything is up and running, start services */
        ServiceManager.getInstance().startServices();

        profiler.pop("main");

    }

    /**
     * initialize all extension handlers from core, mail and contacts.
     * Additionally, load all internally shipped plugins and last but not least,
     * load all external plugins residing in /plugin directory.
     */
    private void initPlugins() {

        //
        // first load all extension handlers
        //

        // load core extension handlers
        PluginManager.getInstance().addExtensionHandlers("org/columba/core/plugin/extensionhandler.xml");

        // load addressbook extension handler
        PluginManager.getInstance().addExtensionHandlers("org/columba/addressbook/plugin/extensionhandler.xml");

        // load mail extension handler
        PluginManager.getInstance().addExtensionHandlers("org/columba/mail/plugin/extensionhandler.xml");

        // load calendar extension handler
        PluginManager.getInstance().addExtensionHandlers("org/columba/calendar/plugin/extensionhandler.xml");

        // load all internal core plugins
        String path = "org/columba/core/plugin/plugin.xml";
        PluginManager.getInstance().addPlugin(path);

        //
        // following internal components plugin registration
        //

        // load all internal addressbook plugins
        path = "org/columba/addressbook/plugin/plugin.xml";
        PluginManager.getInstance().addPlugin(path);

        // load all internal mail plugins
        path = "org/columba/mail/plugin/plugin.xml";
        PluginManager.getInstance().addPlugin(path);

        // load all internal calendar plugins
        path = "org/columba/calendar/plugin/plugin.xml";
        PluginManager.getInstance().addPlugin(path);

        //
        // now load all external plugins residing in /plugins directory
        //
        PluginManager.getInstance().initExternalPlugins();
    }

    /**
     * registerCommandLineArguments method
     */
    private void registerCommandLineArguments() {
        ColumbaCmdLineParser parser = ColumbaCmdLineParser.getInstance();

        parser.addOption(
                new Option("version", GlobalResourceLoader.getString(RESOURCE_PATH, "global", "cmdline_version")));

        parser.addOption(
                new Option("help", GlobalResourceLoader.getString(RESOURCE_PATH, "global", "cmdline_help")));

        parser.addOption(OptionBuilder.withArgName("path").hasArg().create("profile"));

        parser.addOption(
                new Option("profile", GlobalResourceLoader.getString(RESOURCE_PATH, "global", "cmdline_profile")));

        parser.addOption(
                new Option("debug", GlobalResourceLoader.getString(RESOURCE_PATH, "global", "cmdline_debug")));

        parser.addOption(new Option("nosplash",
                GlobalResourceLoader.getString(RESOURCE_PATH, "global", "cmdline_nosplash")));

        // ComponentPluginHandler handler = null;
        // try {
        // handler = (ComponentPluginHandler) PluginManager.getInstance()
        // .getHandler("org.columba.core.component");
        // handler.registerCommandLineArguments();
        // } catch (PluginHandlerNotFoundException e) {
        // e.printStackTrace();
        // }
    }

    /**
     * Uses the command line parser to validate the passed arguments and invokes
     * handlers to process the detected options.
     */
    private boolean handleCoreCommandLineParameters(String[] args) {
        ColumbaCmdLineParser parser = ColumbaCmdLineParser.getInstance();
        CommandLine commandLine;

        try {
            commandLine = parser.parse(args);
        } catch (ParseException e) {
            // oops, something went wrong
            System.err.println("Parsing failed.  Reason: " + e.getMessage());
            parser.printUsage();

            return true;
        }

        if (commandLine.hasOption("help")) {
            parser.printUsage();

            return true;
        }

        // TODO: Make this hack more i18n compatible
        if (commandLine.hasOption("version")) {
            LOG.info(MessageFormat.format(GlobalResourceLoader.getString(RESOURCE_PATH, "global", "info_version"), //$NON-NLS-2$
                    new Object[] { VersionInfo.getVersion(), VersionInfo.getBuildDate() }));
            System.out.println(
                    "Columba (" + VersionInfo.getVersion() + ") built " + VersionInfo.getBuildDate() + "\n");
            return true;
        }

        if (commandLine.hasOption("profile")) {

            // TODO: There's probably a better way to do this hack...
            path = commandLine.getArgList().toString();

            // This is necessary because getArgList returns the path in
            // square brackets
            path = path.substring(1, path.length() - 1);
        }

        if (commandLine.hasOption("debug")) {
            Logging.DEBUG = true;
            Logging.setDebugging(true);
        }

        if (commandLine.hasOption("nosplash")) {
            showSplashScreen = false;
        }

        // Do not exit
        return false;
    }

    /**
     * This hacks the classloader to adjust the library path for convenient
     * native support.
     *
     * @author tstich
     *
     * @throws Exception
     */
    private void setLibraryPath() throws Exception {
        String libDir;
        if (OSInfo.isAMD64Bit())
            libDir = "amd64";
        else
            libDir = "lib";

        // Platform maintainers: add your platform here

        String propertyPath = System.getProperty("java.library.path");

        if (OSInfo.isLinux())
            propertyPath += ":native/linux/";
        else if (OSInfo.isMac())
            propertyPath += ":native/mac/";
        else if (OSInfo.isWin32Platform())
            propertyPath += ";native\\win32\\";
        // Platform maintainers: add your platform here

        propertyPath += libDir;

        System.setProperty("java.library.path", propertyPath);
        LOG.info("The java.library.path = " + propertyPath);
        Field fieldSysPath = ClassLoader.class.getDeclaredField("sys_paths");
        fieldSysPath.setAccessible(true);
        if (fieldSysPath != null) {
            fieldSysPath.set(System.class.getClassLoader(), null);
        }
    }

    /**
     * This hacks the classloader to adjust the classpath for convenient native
     * support.
     * <p>
     * I've cleaned this up using our new global class loader. This way we only
     * add a few new <code>URLs</code> to our class loader instead of
     * modifying the system class loader using reflection.
     *
     * @author tstich,fdietz
     *
     * @throws Exception
     */
    private void addNativeJarsToClasspath() throws Exception {
        File nativeDir;

        String libDir;
        if (OSInfo.isAMD64Bit())
            libDir = "amd64";
        else
            libDir = "lib";

        // Setup the path
        // Platform maintainers: add your platform here
        // see also initPlatformServices() method
        if (OSInfo.isLinux())
            nativeDir = new File("native/linux/" + libDir);
        else if (OSInfo.isMac())
            nativeDir = new File("native/mac/" + libDir);
        else if (OSInfo.isWin32Platform())
            nativeDir = new File("native/win32/" + libDir);
        else {
            LOG.info("Native support for Platform not available.");
            return;
        }

        // Find all native jars
        File[] nativeJars = nativeDir.listFiles(new FilenameFilter() {
            public boolean accept(File dir, String name) {
                return name.endsWith("jar") || name.endsWith("jnilib");
            }
        });
        if (nativeJars == null)
            return;

        // @author: fdietz
        //
        // The following line is not working - just don't know why
        // Main.mainClassLoader.addURLs((URL[]) urlList.toArray(new URL[0]));
        //
        // WORKAROUND:
        //
        // Modify the system class loader instead - horrible! But it works!

        // Get the current classpath from the sysloader
        // through reflection
        URLClassLoader sysloader = (URLClassLoader) ClassLoader.getSystemClassLoader();

        Field ucp = URLClassLoader.class.getDeclaredField("ucp");
        ucp.setAccessible(true);
        URLClassPath currentCP = (URLClassPath) ucp.get(sysloader);
        URL[] currentURLs = currentCP.getURLs();

        // add all native jars
        List<URL> urlList = new ArrayList<URL>();
        for (int i = 0; i < nativeJars.length; i++) {
            urlList.add(nativeJars[i].toURL());
        }

        // add the old classpath
        for (int i = 0; i < currentURLs.length; i++) {
            urlList.add(currentURLs[i]);
        }

        // replace with the modified classpath
        ucp.set(sysloader, new URLClassPath((URL[]) urlList.toArray(new URL[0])));
    }

    /**
     * Initialise system dependent stuff
     */
    private void initPlatformServices() {

        // Initialise system dependent stuff
        ColumbaDesktop.getInstance().initActiveDesktop();
        ColumbaTrayIcon.getInstance().initActiveIcon();
    }
}