/*
 * Copyright (C) The Apache Software Foundation. All rights reserved.
 *
 * This software is published under the terms of the Apache Software License
 * version 1.1, a copy of which has been included with this distribution in
 * the LICENSE file.
 */
package org.apache.phoenix;

import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;

import javax.management.MBeanServer;

import org.apache.framework.context.Context;
import org.apache.framework.container.Entry;
import org.apache.framework.configuration.Configuration;
import org.apache.framework.configuration.ConfigurationBuilder;

import org.apache.avalon.configuration.DefaultConfigurationBuilder;
import org.apache.avalon.component.DefaultComponentManager;
import org.apache.avalon.configuration.ConfigurationException;

import org.apache.phoenix.engine.facilities.DefaultContextBuilder;
import org.apache.phoenix.engine.jmx.MBeanLoader;

/**
 * Entry point to phoenix. Call to start the server. Usage:<br />
 * <code>
 * java org.apache.phoenix.Start [--mBeanServer=%SERVER_CLASS] [--configFile=%CONFIG_FILE_URL]
 * </code>
 *
 * @author <a href="mail@leosimons.com">Leo Simons</a>
 * @author <a href="mailto:donaldp@apache.org">Peter Donald</a>
 * @author <a href="mailto:fede@apache.org">Federico Barbieri</a>
 */
public class Start {
    // main engines used
    private static MBeanServer mBeanServer;
        private static String mBeanServerClass;
        private static final String DEFAULT_MBEANSERVER_CLASS = "org.apache.jmx.MBeanServerImpl";
    private static MBeanLoader mBeanLoader;
        private static String mBeanLoaderClass;
        private static final String DEFAULT_MBEANLOADER_CLASS = "org.apache.phoenix.engine.jmx.MBeanLoader";
    private static Kernel kernel;
        private static String kernelClass;
        private static final String DEFAULT_KERNEL_CLASS = "org.apache.phoenix.engine.DefaultKernel";

    // monitor these for status changes
    private static boolean singleton = false;
    private static boolean shutdown = false;
    private static boolean restart = false;

    // properties
    private static String configurationSource;
        private static final String DEFAULT_CONFIGURATION_SOURCE = "../conf/server.xml";


    public void main(final String[] args) {
        if(singleton)
            System.out.println("Sorry, an instance of phoenix is already running. Phoenix cannot be run multiple times in the same VM.");
            System.exit(1);
        singelton = true;

        final Start start = new Start();

        try {
            start.execute(args);
        }
        catch(final Throwable throwable)
        {
            System.out.println("There was an uncaught exception:");
            System.out.println("---------------------------------------------------------");
            System.out.println(throwable.toString());
            System.out.println("---------------------------------------------------------");
            System.out.println("Please check the configuration files and restart phoenix.");
            System.out.println("If the problem persists, contact the Avalon project.  See");
            System.out.println("http://jakarta.apache.org/avalon for more information.");
            System.exit(1);
        }
    }
    private void execute(String[] args) throws Exception {
        try
        {
            final PrivilegedExceptionAction action = new PrivilegedExceptionAction()
            {
                public Object run() throws Exception
                {
                    execute();
                    return null;
                }
            };

            AccessController.doPrivileged( action );
        }
        catch( final PrivilegedActionException pae )
        {
            // only "checked" exceptions will be "wrapped" in a PrivilegedActionException.
            throw pae.getException();
        }
    }
    private void execute() throws Exception {
        this.parseCommandLineOptions(args);
        this.createMBeanServer();
        this.createMBeanLoader();

        this.installKernel();

        kernel.loadServices();
        kernel.installServices();
        kernel.runServices();
        kernel.loadServerApplications();
        kernel.installServerApplications();
        kernel.runServerApplications();

        while(!this.shutdown && !this.restart)
            kernel.run();
        if(this.restart)
            this.restart();
        else {
            kernel.stop();
            kernel.dispose();
            kernel.finalize();
        }
    }

    private void parseCommandLineOptions(String[] args) {
        // start with the defaults
        this.mBeanServerClass = this.DEFAULT_MBEANSERVER_CLASS;
        this.mBeanLoaderClass = this.DEFAULT_MBEANLOADER_CLASS;
        this.kernelClass = this.DEFAULT_KERNEL_CLASS;
        this.configurationSource = this.DEFAULT_CONFIGURATION_SOURCE;

        // TODO...
    }
    private void createMBeanServer() throws ConfigurationException {
        try
        {
            Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
            this.mBeanServer = (MBeanServer)Class.forName(this.mBeanServerClass).newInstance();
        }
        catch(final Exception e)
        {
            throw new ConfigurationException("Failed to create MBean Server of class " + this.mBeanServerClass, e);
        }
    }
    private void createMBeanLoader() throws ConfigurationException {
        try
        {
            Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
            this.mBeanLoader = (MBeanLoader)Class.forName(this.mBeanLoaderClass).newInstance();
        }
        catch(final Exception e)
        {
            throw new ConfigurationException("Failed to create MBean Loader of class " + this.mBeanLoaderClass, e);
        }
    }
    private void installKernel() throws ConfigurationException {
        try
        {
            Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
            this.kernel = (Kernel)Class.forName(this.kernelClass).newInstance();
        } catch(final Exception e)
        {
            throw new ConfigurationException("Failed to create Kernel of class " + this.kernelClass, e);
        }
        kernel.contextualize((new DefaultContextBuilder()).createContext("phoenix", new Entry()));
        kernel.compose(new DefaultComponentManager());
        kernel.configure((new DefaultConfigurationBuilder()).build(this.configurationSource));
        kernel.init();
        kernel.start();
    }
    private void restart() {
        // TODO
    }
}

