/*
 * The contents of this file are subject to the terms 
 * of the Common Development and Distribution License 
 * (the License).  You may not use this file except in
 * compliance with the License.
 * 
 * You can obtain a copy of the license at 
 * https://glassfish.dev.java.net/public/CDDLv1.0.html or
 * glassfish/bootstrap/legal/CDDLv1.0.txt.
 * See the License for the specific language governing 
 * permissions and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL 
 * Header Notice in each file and include the License file 
 * at glassfish/bootstrap/legal/CDDLv1.0.txt.  
 * If applicable, add the following below the CDDL Header, 
 * with the fields enclosed by brackets [] replaced by
 * you own identifying information: 
 * "Portions Copyrighted [year] [name of copyright owner]"
 * 
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 */
package com.sun.enterprise.management.support;

import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Collection;
import java.io.IOException;

import javax.management.MBeanServer;
import javax.management.ObjectName;
import javax.management.JMException;
import javax.management.InstanceNotFoundException;

import com.sun.appserv.management.base.AMX;
import static com.sun.appserv.management.base.XTypes.LOAD_BALANCER_MONITOR;
import static com.sun.appserv.management.base.XTypes.SERVER_REF_CONFIG;
import static com.sun.appserv.management.base.XTypes.CLUSTER_REF_CONFIG;
import static com.sun.appserv.management.base.XTypes.LOAD_BALANCER_CLUSTER_MONITOR;
import static com.sun.appserv.management.base.XTypes.LOAD_BALANCER_SERVER_MONITOR;
import static com.sun.appserv.management.base.XTypes.LOAD_BALANCER_APPLICATION_MONITOR;
import static com.sun.appserv.management.base.XTypes.LOAD_BALANCER_CONTEXT_ROOT_MONITOR;
import static com.sun.appserv.management.base.AMX.J2EE_TYPE_KEY;
import static com.sun.appserv.management.base.AMX.NAME_KEY;
import static com.sun.appserv.management.base.AMX.JMX_DOMAIN;
import com.sun.appserv.management.base.Container;
import com.sun.appserv.management.base.Util;

import com.sun.appserv.management.client.ProxyFactory;
import com.sun.appserv.management.base.AMXDebug;
import com.sun.appserv.management.util.misc.StringUtil;

import com.sun.enterprise.management.monitor.LoadBalancerClusterMonitorImpl;
import com.sun.enterprise.management.monitor.LoadBalancerMonitorImpl;
import com.sun.enterprise.management.monitor.LoadBalancerServerMonitorImpl;
import com.sun.enterprise.management.monitor.LoadBalancerApplicationMonitorImpl;
import com.sun.enterprise.management.monitor.LoadBalancerContextRootMonitorImpl;
import com.sun.enterprise.management.support.ObjectNames;
import com.sun.appserv.management.util.jmx.MBeanRegistrationListener;

import com.sun.appserv.management.monitor.LoadBalancerMonitor;  
import com.sun.appserv.management.monitor.LoadBalancerClusterMonitor;
import com.sun.appserv.management.monitor.LoadBalancerServerMonitor;
import com.sun.appserv.management.monitor.LoadBalancerApplicationMonitor;
import com.sun.appserv.management.monitor.LoadBalancerContextRootMonitor;

import com.sun.appserv.management.config.DeployedItemRefConfig;
import com.sun.appserv.management.config.ClusterRefConfig;
import com.sun.appserv.management.config.ClusterConfig;
import com.sun.appserv.management.config.ServerRefConfig;
import com.sun.appserv.management.config.ServerConfig;
import com.sun.appserv.management.config.StandaloneServerConfig;
import com.sun.appserv.management.config.ClusteredServerConfig;      
import com.sun.appserv.management.config.LoadBalancerConfig;
import com.sun.appserv.management.config.LBConfig;
import com.sun.appserv.management.config.DomainConfig;

import com.sun.appserv.management.base.AMXRootLogger;

import com.sun.appserv.management.config.J2EEApplicationConfig;
import com.sun.enterprise.config.ConfigException;
import com.sun.enterprise.config.serverbeans.ApplicationHelper;
import com.sun.enterprise.deployment.backend.IASDeploymentException;
import com.sun.enterprise.deployment.phasing.ApplicationConfigHelper;
import com.sun.enterprise.deployment.phasing.DeploymentServiceUtils;

/**
 * Convenience base class for performing registrations
 * for various load balancer related monitors.
*/
public abstract class LBBaseMBeanRegistrationListener 
    extends MBeanRegistrationListener {
    
    final ObjectNames objectNames = ObjectNames.getInstance(JMX_DOMAIN); 
    
    public LBBaseMBeanRegistrationListener(
        final MBeanServer mBeanServer, final ObjectName constraint )	
        throws InstanceNotFoundException, IOException {
        
	super(mBeanServer, constraint);
    }
	
    public LBBaseMBeanRegistrationListener(final MBeanServer mBeanServer)
        throws InstanceNotFoundException, IOException {
        
        this(mBeanServer, (ObjectName)null );
    }
    
    public void mbeanUnregistered(final ObjectName objectName) {}    
           
    protected LoadBalancerClusterMonitor registerLoadBalancerClusterMonitor(
        LoadBalancerMonitor loadBalancerMonitor, String clusterName) 
        throws JMException {
        
        ObjectName loadBalancerMonitorObjName =
            Util.getObjectName(loadBalancerMonitor);

        ObjectName childClusterRefObjectName = 
            objectNames.buildContaineeObjectName( 
                loadBalancerMonitorObjName,
                loadBalancerMonitor.getFullType(), 
                LOAD_BALANCER_CLUSTER_MONITOR, clusterName);

        LoadBalancerClusterMonitorImpl loadBalancerClusterMonitorImpl = 
            new LoadBalancerClusterMonitorImpl();
        try {
            ((MBeanServer)getConn()).registerMBean(
                loadBalancerClusterMonitorImpl, childClusterRefObjectName);
        } catch( JMException ex ) {
            logWarning(
                "LBBaseMBeanRegistrationListener" +
                " registerLoadBalancerClusterMonitor failed. Exception = ", ex);	
        }
        
        return getProxy(childClusterRefObjectName, LoadBalancerClusterMonitor.class );
    }    
    
    protected LoadBalancerServerMonitor registerLoadBalancerServerMonitor(
        LoadBalancerClusterMonitor loadBalancerClusterMonitor, String serverName) 
        throws JMException {

        ObjectName newChildClusterRefObjectName = 
            Util.getObjectName(loadBalancerClusterMonitor);

        ObjectName childServerRefObjectName = 
            objectNames.buildContaineeObjectName(
                newChildClusterRefObjectName, 
                loadBalancerClusterMonitor.getFullType(), 
                LOAD_BALANCER_SERVER_MONITOR, serverName);

        LoadBalancerServerMonitorImpl loadBalancerServerMonitorImpl 
                = new LoadBalancerServerMonitorImpl();
        try {
            ((MBeanServer)getConn()).registerMBean(
                loadBalancerServerMonitorImpl, childServerRefObjectName);
        } catch( JMException ex ) {
            logWarning(
                "LoadBalancerServerRefRegistrationListener" +
                " registerLoadBalancerServerMonitor failed. Exception = ", ex);	
        }

        return getProxy(childServerRefObjectName, LoadBalancerServerMonitor.class);
    }
    
    protected LoadBalancerApplicationMonitor registerLoadBalancerApplicationMonitor(
        LoadBalancerServerMonitor loadBalancerServerMonitor, String appName) 
        throws JMException{

        ObjectName newChildServerRefObjectName = 
            Util.getObjectName(loadBalancerServerMonitor);

        ObjectName childApplicationRefObjectName = 
            objectNames.buildContaineeObjectName( 
                newChildServerRefObjectName,
                loadBalancerServerMonitor.getFullType(), 
                LOAD_BALANCER_APPLICATION_MONITOR, appName);

        LoadBalancerApplicationMonitorImpl 
            loadBalancerApplicationMonitorImpl = 
                new LoadBalancerApplicationMonitorImpl();
        try {
            ((MBeanServer)getConn()).registerMBean( 
                loadBalancerApplicationMonitorImpl, 
                childApplicationRefObjectName );
        } catch( JMException ex ) {
            logWarning(
                "LoadBalancerServerRefRegistrationListener" +
                " registerLoadBalancerApplicationMonitor failed. " +
                "Exception = ", ex);	
        }

        return getProxy(childApplicationRefObjectName, LoadBalancerApplicationMonitor.class);
    }
 
    protected void registerLoadBalancerContextRootMonitor(
        LoadBalancerApplicationMonitor loadBalancerApplicationMonitor, 
        String ctxRootName) throws JMException {
     
        ObjectName newChildAppRefObjectName = 
            Util.getObjectName(loadBalancerApplicationMonitor);

        ObjectName childContextRootObjectName = 
            objectNames.buildContaineeObjectName( 
                newChildAppRefObjectName,
                loadBalancerApplicationMonitor.getFullType(), 
                LOAD_BALANCER_CONTEXT_ROOT_MONITOR, 
                ctxRootName);
        LoadBalancerContextRootMonitorImpl 
            loadBalancerContextRootMonitorImpl = 
                new LoadBalancerContextRootMonitorImpl();

        ((MBeanServer)getConn()).registerMBean( 
            loadBalancerContextRootMonitorImpl, 
            childContextRootObjectName);
    }    
    
    protected List<LoadBalancerMonitor> fetchLBMonitoringRoots(
        LBConfig lbConfig) throws JMException {
        
        List<LoadBalancerMonitor> monitoringRoots = new ArrayList<LoadBalancerMonitor>();
        String name = lbConfig.getName();
        Map<String, LoadBalancerConfig> loadBalancerConfigMap =
                getDomainConfig().getLoadBalancerConfigMap();
        Map<String, LoadBalancerConfig> relatedLoadBalancerConfigMap =
                new HashMap<String, LoadBalancerConfig>();
        
        for (String loadBalancerName : loadBalancerConfigMap.keySet()) {
            LoadBalancerConfig loadBalancerConfig =
                loadBalancerConfigMap.get(loadBalancerName);
            if (loadBalancerConfig.getLbConfigName().equals(name)) {
                relatedLoadBalancerConfigMap.put(
                    loadBalancerName, loadBalancerConfig);
                monitoringRoots.add(fetchMonitoringPeer(loadBalancerConfig));
            }
        }
        
        return monitoringRoots;
    }
    
        protected ProxyFactory
    getProxyFactory()
    {
        return ProxyFactory.getInstance( getConn() );
    }
    
        protected <T extends AMX> T
    getProxy( final ObjectName objectName, final Class<T> theClass)
    {
        return getProxyFactory().getProxy( objectName, theClass );
    }
    
    protected LoadBalancerMonitor fetchMonitoringPeer(
        LoadBalancerConfig loadBalancerConfig) throws JMException {
            String name = loadBalancerConfig.getName();
            //Step1: Create the root empty LoadBalancerMonitor
            ObjectName loadBalancerMonitorObjName = new ObjectName(
                JMX_DOMAIN + ":" + J2EE_TYPE_KEY + "=" + LOAD_BALANCER_MONITOR + 
                "," + NAME_KEY + "=" + name);
            LoadBalancerMonitor loadBalancerMonitor = 
                getProxy(loadBalancerMonitorObjName, LoadBalancerMonitor.class);

            return loadBalancerMonitor;
    }

    protected List<LoadBalancerClusterMonitor> fetchLoadBalancerClusterMonitors(
        List<LoadBalancerMonitor> monitoringRoots, String clusterName) {
        
        List<LoadBalancerClusterMonitor> result = new ArrayList<LoadBalancerClusterMonitor>();

        for (LoadBalancerMonitor monitoringRoot : monitoringRoots) {
            LoadBalancerClusterMonitor loadBalancerClusterMonitor =
                monitoringRoot.getLoadBalancerClusterMonitorMap().get(clusterName);
            if (loadBalancerClusterMonitor != null) 
                result.add(loadBalancerClusterMonitor);
        }
        
        return result;
    }    

    protected Collection<LBConfig> fetchLBConfigs(
        String targetName, boolean isCluster) {

        Map<String,LBConfig> result = new HashMap<String,LBConfig>();
        Map<String,LBConfig> lbConfigMap = getDomainConfig().getLBConfigMap();
        
        if (isCluster) {
            for (String lbConfigName : lbConfigMap.keySet()) {
                LBConfig lbConfig = lbConfigMap.get(lbConfigName);
                Map<String,ClusterRefConfig> lbClusterRefConfigMap =
                    lbConfig.getClusterRefConfigMap();
                for (String clusterRef : lbClusterRefConfigMap.keySet()) {
                    if (clusterRef.equals(targetName)) {
                        result.put(lbConfigName, lbConfig);
                        break;
                    }
                }
            }
        } else {
         /*its a serverName which means you have to find LBConfigs containing 
         1. standalone server references with the same name 
         2. clustered server references with the same name */
            for (String lbConfigName : lbConfigMap.keySet()) {
                LBConfig lbConfig = lbConfigMap.get(lbConfigName);
                Map<String,ServerRefConfig> lbServerRefConfigMap =
                    lbConfig.getServerRefConfigMap();
                for (String serverRef : lbServerRefConfigMap.keySet()) {
                    if (serverRef.equals(targetName)) {
                        result.put(lbConfigName, lbConfig);
                        break;
                    }
                }
            }
            for (String lbConfigName : lbConfigMap.keySet()) {
                LBConfig lbConfig = lbConfigMap.get(lbConfigName);
                Map<String,ClusterRefConfig> lbClusterRefConfigMap =
                    lbConfig.getClusterRefConfigMap();
                Map<String,ClusterConfig> clusterConfigMap =
                    getDomainConfig().getClusterConfigMap();
                Map<String,ClusterConfig> relevantClusterConfigMap = new HashMap<String,ClusterConfig>();
                for (String clusterRef : lbClusterRefConfigMap.keySet()) 
                    relevantClusterConfigMap.put(clusterRef, 
                        clusterConfigMap.get(clusterRef));
                //so now we have the right set of <cluster> elements
                for (String clusterName : relevantClusterConfigMap.keySet()) {
                    ClusterConfig clusterConfig = 
                        relevantClusterConfigMap.get(clusterName);
                    Map<String,ServerRefConfig> clusteredServerRefConfigMap =
                        clusterConfig.getServerRefConfigMap();
                    for (String serverRef : clusteredServerRefConfigMap.keySet()) {
                        if (serverRef.equals(targetName)) {
                            result.put(lbConfigName, lbConfig);
                            break;
                        }
                    }
                }
            }
        }
        return result.values();
    }    

    protected void registerLoadBalancerApplicationMonitorTree(
        LoadBalancerServerMonitor loadBalancerServerMonitor, String appName) 
        throws JMException {
        try{
            if(ApplicationHelper.isSystemApp(DeploymentServiceUtils.getConfigContext(),appName)) {
                return;
            }
            LoadBalancerApplicationMonitor loadBalancerApplicationMonitor =
                    registerLoadBalancerApplicationMonitor(
                    loadBalancerServerMonitor, appName);
            String [] ctxRootNames = ApplicationConfigHelper.getAppContextRoots(DeploymentServiceUtils.getConfigContext(),appName);
            for (String ctxRootName : ctxRootNames) {
                registerLoadBalancerContextRootMonitor(
                        loadBalancerApplicationMonitor, ctxRootName);
            }
        } catch(Exception e){
            e.printStackTrace();
        }
    }
    
    protected void registerLoadBalancerServerMonitorTree(
        LoadBalancerClusterMonitor loadBalancerClusterMonitor,
        Map<String, ServerConfig> serverConfigMap, String serverName) 
        throws JMException {

        LoadBalancerServerMonitor loadBalancerServerMonitor =
            registerLoadBalancerServerMonitor(
                loadBalancerClusterMonitor, serverName);
        
        ServerConfig serverConfig = 
            (ServerConfig) serverConfigMap.get(serverName);
        Map<String,DeployedItemRefConfig> deployedItemRefConfigMap =
            serverConfig.getDeployedItemRefConfigMap();

        for (String appName : deployedItemRefConfigMap.keySet()) {

            try{
                //if the type is user, then only set the lb-enabled to true
                if(ApplicationHelper.isSystemApp(DeploymentServiceUtils.getConfigContext(),appName)) {
                    continue;
                }
                LoadBalancerApplicationMonitor loadBalancerApplicationMonitor =
                        registerLoadBalancerApplicationMonitor(
                        loadBalancerServerMonitor, appName);
                String [] ctxRootNames = ApplicationConfigHelper.getAppContextRoots(DeploymentServiceUtils.getConfigContext(),appName);                
                for (String ctxRootName : ctxRootNames) {
                    if(ctxRootName.startsWith("/"))
                        ctxRootName = ctxRootName.substring(1);
                    registerLoadBalancerContextRootMonitor(
                            loadBalancerApplicationMonitor, ctxRootName);
                }
            }catch(ConfigException ex){
                logWarning(
                        "LoadBalancerServerRefRegistrationListener" +
                        " registerLoadBalancerServerMonitorTree failed. " +
                        "Exception = ", ex);
            }catch(IASDeploymentException ex){
                logWarning(
                        "LoadBalancerServerRefRegistrationListener" +
                        " registerLoadBalancerServerMonitorTree failed. " +
                        "Exception = ", ex);
            }
        }            
    }    

    protected void registerLoadBalancerClusterMonitorTree(
        LoadBalancerMonitor lbm , String clusterName) throws JMException {
        
        LoadBalancerClusterMonitor lbcm = 
            registerLoadBalancerClusterMonitor(lbm, clusterName);

        //1. get ALL <cluster> elements and find the correct <cluster>
        //element corresponding to the one that is passed in
        Map<String,ClusterConfig> clusterConfigMap = 
            getDomainConfig().getClusterConfigMap();
        ClusterConfig clusterConfig = 
            (ClusterConfig)clusterConfigMap.get(clusterName);
                        
        //2. Get corresponding <server-ref> elements for this <cluster> element
        //found in step 1
        Map <String, ClusteredServerConfig> cServerConfigMap = 
            clusterConfig.getClusteredServerConfigMap();
        
        //doing the following to enable reuse of the 
        //registerLoadBalancerServerMonitorTree method
        Map<String, ServerConfig> serverConfigMap = new HashMap<String, ServerConfig>();
        for (String key : cServerConfigMap.keySet()) 
            serverConfigMap.put(key, (ServerConfig)cServerConfigMap.get(key));
        
        for (String serverName : serverConfigMap.keySet()) {
            registerLoadBalancerServerMonitorTree(
                lbcm, serverConfigMap, serverName);
        }
    }
    
    protected DomainConfig getDomainConfig() {
        return 
        ProxyFactory.getInstance(getConn()).getDomainRoot().getDomainConfig();
    }
    
    protected LBDeregistrationUtil getLBDeregistrationUtil(
        MBeanServer mBeanServer) {
        return LBDeregistrationUtil.getInstance(mBeanServer);
    }
    
    protected void debug(Object o) {
        if (o instanceof Throwable) ((Throwable)o).printStackTrace();
        AMXDebug.getInstance().getOutput("TestLoadBalancerLoad")
            .println(StringUtil.toString(", ", o));
    }   
    
    protected void logWarning(String prefix, Exception ex) {
        AMXRootLogger.getInstance().warning(prefix + " : " + ex.getMessage());
    }
}