/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2007-2009 Bull S.A.S.
 * Contact: jonas-team@ow2.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.launcher.felix;

import java.io.File;
import java.rmi.RMISecurityManager;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.naming.Context;

import org.apache.felix.framework.Felix;
import org.apache.felix.framework.util.FelixConstants;
import org.apache.felix.main.AutoActivator;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.ow2.jonas.launcher.felix.util.IOUtils;
import org.ow2.jonas.launcher.felix.util.JOnASUtils;

/**
 * JOnAS Running on OSGi/Felix.
 * @author Guillaume Sauthier
 */
public class JOnAS {

    /**
     * OSGi Gateway instance.
     */
    private Felix felix;

    /**
     * Property for the security manager.
     */
    private static final String SECURITY_MANAGER = "jonas.security.manager";

    /**
     * Creates a new configured framework instance.
     * @param forceCleanUp true if cache directory must be deleted.
     * @throws Exception cannot create the internal gateway.
     */
    @SuppressWarnings("unchecked")
    public JOnAS(final boolean forceCleanUp) throws Exception {
        System.out.println("\nWelcome to OW2 JOnAS (Running on Felix).");
        System.out.println("-----------------------------------------------\n");

        // RMI Security Manager
        boolean useSecurityManager = new Boolean(JOnASUtils.getServerProperty(SECURITY_MANAGER, "true").trim()).booleanValue();

        if (useSecurityManager) {
            if (System.getSecurityManager() == null) {
                System.setSecurityManager(new RMISecurityManager());
            }
        }

        // Init required System properties
        initializeSystemProperties();

        // Get a new provider
        IConfigurationProvider provider = getConfigurationProvider();

        // Retrieve the configuration
        Map configProps = provider.getConfiguration();

        // Delete the cache directory if requested
        if (forceCleanUp) {
            IOUtils.deleteDir(new File((String) configProps.get(Constants.FRAMEWORK_STORAGE)));
        }

        List list = new ArrayList();
        list.add(new AutoActivator(configProps));
        configProps.put(FelixConstants.SYSTEMBUNDLE_ACTIVATORS_PROP, list);

        // Create the instance
        felix = new Felix(configProps);

        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                try {
                    // Only stop if the system is still running
                    if (felix.getState() == Bundle.ACTIVE) {
                        felix.stop();
                    }
                } catch (Exception e) {
                    e.printStackTrace(System.err);
                }
            }
        });
    }

    /**
     * Start JOnAS on Felix.
     * @throws Exception Thrown if the start fails
     */
    public void start() throws Exception {
        // Start it !
        felix.start();

        startTransientBundles();

        // Wait for framework to stop to exit the VM.
        felix.waitForStop(0);
        System.exit(0);
    }

    /**
     * Stop JOnAS.
     * @throws Exception Thrown if the stop fails
     */
    public void stop() throws Exception {
        // Only stop if the system is still running
        if (felix.getState() == Bundle.ACTIVE) {
            felix.stop();
        }
    }

    /**
     * @return a new {@link IConfigurationProvider}.
     */
    protected IConfigurationProvider getConfigurationProvider() {
        // can be sub-classed
        return new DefaultConfigurationProvider();
    }

    /**
     * Start a new JOnAS.
     * @param args not used
     * @throws Exception if something failed
     */
    public static void main(final String[] args) throws Exception {
        JOnAS jonas = new JOnAS(Boolean.getBoolean("jonas.cache.clean"));
        jonas.start();
    }

    /**
     * Initialize required System properties (using reasonable default values
     * when needed).
     */
    protected void initializeSystemProperties() {
        // Set the current date to calculate the server startup time
        System.setProperty("jonas.start.date", Long.toString(System.currentTimeMillis()));

        // Sets the Wrappers that monolog will remove in order to print the
        // right caller
        System.setProperty("monolog.wrappers", "mx4j.log.CommonsLogger,mx4j.log.Logger,java.util.logging.Logger,"
                + "org.ow2.util.log.JDKLogger,org.apache.juli.logging.DirectJDKLog,"
                + "org.ow2.carol.util.configuration.TraceCarol,org.slf4j.impl.JCLLoggerAdapter");

        // Corba/JacORB Properties
        System.setProperty("org.omg.CORBA.ORBClass", "org.jacorb.orb.ORB");
        System.setProperty("org.omg.CORBA.ORBSingletonClass", "org.jacorb.orb.ORBSingleton");
        System.setProperty("org.omg.PortableInterceptor.ORBInitializerClass.standard_init",
                "org.jacorb.orb.standardInterceptors.IORInterceptorInitializer");
        // Corba/Carol Properties
        System.setProperty("javax.rmi.CORBA.PortableRemoteObjectClass", "org.ow2.carol.rmi.multi.MultiPRODelegate");
        System.setProperty("javax.rmi.CORBA.UtilClass", "org.ow2.carol.util.delegate.UtilDelegateImpl");

        // Naming properties
        System.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.ow2.carol.jndi.intercept.spi.InterceptorInitialContextFactory");

        // Security Properties
        setDefaultPropertyIfNotSet("java.security.policy", IOUtils.getSystemPath(JOnASUtils.getJOnASBase(), "conf/java.policy"));
        setDefaultPropertyIfNotSet("java.security.auth.login.config", IOUtils.getSystemPath(JOnASUtils.getJOnASBase(),
                "conf/jaas.config"));

    }

    /**
     * If the property was already set, do not change its value, otherwise, use
     * the default.
     * @param key property name
     * @param def default property value
     */
    protected void setDefaultPropertyIfNotSet(final String key, final String def) {
        System.setProperty(key, System.getProperty(key, def));
    }

    /**
     * The bundle start operation is transient for TUI and GUI bundles.
     * @throws BundleException If bundles startup fails
     */
    protected void startTransientBundles() throws BundleException {
        Bundle[] bundles = felix.getBundleContext().getBundles();
        for (Bundle bundle : bundles) {
            if (bundle.getSymbolicName().equals("org.apache.felix.shell.tui")) {
                // Use Text UI ?
                if (Boolean.getBoolean("jonas.felix.tui.enabled")) {
                    startTransient(bundle);
                }
            }
            if (bundle.getSymbolicName().startsWith("org.apache.felix.shell.gui")) {
                // Use GUI ?
                if (Boolean.getBoolean("jonas.felix.gui.enabled")) {
                    startTransient(bundle);
                }
            }
        }
    }

    /**
     * The bundle start operation is transient for the given bundle.
     * @throws BundleException If bundle startup fails
     */
    protected void startTransient(final Bundle bundle) throws BundleException {
        bundle.start(Bundle.START_TRANSIENT);
    }

}
