/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2007-2008 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 (at your option) 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.cmi.internal;

import java.io.File;
import java.net.URL;
import java.util.Properties;

import javax.ejb.EJBObject;

import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceRegistration;
import org.ow2.carol.util.configuration.ConfigurationRepository;
import org.ow2.cmi.controller.server.AbsServerClusterViewManager;
import org.ow2.cmi.controller.server.ServerClusterViewManager;
import org.ow2.cmi.info.CMIInfoExtractor;
import org.ow2.cmi.info.CMIInfoExtractorException;
import org.ow2.cmi.info.CMIInfoRepository;
import org.ow2.cmi.info.ClusteredObjectInfo;
import org.ow2.cmi.osgi.ILoadBalancingService;
import org.ow2.jonas.cmi.CmiService;
import org.ow2.jonas.jmx.JmxService;
import org.ow2.jonas.lib.bootstrap.JProp;
import org.ow2.jonas.lib.execution.ExecutionResult;
import org.ow2.jonas.lib.execution.IExecution;
import org.ow2.jonas.lib.execution.RunnableHelper;
import org.ow2.jonas.lib.service.AbsServiceImpl;
import org.ow2.jonas.lib.util.Log;
import org.ow2.jonas.service.ServiceException;

/**
 * CMI Service interface. It provides a way to use CMI with JOnAS.
 *
 * @author Loris Bouzonnet
 */
public class CmiServiceImpl extends AbsServiceImpl implements CmiService {

    /**
     * Logger for traces.
     */
    private static Logger cmilogger = Log.getLogger("org.ow2.jonas.cmi");

    /**
     * Registry service used to discover active protocols (auto GenIC).
     */
    private JmxService jmxService = null;

    /**
     * OSGi bundle context.
     */
    private final BundleContext context;

    /**
     * The manager of cluster view.
     */
    private ServerClusterViewManager clusterViewManager;

    /**
     * Registration of the cluster view manager.
     */
    private ServiceRegistration clusterViewManagerRegistration;

    /**
     * Manager for load balancing policy/strategy.
     */
    private CmiServicePolicyStrategyManager policyStrategyManager = null;

    /**
     * Constructor providing the bundle context.
     *
     * @param context
     *            the bundle context
     */
    public CmiServiceImpl(final BundleContext context) {
        this.context = context;
    }

    /**
     * Start the service.
     *
     * @throws ServiceException
     *             when cannot configure or start the CMI instance.
     */
    @Override
    protected void doStart() throws ServiceException {

        Properties cmiProperties = null;
        cmiProperties = CmiServiceHelper.buildCmiProperties();
        File deployDirectory = new File(JProp.getJonasBase(),
                CmiServiceProperty.DEFAULT_DEPLOY_DIRECTORY.getPropertyName());
        try {
            if (deployDirectory.exists()) {
                cmilogger.log(BasicLevel.DEBUG,
                        "Deploy directory of CMI service is: "
                                + deployDirectory.getAbsolutePath());
                cmiProperties.put(CmiServiceProperty.DEPLOY_DIRECTORY_KEY
                        .getPropertyName(), deployDirectory.toURL().toString());
            } else {
                cmilogger.log(BasicLevel.DEBUG,
                        "No deploy directory for CMI service");
            }
            ConfigurationRepository.getServerConfiguration().enableCMI(
                    cmiProperties);
        } catch (Exception e) {
            cmilogger.log(BasicLevel.ERROR,
                    "Cannot configure Carol to use CMI", e);
            throw new ServiceException("Cannot configure Carol to use CMI", e);
        }
        // Start the CMI server in an execution block
        IExecution<Void> startExec = new IExecution<Void>() {
            public Void execute() throws Exception {
                clusterViewManager = AbsServerClusterViewManager.start();
                return null;
            }
        };

        // Execute
        ExecutionResult<Void> startExecResult = RunnableHelper.execute(
                getClass().getClassLoader(), startExec);

        if (startExecResult.hasException()) {
            cmilogger.log(BasicLevel.ERROR,
                    "Cannot start the server-side manager", startExecResult
                            .getException());
            throw new ServiceException("Cannot start the server-side manager",
                    startExecResult.getException());
        }

        // Register the cluster view manager as a service
        clusterViewManagerRegistration = context.registerService(
                ServerClusterViewManager.class.getName(), clusterViewManager,
                null);
        // TODO Auto-generated method stub
        policyStrategyManager = new CmiServicePolicyStrategyManager(
                clusterViewManager);
        /**
         * The LoadBalancingListener will delegate processing of policy/strategy
         * services to policyStrategyManager
         */
        LoadBalancingListener listener = new LoadBalancingListener(
                policyStrategyManager);
        /**
         * Add listener for load balancing service
         */
        cmilogger.log(BasicLevel.DEBUG,
                "Adding listener for load balancing service ");
        try {
            context.addServiceListener(listener, "(objectClass="
                    + ILoadBalancingService.class.getName() + ")");
        } catch (InvalidSyntaxException e) {
            // TODO Auto-generated catch block
            cmilogger.log(BasicLevel.ERROR,
                    "Unable to add service listener for "
                            + ILoadBalancingService.class.getName()
                            + " because: " + e);
        }
    }

    /**
     * Stop the service.
     *
     * @throws ServiceException
     *             when the CMI server side manager cannot be stopped.
     */
    @Override
    protected void doStop() throws ServiceException {

        // Unregister the cluster view manager of the OSGi bundle context
        if (clusterViewManagerRegistration != null) {
            clusterViewManagerRegistration.unregister();
        }

        // Disable binding into the cluster
        try {
            ConfigurationRepository.getServerConfiguration().disableCMI();
        } catch (Exception e) {
            cmilogger.log(BasicLevel.ERROR, "Cannot disable cmi in Carol", e);
        }

        // Stop it
        if (clusterViewManager != null) {
            try {
                ((AbsServerClusterViewManager) clusterViewManager).stop();
            } catch (Exception e) {
                cmilogger.log(BasicLevel.ERROR,
                        "Cannot stop the server-side manager", e);
                throw new ServiceException(
                        "Cannot stop the server-side manager", e);
            }
        }
    }

    /**
     * Add a bean to the cluster.
     *
     * @param jndiName
     *            name of the bean
     * @param clusterPolicyName
     *            filename
     * @param homeClass
     *            class of the home interface
     * @param remoteClass
     *            class of the remote interface
     * @param classLoader
     *            the classloader used by the container of this bean
     * @param stateful
     *            true if the bean has a state
     * @param clusterReplicated
     *            true if the bean is replicated (ha service is required)
     * @throws Exception
     *             if the provided policy of load-balancing is not valid
     */
    public void addClusteredObject(final String jndiName,
            final String clusterPolicyName, final Class<?> homeClass,
            final Class<? extends EJBObject> remoteClass,
            final ClassLoader classLoader, final boolean stateful,
            final boolean clusterReplicated) throws Exception {

        // An object is clustered if and only if a policy of load-balancing is
        // associated to this
        if (clusterPolicyName == null) {
            cmilogger.log(BasicLevel.DEBUG, "The object with name " + jndiName
                    + " is not clustered.");
            return;
        }
        URL url = classLoader.getResource(clusterPolicyName);
        if (url != null) {
            try {
                ClusteredObjectInfo clusteredObjectInfo = null;
                clusteredObjectInfo = CMIInfoExtractor
                        .extractClusteringInfoFromDD(jndiName, homeClass,
                                remoteClass, url, stateful, clusterReplicated,
                                null);
                CMIInfoRepository.addClusteredObjectInfo(jndiName,
                        clusteredObjectInfo);

                cmilogger.log(BasicLevel.INFO, "The object with name "
                        + jndiName + " is clustered.");
            } catch (CMIInfoExtractorException e) {
                cmilogger.log(BasicLevel.ERROR,
                        "Error when extracting infos about clustering for the object with name "
                                + jndiName, e);
                throw new Exception(
                        "Error when extracting infos about clustering for the object with name "
                                + jndiName, e);
            }
        } else {
            cmilogger.log(BasicLevel.ERROR,
                    "Cannot find the policy of load-balancing with name "
                            + clusterPolicyName);
            throw new Exception(
                    "Cannot find the policy of load-balancing with name "
                            + clusterPolicyName);
        }

    }

    /**
     * @return the jmxService
     */
    protected JmxService getJmxService() {
        return jmxService;
    }

    /**
     * @param jmxService
     *            the jmxService to set
     */
    public void setJmxService(final JmxService jmxService) {
        this.jmxService = jmxService;
    }

}