/**
 * 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.jk;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

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 clusters used by Apache mod-JK
 * These Clusters are built dynamically, when a new server is discovered
 * as being part of a cluster of this type.
 * @author durieuxp
 */
public class JkClusterFactory extends ClusterFactory {

    /**
     * There is only 1 JKCluster, defined in "worker.properties", and
     * its members are the workers with type="ajp13".
     */
    private JkCluster cluster = null;

    /**
     * True when worker.properties has been looked for.
     * We decided to look at this only when the first Worker has been started.
     */
    private boolean configSearched = false;

    /**
     * Constructor
     */
    public JkClusterFactory(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) {
        if (cluster != null && cluster.getName().equals(name)) {
            return cluster;
        } else {
            return null;
        }
    }

    /**
     * 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 mod_jk worker.
     */
    @Override
    public boolean notifyServer(final ServerProxy proxy) {

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

        // Check if there is an Engine with a jvmRoute defined.
        // This jvmRoute should match a Worker defined in workers.properties
        String workerName = null;
        ObjectName engineOn;
        try {
            // CatalinaObjectName
            engineOn = ObjectName.getInstance(domainName + ":type=Engine");
        } catch (MalformedObjectNameException e1) {
            logger.log(BasicLevel.WARN, e1);
            return false;
        }
        if (proxy.isRegistered(engineOn)) {
            logger.log(BasicLevel.DEBUG, "Found 1 engine. Look for jvmRoute");
            workerName = (String) proxy.getAttribute(engineOn, "jvmRoute");
        }
        if (workerName == null) {
            logger.log(BasicLevel.DEBUG, serverName + " is not a JK-Cluster server");
            return false;
        }
        if (!configSearched) {
            // First worker found: try to find the "workers.properties" file.
            try {
                // Build a unique JkCluster, based on workers.properties info.
                cluster = new JkCluster(this);
            } catch (Exception e) {
                // No config file, or error in file.
                // => We don't build the Cluster.
                logger.log(BasicLevel.DEBUG, "Unable to build jk cluster for " + workerName
                        + " because " + e);
            }
        }
        if (cluster == null) {
            logger.log(BasicLevel.WARN, "Bad configuration: No workers.properties for " + workerName);
            return false;
        } else {
            configSearched = true;
        }

        // Check if the managed server has registered a AJP1.3 connector
        int workerPort = 0;
        ObjectName connectorOns;
        try {
            // CatalinaObjectName
            connectorOns = ObjectName.getInstance(domainName + ":type=Connector,*");
        } catch (MalformedObjectNameException e) {
            logger.log(BasicLevel.WARN, e);
            return false;
        }
        Iterator it = proxy.queryNames(connectorOns).iterator();
        while (it.hasNext()) {
            ObjectName connectorOn = (ObjectName) it.next();
            String protocol = (String) proxy.getAttribute(connectorOn, "protocol");
            if ("AJP/1.3".equals(protocol)) {
                workerPort = ((Integer) proxy.getAttribute(connectorOn, "port")).intValue();
                logger.log(BasicLevel.DEBUG, "Detected server has AJP Connector MBean with port=" + workerPort);
            }
        }
        if (workerPort == 0) {
            logger.log(BasicLevel.WARN, "No AJP/1.3 protocol: forget it");
            return false;
        }

        // add Worker To Cluster
        return cluster.addWorker(workerName, workerPort, proxy);
    }

    @Override
    public Collection getClusterList() {
        ArrayList ret = new ArrayList();
        if (cluster != null) {
            ret.add(cluster);
        }
        return ret;
    }

    /**
     * Update dynamic info for all the jk clusters.
     */
    @Override
    public void getMonitoringInfo() {
        // Not implemented.
    }
}
