/**
 * 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.lib.jmbeans.osgi;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;

import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;

import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;
import org.osgi.framework.BundleContext;
import org.ow2.jonas.Version;
import org.ow2.jonas.configuration.ConfigurationManager;
import org.ow2.jonas.jmx.JmxService;
import org.ow2.jonas.lib.jmbeans.J2EEServer;
import org.ow2.jonas.lib.jmbeans.JavaVm;
import org.ow2.jonas.lib.jmbeans.LogManager;
import org.ow2.jonas.lib.jmbeans.OsgiServiceListener;
import org.ow2.jonas.lib.jmbeans.OsgiServicesUtil;
import org.ow2.jonas.lib.jmbeans.ReconfigManager;
import org.ow2.jonas.lib.jmbeans.ServiceItem;
import org.ow2.jonas.lib.jmbeans.ServiceManager;
import org.ow2.jonas.lib.jmbeans.ServiceManagerUpdater;
import org.ow2.jonas.lib.jmbeans.ServiceState;
import org.ow2.jonas.lib.management.domain.J2EEDomain;
import org.ow2.jonas.lib.management.javaee.J2eeObjectName;
import org.ow2.jonas.lib.timer.TimerManager;
import org.ow2.jonas.lib.util.Log;
import org.ow2.jonas.properties.ServerProperties;
import org.ow2.jonas.registry.RegistryService;
import org.ow2.jonas.versioning.VersioningService;
import org.ow2.util.archive.api.IArchiveManager;
import org.ow2.util.ee.deploy.api.deployer.IDeployerManager;

/**
 * Activator for the bundle which creates the basic JSR77 MBeans.
 * Provides an OSGi service to update service manager.
 * @author Adriana Danes
 */
public class MBeansRegistration implements ServiceManagerUpdater {
    /**
     * Property for the log file configuration.
     */
    private static final String LOG_CONFIGFILE = "jonas.log.configfile";

    /**
     * Default property value for the log file.
     */
    private static final String DEF_LOG_CONFIGFILE = "trace";

    /**
     * File for log configuration.
     */
    private String logConfigFile = null;

    /**
     * Current logger.
     */
    private static Logger logger = Log.getLogger("org.ow2.jonas.lib.jmbeans");

    /**
     * Jmx service instance.
     */
    private JmxService jmxService = null;

    /**
     * Registery service instance.
     */
    private RegistryService registryService = null;

    /**
     * JOnAS configuration manager.
     */
    private ConfigurationManager configManager = null;

    /**
     * Basic naming properties:
     *
     * The management domain name.
     */
    private String domainName = null;

    /**
     * The server instance name.
     */
    private String serverName = null;

    /**
     * OSGi BundleContext.
     */
    private BundleContext bc = null;

    /**
     * J2EEServer object.
     */
    private J2EEServer server = null;

    /**
     * ServerProperties service instance.
     */
    private ServerProperties serverProperties = null;

    /**
     * DeployerManager instance.
     */
    private IDeployerManager deployerManager = null;

    /**
     * ArchiveManager instance.
     */
    private IArchiveManager archiveManager = null;

    /**
     * TimerManager instance.
     */
    private TimerManager tm = null;

    /**
     * Service manager.
     */
    private ServiceManager serviceManager = null;

    /**
     * Constructor.
     * @param bc OSGi {@link BundleContext}.
     */
    public MBeansRegistration(final BundleContext bc) {
        this.bc = bc;
    }

    /**
     * Start bundle.
     * @throws Exception could not start bundle.
     */
    public void start() throws Exception {
        logger.log(BasicLevel.DEBUG, "Starting jonas-mbeans Bundle...");

        // Get domainName and serverName from the Bootstrap service
        // Got all we need to create and register JSR77 MBeans
        try {
            domainName = serverProperties.getDomainName();
            serverName = serverProperties.getServerName();

            // TODO get a reference on a TimerManager service.
            tm = TimerManager.getInstance();

            // set configuration info into the Log System (monolog)
            String logConfigFileName = serverProperties.getValue(LOG_CONFIGFILE, DEF_LOG_CONFIGFILE);
            Log.configure(logConfigFileName);
            Properties logProperties = Log.getProperties();
            logConfigFile = Log.getConfigFileName();

            registerMBeans(serverProperties, logProperties);
            // unset props as we don't need properties any more
            serverProperties = null;
        } catch (MalformedObjectNameException me) {
            logger.log(BasicLevel.ERROR, "Failed to register mbeans: " + me.getMessage());
            return;
        } catch (Exception e) {
            System.out.println("Failed to register mbeans");
            e.printStackTrace();
            return;
        }
    }

    /**
     * Stop the bundle.
     * @throws Exception could not stop the bundle.
     */
    public void stop() throws Exception {
        logger.log(BasicLevel.DEBUG, "Stopping jonas-mbeans Bundle...");
        if (jmxService != null) {
            unregisterMBeans(domainName, serverName);
        }
        registryService = null;
    }

    /**
     * Register JSR77 MBeans.
     * @param props JONAS server properties
     * @param logProps logging system properties
     * @throws Exception problem with some MBeans creation or registration
     */
    private void registerMBeans(final ServerProperties props, final Properties logProps) throws Exception {
        // Create and register a J2EEServer MBean
        // --------------------------------------
        String name = domainName + ":j2eeType=J2EEServer,name=" + serverName;
        ObjectName serverOn = ObjectName.getInstance(name);
        server = new J2EEServer(serverOn.toString(), true, false, true, bc);
        server.setServerVendor(Version.VENDOR);
        server.setServerVersion(Version.getNumber());
        server.setServerName(serverName);
        server.setDomainName(domainName);
        server.setAllProperties(props);
        server.setJonasBase(props.getValue("jonas.base"));
        server.setJonasRoot(props.getValue("jonas.root"));
        server.setProtocols(getProtocols());
        server.setArchiveManager(archiveManager);
        server.setDeployerManager(deployerManager);
        server.setConfigManager(configManager);

        // TODO get the list of dirs from the DeployableMonitor service MBean
        server.initRepositoryDirs();

        // TODO un-comment below when jonas.root accessible
        //String versions = props.getVersionsFile();
        String versions = "Versions";
        server.setVersions(versions);
        // activate memory monitoring
        server.setTimerManager(tm);
        server.setActivated(true);
        // Load the J2EEServer, J2EEDomain, JVM, WorkManager MBean descriptions
        Package p = server.getClass().getPackage();
        jmxService.loadDescriptors(p.getName(), getClass().getClassLoader());

        // Register J2EEServer MBean
        jmxService.registerModelMBean(server, serverOn);
        logger.log(BasicLevel.DEBUG, "MBeans registered: " + serverOn.toString());

        // Create and register J2EEDomain MBean
        // ------------------------------------
        ObjectName domainOn= J2eeObjectName.J2EEDomain(domainName);
        J2EEDomain domain = new J2EEDomain(domainOn.toString());
        domain.setJmxService(jmxService);
        boolean master = props.isMaster();
        domain.setMaster(master, serverName);
        jmxService.registerModelMBean(domain, domainOn);
        logger.log(BasicLevel.DEBUG, "MBeans registered: " + domainOn.toString());

        // Set relation J2EEDomain -> J2EEServer
        domain.setMyJ2EEServerOn(serverOn.toString());

        // Create and register JavaVM MBean
        // --------------------------------
        String jvmName = serverName; // maybe this could be another name
        ObjectName objectName= J2eeObjectName.JVM(domainName, serverName, jvmName);
        JavaVm jvm = new JavaVm(objectName.toString(), props);
        jmxService.registerModelMBean(jvm, objectName);
        logger.log(BasicLevel.DEBUG, "MBeans registered: " + objectName.toString());

        // Set relation J2EEServer -> JavaVM
        server.addJavaVM(objectName.toString());

        logger.log(BasicLevel.DEBUG, "MBeans registered: " + objectName);

        // Create and register the ReconfigManager
        String nameReconfigManager = domainName + ":type=management,name=reconfigManager";
        objectName = ObjectName.getInstance(nameReconfigManager);
        ReconfigManager reconfigManager = new ReconfigManager(objectName, props);
        reconfigManager.setJmxService(jmxService);
        reconfigManager.setLogConfigFileName(logConfigFile);
        reconfigManager.setServerConfigFileName(props.getPropFileName());
        reconfigManager.addMBeanServerDelegateListener();
        jmxService.registerModelMBean(reconfigManager, objectName);
        logger.log(BasicLevel.DEBUG, "MBeans registered: " + objectName.toString());

        // Create and register a MBean for the logging management
        String nameLogging = domainName + ":type=service,name=log";
        objectName = ObjectName.getInstance(nameLogging);
        LogManager logManager = new LogManager(objectName.toString());
        logManager.setProps(logProps);
        logManager.setConfigFile(logConfigFile);
        jmxService.registerModelMBean(logManager, objectName);
        logger.log(BasicLevel.DEBUG, "MBeans registered: " + objectName.toString());

        // Create ServiceManager and OsgiListener
        // --------------------------------------
        serviceManager = new ServiceManager(server);
        server.setServiceManager(serviceManager);
        updateServiceManager(props);

        // Create OsgiServiceListener
        OsgiServiceListener listener = new OsgiServiceListener(serviceManager);
        bc.addServiceListener(listener);

        //testAttributeListener();
    }

    /**
     * Unregister JSR77 MBeans.
     * @param domainName domain name
     * @param serverName server name
     * @throws Exception MBean unregistration problem
     */
    private void unregisterMBeans(final String domainName, final String serverName) throws Exception {
        // Unregister J2EEServer MBean
        String name = domainName + ":j2eeType=J2EEServer,name=" + serverName;
        ObjectName objectName = ObjectName.getInstance(name);
        jmxService.unregisterModelMBean(objectName);
        logger.log(BasicLevel.DEBUG, "MBeans unregistered: " + objectName.toString());

        // Unregister JVM MBean
        String jvmName = serverName; // maybe this could be another name
        objectName = J2eeObjectName.JVM(domainName, serverName, jvmName);
        jmxService.unregisterModelMBean(objectName);
        logger.log(BasicLevel.DEBUG, "MBeans unregistered: " + objectName.toString());

        // Unregister J2EEDomain MBean
        objectName = J2eeObjectName.J2EEDomain(domainName);
        jmxService.unregisterModelMBean(objectName);
        logger.log(BasicLevel.DEBUG, "MBeans unregistered: " + objectName.toString());

        // Unregister the MBean for logging management
        String nameLogging = domainName + ":type=service,name=log";
        objectName = ObjectName.getInstance(nameLogging);
        jmxService.unregisterModelMBean(objectName);
        logger.log(BasicLevel.DEBUG, "MBeans unregistered: " + objectName.toString());

        // Unregister the ReconfigManager MBean
        String nameReconfigManager = domainName + ":type=management,name=reconfigManager";
        objectName = ObjectName.getInstance(nameReconfigManager);
        jmxService.unregisterModelMBean(objectName);
        logger.log(BasicLevel.DEBUG, "MBeans unregistered: " + objectName.toString());

        //testAttributeListenerUndo();
    }

    /**
     * @return Get the protocols list from the registry service
     */
    private String getProtocols() {
        String protocols = "";
        String prefix = "rmi/";
        List<String> protocolList = registryService.getActiveProtocolNames();
        Iterator<String> it = protocolList.iterator();
        while (it.hasNext()) {
            String protocolName = it.next();
            if (protocols == "") {
                protocols = prefix + protocolName;
            } else {
                protocols += "," +  prefix + protocolName;
            }
        }
        return protocols;
    }

    /**
     * Update ServiceManager with services state.
     * @param props JONAS server properties.
     * @throws Exception when we cannot get the registered JOnAS services.
     */
    public void updateServiceManager(final ServerProperties props) throws Exception {
        // Get initially defined services list in JOnAS configuration file
        String[] services = props.getValueAsArray("jonas.services");


        serviceManager.deleteAllServices();
        for (String service : services) {
            // Add all services in STOPPED state
            ServiceItem servItem = new ServiceItem();
            servItem.setName(service);
            servItem.setDescription(service + " description ...");
            servItem.setState(ServiceState.STOPPED);
            servItem.setMandatory(Boolean.FALSE);
            serviceManager.addService(servItem);
        }

        // Get mandatory services list
        ArrayList<String> mandatoryServices = OsgiServicesUtil.getJonasMandatoryServices(bc);
        for (String service : mandatoryServices) {
            serviceManager.setMandatoryService(service);
        }

        // Get already running services list
        ArrayList<String> runningServices = OsgiServicesUtil.getJonasServices(bc);
        for (String service : runningServices) {
            serviceManager.setServiceState(service, ServiceState.RUNNING);
        }
    }

    /**
     * @param versioningService  The versioning service to set.
     */
    public void setVersioningService(final VersioningService versioningService) {
        server.setVersioningService(versioningService);
    }

    /**
     * Sets the versioning service to null.
     */
    public void unsetVersioningService() {
        server.unsetVersioningService();
    }

    /**
     * @return The versioning service.
     */
    public VersioningService getVersioningService() {
        return server.getVersioningService();
    }

    /**
     * @param configManager the configManager to set
     */
    public void setConfigManager(final ConfigurationManager configManager) {
        this.configManager = configManager;
    }

    /**
     * @param deployerManager the deployerManager to set
     */
    public void setDeployerManager(final IDeployerManager deployerManager) {
        this.deployerManager = deployerManager;
    }

    /**
     * @param archiveManager the archiveManager to set
     */
    public void setArchiveManager(final IArchiveManager archiveManager) {
        this.archiveManager = archiveManager;
    }

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

    /**
     * @param registryService the registryService to set
     */
    public void setRegistryService(final RegistryService registryService) {
        this.registryService = registryService;
    }

    /**
     * @param serverProperties the serverProperties to set
     */
    public void setServerProperties(final ServerProperties serverProperties) {
        this.serverProperties = serverProperties;
    }
}
