/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2006 Bull S.A.
 * 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.lib.management.domain.cluster.cmi;

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import java.util.StringTokenizer;

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

import org.objectweb.util.monolog.api.BasicLevel;
import org.ow2.jonas.lib.management.domain.DomainMonitor;
import org.ow2.jonas.lib.management.domain.cluster.BaseCluster;
import org.ow2.jonas.lib.management.domain.cluster.ClusterFactory;
import org.ow2.jonas.lib.management.domain.proxy.server.ServerProxy;

/**
 * Factory for CMI clusters
 * These Clusters are built dynamically, when a new server is discovered
 * as being part of a cluster of this type.
 * @author durieuxp
 */
public class CmiClusterFactory extends ClusterFactory {

    /**
     * List of TomcatCluster objects
     * There may be more than 1 TomcatCluster in the domain.
     * key = clusterName, value = TomcatCluster
     */
    private HashMap<String, CmiCluster> myclusters = new HashMap<String, CmiCluster>();

    /**
     * Constructor
     */
    public CmiClusterFactory(final DomainMonitor dm) {
        super(dm);
    }

    /**
     * Look for a cluster by its name
     * @param name fo the cluster
     * @return cluster or null if not found
     */
    @Override
    public BaseCluster getCluster(final String name) {
        return myclusters.get(name);
    }

    /**
     * A new server has been discovered.
     * In case this server is recognized, it is added in a Cluster.
     * If not, nothing is done.
     * @param proxy The new ServerProxy
     * @return True if recognized as a tomcat server.
     */
    @Override
    public boolean notifyServer(final ServerProxy proxy) {

        String serverName = proxy.getServerName();
        logger.log(BasicLevel.DEBUG, serverName);

        // Determine if a CMI Cluster MBean exists in the server
        // referenced by this proxy.
        ObjectName ons;
        try {
            ons = ObjectName.getInstance(domainName + ":name=CMIServer,*");
        } catch (MalformedObjectNameException e1) {
            logger.log(BasicLevel.ERROR, "MalformedObjectNameException");
            return false;
        }
        Set<?> set = proxy.queryNames(ons);
        if (set == null) {
            logger.log(BasicLevel.DEBUG, "Cannot reach " + serverName);
            return false;
        }
        if (set.isEmpty()) {
            logger.log(BasicLevel.DEBUG, "No CMI Cluster with name=CMIServer declared");
            return false;
        }
        // Cluster MBean
        // ObjectName = "<domain>:type=protocol,cluster=clusterName,name=CMIServer,protocol=UDP
        ObjectName on = null;
        String protocol = null;
        for (Iterator<?> it = set.iterator(); it.hasNext();) {
            on = (ObjectName) it.next();
            protocol = on.getKeyProperty("protocol");
            if ("UDP".equals(protocol) || "TCP".equals(protocol) || "JMS".equals(protocol)) {
                break;
            }
        }

        String clusterName;
        if(protocol == null) {
            // Unknown implementation of CMI, use the default values
            logger.log(BasicLevel.WARN, "Unknown implementation of CMI: using the default values");
            on = null;
            clusterName = "unknown";
            protocol = "unknown";
        } else {
            // Found an MBean: Get information about this cluster
            logger.log(BasicLevel.DEBUG, "Found CMICluster protocol=" + protocol);
            clusterName = on.getKeyProperty("cluster");
        }


        // Retrieve the CmiCluster. Create it if necessary.
        // The channel should be a unique ident for it.
        CmiCluster cluster = myclusters.get(clusterName);
        if (cluster == null) {
            ObjectName clon = null;
            try {
                cluster = new CmiCluster(this);
                clon = cluster.setName(clusterName);
            } catch (JMException e) {
                logger.log(BasicLevel.ERROR, "Cannot create CMI Cluster:" + e);
                return false;
            }

            cluster.setProtocol(protocol);

            // Get additional info provided by the Cluster MBean
            if(on != null) {
                String strprop = (String) proxy.getAttribute(on, "PropertiesAsString");
                StringTokenizer stk = new StringTokenizer(strprop, "{},");
                int nb = stk.countTokens();
                for (int i = 0; i < nb; i++) {
                    String str = stk.nextToken();
                    int ind = str.indexOf('=', 0);
                    if (ind <= 0) {
                        logger.log(BasicLevel.ERROR, "Bad property: " + str);
                    }
                    String key = str.substring(0, ind).trim();
                    String value = str.substring(ind + 1).trim();
                    logger.log(BasicLevel.DEBUG, key + "=" + value);
                    if (key.equals("mcast_port")) {
                        int mcastPort = (new Integer(value)).intValue();
                        cluster.setMcastPort(mcastPort);
                    }
                    if (key.equals("mcast_addr")) {
                        cluster.setMcastAddr(value);
                    }
                    // TODO Add other parameters
                }
            }

            // Get  info provided by the CMI MBean
            // ObjectName = "<domain>:type=cmi,name=CMIServer,J2EEServer=<server>
            ObjectName cmiOn = null;
            for (Iterator<?> it = set.iterator(); it.hasNext();) {
                on = (ObjectName) it.next();
                String type = on.getKeyProperty("type");
                if ("cmi".equals(type)) {
                    cmiOn = on;
                    break;
                }
            }
            cluster.delayToRefresh = ((Integer) proxy.getAttribute(cmiOn, "DelayToRefresh")).intValue();

            // Register the MBean if not done
            if (!mbeanServer.isRegistered(clon)) {
                try {
                    // A MBean is registered for each Cluster
                    mbeanServer.registerMBean(cluster, clon);
                } catch (Exception e) {
                    logger.log(BasicLevel.ERROR, "Cannot register cluster:" + e);
                    return false;
                }
            }
            myclusters.put(clusterName, cluster);
        }

        // add a server to the cluster
        return cluster.addCmiServer(serverName, proxy);
    }

    @Override
    /**
     * Return the CmiClusters in a domain.
     */
    public Collection<CmiCluster> getClusterList() {
        return myclusters.values();
    }
    /**
     * Update dynamic info for all the cmi clusters.
     */
    @Override
    public void getMonitoringInfo() {
        for (Iterator<CmiCluster> it = myclusters.values().iterator(); it.hasNext();) {
            CmiCluster cmiCluster = it.next();
            cmiCluster.getMonitoringInfo();
        }
    }
}
