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

/*
 * $Header: /cvs/glassfish/admin-core/mbeanapi/src/java/com/sun/appserv/management/helper/LBConfigHelper.java
 * $Revision: 1.0
 * $Date: 2006/03/09 20:30:27 $
 */
package com.sun.appserv.management.helper;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.HashSet;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.management.MBeanException;

import com.sun.appserv.management.DomainRoot;
import com.sun.appserv.management.config.ServerRefConfig;
import com.sun.appserv.management.config.ClusterRefConfig;
import com.sun.appserv.management.config.LBConfig;
import com.sun.appserv.management.config.LoadBalancerConfig;
import com.sun.appserv.management.config.DomainConfig;
import com.sun.appserv.management.config.ClusterConfig;
import com.sun.appserv.management.config.StandaloneServerConfig;
import com.sun.appserv.management.config.ClusteredServerConfig;
import com.sun.appserv.management.config.DeployedItemRefConfig;
import com.sun.appserv.management.config.J2EEApplicationConfig;
import com.sun.appserv.management.config.LBConfigKeys;
import com.sun.appserv.management.config.ObjectTypeValues;
import com.sun.appserv.management.config.ServerConfig;
import com.sun.appserv.management.ext.lb.LoadBalancer;
import com.sun.appserv.management.monitor.LoadBalancerApplicationMonitor;
import com.sun.appserv.management.monitor.LoadBalancerClusterMonitor;
import com.sun.appserv.management.monitor.LoadBalancerContextRootMonitor;
import com.sun.appserv.management.monitor.LoadBalancerMonitor;
import com.sun.appserv.management.monitor.LoadBalancerServerMonitor;
import com.sun.appserv.management.monitor.MonitoringRoot;
import com.sun.appserv.management.monitor.statistics.LoadBalancerContextRootStats;
import com.sun.appserv.management.monitor.statistics.LoadBalancerServerStats;
import com.sun.appserv.management.util.misc.Formatter;
import com.sun.appserv.management.util.misc.StringSourceBase;

/**
 * Helper class for simplifying load balancer administration.
 * @since AppServer 9.0
 */
public final class LBConfigHelper {
    
    /**
     * Public constructor
     */
    public LBConfigHelper(final DomainRoot domainRoot) {
        mDomainConfig = domainRoot.getDomainConfig();
        mDomainRoot = domainRoot;
        mLogger = Logger.getLogger(domainRoot.getMBeanLoggerName());
        resBundle = ResourceBundle.getBundle(this.getClass().getPackage().getName()+".LocalStrings");
        formatter = new Formatter(new StringSourceBase());
    }
    
    /**
     * Returns a filtered LBConfig list given the name of the server reference
     * @return Map of items, keyed by name.
     * @see com.sun.appserv.management.config.LBConfig
     */
    public List<LBConfig> getLBConfigsForServer(String serverName) {
        Map<String,LBConfig> lbconfigs = mDomainConfig.getLBConfigMap();
        List<LBConfig> list = new ArrayList<LBConfig>();
        for(LBConfig config:lbconfigs.values()){
            Map<String,ServerRefConfig> map = config.getServerRefConfigMap();
            for(String name:map.keySet()){
                if(name.equals(serverName)){
                    list.add(config);
                }
            }
        }
        return list;
    }
    
    /**
     * Returns a filtered LBConfig list given the name of the cluster reference
     * @return Map of items, keyed by name.
     * @see com.sun.appserv.management.config.LBConfig
     */
    public List<LBConfig> getLBConfigsForCluster(String clusterName) {
        Map<String,LBConfig> lbconfigs = mDomainConfig.getLBConfigMap();
        List<LBConfig> list = new ArrayList<LBConfig>();
        for(LBConfig config:lbconfigs.values()){
            Map<String,ClusterRefConfig> map = config.getClusterRefConfigMap();
            for(String name:map.keySet()){
                if(name.equals(clusterName)){
                    list.add(config);
                }
            }
        }
        return list;
    }
    
    /**
     * Returns a filtered list of server references given the name of the load
     * balancer config.
     * @return Map of items, keyed by name.
     * @see com.sun.appserv.management.config.LBConfig
     */
    public Map<String, ServerRefConfig> getServersInLBConfig(String lbConfigName) {
        Map<String,LBConfig> lbconfigs = mDomainConfig.getLBConfigMap();
        LBConfig lbconfig = lbconfigs.get(lbConfigName);
        return lbconfig.getServerRefConfigMap();
    }
    
    /**
     * Returns a filtered list of cluster references given the name of the load
     * balancer config.
     * @return Map of items, keyed by name.
     * @see com.sun.appserv.management.config.LBConfig
     */
    public Map<String, ClusterRefConfig> getClustersInLBConfig(String lbConfigName) {
        Map<String,LBConfig> lbconfigs = mDomainConfig.getLBConfigMap();
        LBConfig lbconfig = lbconfigs.get(lbConfigName);
        return lbconfig.getClusterRefConfigMap();
    }
    
    /**
     * Lists all the standalone server instances and clusters in the domain.
     *
     * @return all the standalone server instances and clusters in the domain
     */
    public String[] listTargets() {
        Set<String> targetSet = new HashSet<String>();
        
        Map<String,ClusterConfig> cConfigMap =
                mDomainConfig.getClusterConfigMap();
        
        if (cConfigMap != null) {
            targetSet.addAll( cConfigMap.keySet());
        }
        Map<String,StandaloneServerConfig> ssConfigMap =
                mDomainConfig.getStandaloneServerConfigMap();
        
        if (ssConfigMap != null) {
            targetSet.addAll( ssConfigMap.keySet());
        }
        
        String[] targetArr = new String[targetSet.size()];
        return (String[]) targetSet.toArray(targetArr);
    }
    
    /**
     * Lists all the standalone server instances and clusters in the load
     * balancer.
     *
     * @param lbName    Name of the load balancer
     *
     * @return all the standalone server instances and clusters in the load
     * balancer
     */
    public String[] listTargets(final String lbName) {
        
        Set<String> targetSet = new HashSet<String>();
        
        Map<String, LoadBalancerConfig> lbMap =
                mDomainConfig.getLoadBalancerConfigMap();
        
        if (lbMap == null) {
            return null;
        }
        
        LoadBalancerConfig lb = (LoadBalancerConfig) lbMap.get(lbName);
        if (lb == null) {
            return null;
        }
        
        String lbConfigName = lb.getLbConfigName();
        Map<String, LBConfig> lbConfigMap = mDomainConfig.getLBConfigMap();
        if ((lbConfigMap == null) || (lbConfigName == null) ){
            return null;
        }
        LBConfig lbConfig = (LBConfig) lbConfigMap.get(lbConfigName);
        if (lbConfig == null) {
            return null;
        }
        Map<String,ClusterRefConfig> cRefMap =
                lbConfig.getClusterRefConfigMap();
        
        if ( cRefMap != null) {
            targetSet.addAll(cRefMap.keySet());
        }
        Map<String,ServerRefConfig> sRefMap =
                    lbConfig.getServerRefConfigMap();
         
        if ( sRefMap != null) {
            targetSet.addAll(sRefMap.keySet());
        }
        
        String [] targetArr = new String[targetSet.size()];
        
        return (String[]) targetSet.toArray(targetArr);
        
    }
    
    /**
     * Creates a load balancer element ( and the necessary config)
     *
     *
     * @param loadbalancerName      Name of the load balancer
     * @param autoApplyEnabled      Auto apply enabled or not
     * @param targets               Standalone server instances or clusters
     * @param params                This is optional, loadbalancer
     *                              configuration elemements. The valid
     *                              keys are:
     *     <ul>
     *        <li>{@link LBConfigKeys#RESPONSE_TIMEOUT_IN_SECONDS_KEY}</li>
     *        <li>{@link LBConfigKeys#HTTPS_ROUTING_KEY}</li>
     *        <li>{@link LBConfigKeys#RELOAD_POLL_INTERVAL_IN_SECONDS_KEY}</li>
     *        <li>{@link LBConfigKeys#MONITORING_ENABLED_KEY}</li>
     *        <li>{@link LBConfigKeys#ROUTE_COOKIE_ENABLED_KEY}</li>
     *     </ul>
     *
     * @return {@link LoadBalancerConfig}
     */
    public LoadBalancerConfig createLoadbalancer(String loadbalancerName,
            boolean autoApplyEnabled, String[] targets, Map<String,String> params) {
        
        // first create the lb-config element
        
        if ( loadbalancerName == null ) {
            throw new IllegalArgumentException(
                    "loadbalancerName can not be null");
        }
        
        //iniitialize lb-config name that we are going to use for this lb
        String lbConfigName = loadbalancerName + LB_CONFIG_SUFFIX;
        
        //the following block tries to get a unique lb-config name
        //get all the lb-configs
        Map<String,LBConfig> lbconfigMap = mDomainConfig.getLBConfigMap();
        if(lbconfigMap != null){
            //keep appending a counter till there is no lb-config by that name
            for(int i=1;lbconfigMap.get(lbConfigName) != null;i++){
                lbConfigName = loadbalancerName + LB_CONFIG_SUFFIX + "_" + i;
            }
        }
        
        //create the lb-config
        LBConfig lbConfig = mDomainConfig.createLBConfig(lbConfigName, params);
        
        if ( targets != null) {
            for (int idx =0; idx < targets.length; idx++) {
                String targetName = targets[idx];
                
                if ( isCluster(targetName)) {
                    lbConfig.createClusterRefConfig(targetName, null);
                } else if ( isStandaloneServer( targetName)) {
                    lbConfig.createServerRefConfig(targetName, null);
                }
            }
        }
        
        // now create the load-balancer element
        return mDomainConfig.createLoadBalancerConfig(loadbalancerName,
                lbConfigName, autoApplyEnabled, null);
    }
    
    /**
     * Deletes a load balancer element ( and the necessary config, if nobody
     * else is using this config)
     */
    public void removeLoadbalancer(String loadbalancerName) {

        //get the load balancers map
        Map<String, LoadBalancerConfig> lbMap =
                mDomainConfig.getLoadBalancerConfigMap();
        if ( lbMap == null) {
            return;
        }
        
        //first get the lbConfigName
        LoadBalancerConfig loadbalancerConfig = lbMap.get(loadbalancerName);
        if(loadbalancerConfig == null){
            return;
        }
        String lbConfigName = loadbalancerConfig .getLbConfigName();
        
        // now remove load-balancer element
        mDomainConfig.removeLoadBalancerConfig(loadbalancerName);

        //get the load balancers map again
        lbMap = mDomainConfig.getLoadBalancerConfigMap();
        if ( lbMap == null) {
            return;
        }
        
        // now remove lb-config, if nobody is using it
        for(LoadBalancerConfig lbConfig : lbMap.values()){
            if ( lbConfig.getLbConfigName().equals(lbConfigName)) {
                // this load-balancer element is still using it, just return
                // else continue to check other elements
                return;
            }
        }
        
        // no load-balancer element is using this lb-config, remove it
        mDomainConfig.removeLBConfig(lbConfigName);
    }
    
    
    /**
     * Disables load balancing for a server with a quiescing period specififed
     * by the timeout .
     *
     * @param target target server whose load balancing has to be disabled
     * @param timeout quiescing time
     */
    public void disableServer(String target, int timeout) {
        setServerStatus(target,timeout,false);
    }
    
    /**
     * Enables a server for load balancing.
     *
     * @param target target server whose load balancing has to be enabled
     */
    public void enableServer(String target) {
        setServerStatus(target, 0,true);
    }
    
    /**
     * Disables load balancing for a particular application in a server instance
     * with a quiescing period specififed by the timeout .
     *
     * @param target target server where the application has been deployed
     * @param appName application name.
     * @param timeout quiescing time
     */
    public void disableApplication(String target, String appName, int timeout) {
        setApplicationStatus(target, appName, timeout, false);
    }
    
    /**
     * Enables load balancing for a particular application in a server instance
     *
     * @param target target server where the application has been deployed
     * @param appName application name.
     */
    public void enableApplication(String target, String appName) {
        setApplicationStatus(target, appName, 0, true);
    }
    
    /**
     * This is a convenience method to fetch the stats for server instance(s) 
     * which are either standalone or clustered or both
     * @param targetLoadBalancer    Load Balancer for which stats are to be
     *                              returned
     * @param target                Target cluster name. This is used if
     *                              allTargets (next param) is false.
     * @param allTargets            list Monitors for all targets.
     *
     * @return Map of LoadBalancerServerStats and the fully qualified names 
     * of the servers i.e. clustername.servername or servername
     */
    public Map<String, LoadBalancerServerStats> getInstanceStats(
            final String targetLoadBalancer, final String target, boolean allTargets) {
        
        if ( targetLoadBalancer == null )
            throw new IllegalArgumentException(
                    "Load Balancer Name can not be null");
        if ( !allTargets && target == null )
            throw new IllegalArgumentException(
                    "Specify AllTargets or atleast one target");
        
        Map<String,LoadBalancerServerStats>
                loadBalancerServerStatsMap = new HashMap<String,LoadBalancerServerStats>();
        
        Map<String, LoadBalancerServerMonitor> instanceMonitorMap =
                getInstanceMonitors(targetLoadBalancer, target, allTargets);
        
        for (String serverFQName : instanceMonitorMap.keySet()) {
            LoadBalancerServerMonitor loadBalancerServerMonitor =
                    instanceMonitorMap.get(serverFQName);
            LoadBalancerServerStats loadBalancerServerStats =
                    loadBalancerServerMonitor.getLoadBalancerServerStats();
            loadBalancerServerStatsMap.put(serverFQName, loadBalancerServerStats);
        }
        return loadBalancerServerStatsMap;
    }
    
    /**
     * This is a convenience method to fetch the stats for context roots 
     * for an application.
     * @param targetLoadBalancer    Load Balancer for which stats are to be
     *                              returned
     * @param target                Target cluster name. This is used if
     *                              allTargets (next param) is false.
     * @param allTargets            list Monitors for all targets.
     *
     * @return Map of LoadBalancerContextRootStats and the fully qualified names 
     * of the servers i.e. clustername.servername or servername
     */
    public Map<String, LoadBalancerContextRootStats> getInstanceStats(
            final String targetLoadBalancer, final String contextRoot,
            final String target, boolean allTargets) {
        
        if ( contextRoot == null )
            throw new IllegalArgumentException("ContextRoot can not be null");
        if ( targetLoadBalancer == null )
            throw new IllegalArgumentException(
                    "Load Balancer Name can not be null");
        if ( !allTargets && target == null )
            throw new IllegalArgumentException(
                    "Specify AllTargets or atleast one target");
        
        Map<String,LoadBalancerContextRootStats>
                loadBalancerContextRootStatsMap = new HashMap<String,LoadBalancerContextRootStats>();
        
        Map<String, LoadBalancerServerMonitor> instanceMonitorMap =
                getInstanceMonitors(targetLoadBalancer, target, allTargets);
        
        for (String serverFQName : instanceMonitorMap.keySet()) {
            LoadBalancerServerMonitor loadBalancerServerMonitor =
                    instanceMonitorMap.get(serverFQName);
            Map<String, LoadBalancerApplicationMonitor>
                    loadBalancerApplicationMonitorMap =
                    loadBalancerServerMonitor.getLoadBalancerApplicationMonitorMap();
            for (String appName : loadBalancerApplicationMonitorMap.keySet()) {
                LoadBalancerApplicationMonitor loadBalancerApplicationMonitor =
                        loadBalancerApplicationMonitorMap.get(appName);
                Map<String, LoadBalancerContextRootMonitor>
                        loadBalancerContextRootMonitorMap =
                        loadBalancerApplicationMonitor.getLoadBalancerContextRootMonitorMap();
                LoadBalancerContextRootMonitor loadBalancerContextRootMonitor =
                        loadBalancerContextRootMonitorMap.get(contextRoot);
                loadBalancerContextRootStatsMap.put(
                        serverFQName, (LoadBalancerContextRootStats)loadBalancerContextRootMonitor.getStats());
            }
        }
        return loadBalancerContextRootStatsMap;
    }
    
    /**
     * This method supports the configure-http-lb-config CLI command. It creates a lb-config, cluster-ref, health-checker by using
     * the given parameters.
     * @param configName the name for the lb-config element that will be created
     * @param target cluster-ref or server-ref parameter of lb-config
     * @param options Map of option name and option value. The valid options are
     *          responsetimeout response-timeout-in-seconds attribute of lb-config
     *          httpsrouting https-routing parameter of lb-config
     *          reloadinterval reload-poll-interval-in-seconds parameter of lb-config
     *          monitor monitoring-enabled parameter of lb-config
     *          routecookie route-cookie-enabled parameter of lb-config
     *          filepath the path to the file where loadbalancer.xml will be exported
     *          healthcheckerurl url attribute of health-checker
     *          healthcheckerinterval interval-in-seconds parameter of health-checker
     *          healthcheckertimeout timeout-in-seconds parameter of health-checker
     * @param filePath the path to the file where loadbalancer.xml will be exported
     * @return the path to the newly written file
     * @since AS 9.0
     * @throws javax.management.MBeanException exception indicating the original cause of problm
     */
    public String configureHTTPLBConfig(String configName, String target, 
            Map<String,String> options, String filePath) throws MBeanException {
        
        String healthCheckerUrl = options.get(HEALTH_CHECKER_URL);
        String healthCheckerInterval = options.get(HEALTH_CHECKER_INTERVAL);
        String healthCheckerTimeout = options.get(HEALTH_CHECKER_TIMEOUT);
        String lbPolicy = options.get(LB_POLICY);
        String lbPolicyModule = options.get(LB_POLICY_MODULE);
        boolean isCluster = isCluster(target);
        Map<String,String> params = getParams(options);
        Map<String,LBConfig> lbconfigs = mDomainConfig.getLBConfigMap();
        if(lbconfigs.get(configName)!=null){
                String msg = formatter.format(resBundle.getString("LbConfigExists"),
                        configName);
                throw new MBeanException(new RuntimeException(msg));            
        }
        if(!isCluster){
            if((lbPolicy!=null) || (lbPolicyModule!=null)){
                //throw exception
                String msg = formatter.format(resBundle.getString("NotCluster"),
                        target);
                throw new MBeanException(new RuntimeException(msg));
            }
        }
        //create the lb-config
        LBConfig lbConfig = mDomainConfig.createLBConfig(configName, params);
        
        if(isCluster){
            
            //create the cluster-ref
            ClusterRefConfig clusterRefConfig = lbConfig.createClusterRefConfig(target, null,null);
            if(lbPolicy!=null){
                clusterRefConfig.setLBPolicy(lbPolicy);
            }
            if(lbPolicyModule!=null){
                clusterRefConfig.setLBPolicyModule(lbPolicyModule);
            }
            //create the health checker
            clusterRefConfig.createHealthCheckerConfig(healthCheckerUrl,
                    healthCheckerInterval, healthCheckerTimeout);
            //lb-enable all the instances in the cluster
            enableServer(target);
        }else{
            //create the server-ref
            ServerRefConfig serverRefConfig = lbConfig.createServerRefConfig(target,null,true,true);
            //create the health checker
            serverRefConfig.createHealthCheckerConfig(healthCheckerUrl,
                    healthCheckerInterval, healthCheckerTimeout);
        }
        //lb-enable all applications in the cluster
        enableAllApplications(target);
        if(filePath!=null){
            // create a load-balancer element so that we can get loadbalancer.xml
            LoadBalancer lb = createLoadBalancer(configName);
            //get the loadbalancer.xml
            String lbxml = lb.getLoadBalancerXML();
            mDomainConfig.removeLoadBalancerConfig(configName+LB_SUFFIX);
            //write it to the file.
            return writeToFile(lbxml, filePath);
        }
        return null;
    }
    
    /**
     * Configures the lb-weight attribute of each instance with the corresponding weight. Corresponds to the CLI 
     * command configure-lb-weight
     * @param clusterName name of the cluster
     * @param instanceVsWeights Map of instance name Vs weight
     */
    public void configureLBWeight(String clusterName, Map instanceVsWeights) {
        
        //get ALL clustered <server> elements
        Map<String,ClusterConfig> clusterConfigMap = mDomainConfig.getClusterConfigMap();
        //get the cluster config for the given cluster
        ClusterConfig clusterConfig = clusterConfigMap.get(clusterName);
        if(clusterConfig == null)
            throw new IllegalArgumentException(formatter.format(resBundle.getString("InvalidCluster"),clusterName));
        //get the map of clustered server config
        Map<String,ClusteredServerConfig> clusteredServerConfigMap = clusterConfig.getClusteredServerConfigMap();
        //iterate through all the given weights and instances
        for(Object instance:instanceVsWeights.keySet()){
            //get the corresponding clustered server config for this instance
            ClusteredServerConfig clusteredServerConfig = clusteredServerConfigMap.get(instance);
            if(clusteredServerConfig == null)
                throw new IllegalArgumentException(formatter.format(resBundle.getString("InvalidInstance"),instance,clusterName));
            //set the lb-weight
            clusteredServerConfig.setLBWeight( instanceVsWeights.get(instance).toString());
        }
    }

    /**
     * Enables all user applications in the given target
     * @param target The target for which all the applications have to be enabled
     */
    public void enableAllApplications(String target){
        //get the server config for this target
        Map<String,ServerConfig> serverConfigMap = mDomainConfig.getServerConfigMap();
        ServerConfig serverConfig = serverConfigMap.get(target);
        if(serverConfig != null){
            //get all the deployed item config
            Map<String,DeployedItemRefConfig> deployedItemRefConfigMap = serverConfig.getDeployedItemRefConfigMap();
            //iterate through all the deployed items
            for (DeployedItemRefConfig deployedItemRefConfig : deployedItemRefConfigMap.values()){
                //get the deployed app 
                J2EEApplicationConfig app = mDomainConfig.getJ2EEApplicationConfigMap().get(deployedItemRefConfig.getName());
                if(app == null) {
                    continue;
                }
                //if the type is user, then only set the lb-enabled to true
                if(app.getObjectType().equals(ObjectTypeValues.USER)) {
                    deployedItemRefConfig.setLBEnabled(true);
                }
            }
        }
    }

    // PRIVATE METHODS
    
    boolean isCluster(String name) {
        Map<String,ClusterConfig> cConfigMap =
                mDomainConfig.getClusterConfigMap();
        
        if (cConfigMap == null) {
            return false;
        }
        ClusterConfig cConfig = (ClusterConfig) cConfigMap.get(name);
        if ( cConfig == null) {
            return false;
        } else {
            return true;
        }
    }
    
    boolean isStandaloneServer(String name) {
        Map<String,StandaloneServerConfig> ssConfigMap =
                mDomainConfig.getStandaloneServerConfigMap();
        
        if (ssConfigMap == null) {
            return false;
        }
        StandaloneServerConfig ssConfig = (StandaloneServerConfig)
        ssConfigMap.get(name);
        if ( ssConfig == null) {
            return false;
        } else {
            return true;
        }
    }
    
    /**
     * sets the lb-enabled flag for the target, also sets the disable-timeout-in-minutes
     * if the status flag is false.
     * @param target the cluster or the server for which the lb-enabled has to be set
     * @param timeout The disable-timeout-in-minutes if the server has to be disabled
     * @param status true to enable, false to disable the server
     */
    private void setServerStatus(String target, int timeout, boolean status) {
        //timeout has to be specified for disable operation
        if (timeout < 0 && !status) {
            String msg = resBundle.getString("InvalidNumber");
            throw new IllegalArgumentException(msg);
        }
        
        try {
            // disables cluster if target is a cluster
            if (isCluster(target)) {
                //get the cluster config
                ClusterConfig cRef = mDomainConfig.getClusterConfigMap().get(target);
                if (cRef == null) {
                    mLogger.log(Level.FINEST," server " + target +
                            " does not exist in any cluster in the domain");
                    String msg = formatter.format(resBundle.getString("ServerNotDefined"),
                            target);
                    throw new MBeanException(new RuntimeException(msg));
                }

                //iterate through all the servers in the cluster
                for(ServerRefConfig sRef : cRef.getServerRefConfigMap().values()){
                    //set lb-enabled flag and the timeout with error checks
                    setLBEnabled(sRef, status, timeout, target);
                }
            } else { // target is a server
                ServerRefConfig sRef = null;
                boolean foundTarget = false;
                //get all the lb-configs
                Map<String,LBConfig> lbConfigs = mDomainConfig.getLBConfigMap();
                //iterate through lb-configs
                for(LBConfig lbConfig : lbConfigs.values()){
                    //get the server-ref in this lb-config
                    Map<String,ServerRefConfig> serverRefs = lbConfig.getServerRefConfigMap();
                    //get the server-ref for this target
                    sRef = serverRefs.get(target);
                    if (sRef == null) {
                        mLogger.log(Level.FINEST," server " + target +
                                " does not exist in " + serverRefs);
                    } else {
                        foundTarget = true;
                        break;
                    }
                }
                // did not find server target
                if (!foundTarget) {
                    //get the server-ref from some some cluster
                    sRef = getServerRefConfigFromCluster(target);
                    if (sRef == null) {
                        mLogger.log(Level.FINEST," server " + target +
                                " does not exist in any cluster in the domain");
                        String msg = formatter.format(resBundle.getString("ServerNotDefined"),
                                            target);
                        throw new MBeanException(new RuntimeException(msg));
                    }
                }
                //set the lb-enabled flag
                setLBEnabled(sRef, status, timeout, target);
            }
            
        } catch(Exception ce) {
            ce.printStackTrace();
            throw new RuntimeException(ce);
        }
        
    }

    /**
     * sets the lb-enabled flag for the target
     * @param sRef server-ref class
     * @param status the enable status
     * @param timeout disable timeout
     * @param target the target cluster or server
     * @throws javax.management.MBeanException wrapper for all exceptions
     */
    private void setLBEnabled(final ServerRefConfig sRef, final boolean status, 
            final int timeout, final String target) throws MBeanException {
        int curTout = sRef.getDisableTimeoutInMinutes();
        //check if it is already in the state desired
        boolean enabled = sRef.getLBEnabled();
        if(!status){
            if ((enabled == false) && (curTout == timeout)) {
                        String msg = formatter.format(resBundle.getString("ServerDisabled"),
                                sRef.getRef());
                        throw new MBeanException(new Exception(msg));
            }
            //set the disable timeout in minutes
            sRef.setDisableTimeoutInMinutes(timeout);
            //set the lb-enabled to false
            sRef.setLBEnabled(false);
            mLogger.log(Level.INFO,formatter.format(resBundle.getString(
                    "http_lb_admin.ServerDisabled"), target));
        }else{
            if (enabled == true) {
                        String msg = formatter.format(resBundle.getString("ServerEnabled"),
                                sRef.getRef());
                throw new MBeanException(new Exception("ServerEnabled"));
            }
            sRef.setLBEnabled(true);
            mLogger.log(Level.INFO,formatter.format(resBundle.getString(
                    "http_lb_admin.ServerEnabled"), target));
        }
    }
    
    /**
     * sets the lb-enabled flag to the value of status for the given target
     * if status is false also sets the disable-timeout-in-minutes for the application
     * @param target server or cluster target
     * @param appName application name
     * @param timeout disable timeout in minutes
     * @param status enable status
     */
    private void setApplicationStatus(String target, String appName,int timeout, boolean status){
        
        //disable timeout is required for disable operation
        if (timeout < 0 && !status) {
            String msg = resBundle.getString("InvalidNumber");
            throw new IllegalArgumentException(msg);
        }
        
        try {
            DeployedItemRefConfig dRef = null;
            // disables cluster if target is a cluster
            if (isCluster(target)) {
                //get the clusterConfig for this cluster
                Map<String,ClusterConfig> clusterConfigs = mDomainConfig.getClusterConfigMap();
                ClusterConfig clusterConfig = clusterConfigs.get(target);
                //get the deployed item object corresponding to the given appName
                dRef = clusterConfig.getDeployedItemRefConfigMap().get(appName);
            } else { // target is a server
                //get the standalone serverConfig
                Map<String,StandaloneServerConfig> ssConfigMap =
                        mDomainConfig.getStandaloneServerConfigMap();
                StandaloneServerConfig ssc = ssConfigMap.get(target);
                if (ssc == null) {
                    //get the clustered server config
                    ClusteredServerConfig s = mDomainConfig.getClusteredServerConfigMap().get(target);
                    //get the deployed item object corresponding to the given appName
                    dRef = s.getDeployedItemRefConfigMap().get(appName);
                }else{
                    //get the deployed item object corresponding to the given appName
                    dRef = ssc.getDeployedItemRefConfigMap().get(appName);
                }
            }
            if (dRef == null) {
                mLogger.log(Level.FINEST," server " + target +
                        " does not exist in any cluster in the domain");
                String msg = formatter.format(resBundle.getString("AppRefNotDefined"),
                        target);
                throw new MBeanException(new RuntimeException(msg));
            }
            int curTout = Integer.parseInt(
                    dRef.getDisableTimeoutInMinutes());
            //check if the app is already in the state desired
            boolean enabled = dRef.getLBEnabled();
            if(!status){
                if ((enabled == false) && (curTout == timeout)) {
                    String msg = resBundle.getString("AppDisabledOnServer"
                            );
                    throw new MBeanException(new Exception(msg));
                }
                //set the disable timeout
                dRef.setDisableTimeoutInMinutes( "" + timeout );
                //disable the app
                dRef.setLBEnabled(false);
                mLogger.log(Level.INFO,resBundle.getString(
                        "http_lb_admin.ApplicationDisabled"));
            }else{
                if (enabled == true) {
                    String msg = resBundle.getString("AppEnabledOnServer"
                            );
                    throw new MBeanException(new Exception(msg));
                }
                //enable the app
                dRef.setLBEnabled(true);
                mLogger.log(Level.INFO,resBundle.getString(
                        "http_lb_admin.ApplicationEnabled"));
            }
        } catch(Exception ce) {
            throw new RuntimeException(ce);
        }
        
    }
    
    /**
     * returns the server-ref elements corresponding to the target
     * by searching all the cluster-ref elements.
     * @param target target server or cluster
     * @return ServerRefConfig the class that refers to the server-ref obtained by searching through all the clusters
     * @throws javax.management.MBeanException wrapper for all the exceptions
     */
    private ServerRefConfig getServerRefConfigFromCluster(String target)
    throws MBeanException {
        // check if this server is part of cluster, then
        // turn on lb-enable flag in the cluster.
        //get all the lb-configs
        Map<String,LBConfig> lbConfigs = mDomainConfig.getLBConfigMap();
        //iterate through all the lb-configs
        for(LBConfig lbConfig : lbConfigs.values()){
            //get the cluster-refs in this lb-config
            Map<String,ClusterRefConfig> clusterRefs = lbConfig.getClusterRefConfigMap();
            //iterate through all the cluster-refs
            for(ClusterRefConfig clusterRef :clusterRefs.values()){
                
                //get the cluster name
                String clusterName = clusterRef.getReferencedClusterName();
                
                //get the clusterConfig for this clustername
                Map<String,ClusterConfig> clusterConfigs =  mDomainConfig.getClusterConfigMap();
                ClusterConfig config = clusterConfigs.get(clusterName);
                
                //get all the server-refs for this cluster
                Map<String,ServerRefConfig> serverRefConfigs = config.getServerRefConfigMap();
                
                //iterate through the server-refs
                for(ServerRefConfig serverRefConfig : serverRefConfigs.values() ){
                    
                    //if this server-ref matches the given target , return it
                    if(serverRefConfig.getName().equals(target)){
                        return serverRefConfig;
                    }
                    
                }
            }
        }
        return null;
    }

    /**
     * Gets the Maps of LoadBalancerServerMonitor for specified load balancer and
     * specified target(s).
     *
     * @param targetLoadBalancer    Load Balancer for which Monitors are to be
     *                              returned
     * @param target                Target cluster name. This is used if
     *                              allTargets (next param) is false.
     * @param allTargets            list Monitors for all targets.
     *
     * @return Map of LoadBalancerServerMonitors and their names
     */
    public Map<String, LoadBalancerServerMonitor> getInstanceMonitors(
        final String targetLoadBalancer, final String target, boolean allTargets) {

        Map<String,LoadBalancerServerMonitor> 
        loadBalancerServerMonitorMap = new HashMap<String,LoadBalancerServerMonitor>();
    
        MonitoringRoot mRoot = mDomainRoot.getMonitoringRoot();
        Map<String, LoadBalancerMonitor> loadBalancerMonitorMap =
                mRoot.getLoadBalancerMonitorMap();
        LoadBalancerMonitor loadBalancerMonitor =
                loadBalancerMonitorMap.get(targetLoadBalancer);
        
        Map<String, LoadBalancerClusterMonitor> loadBalancerClusterMonitorMap =
                loadBalancerMonitor.getLoadBalancerClusterMonitorMap();
        if (!allTargets) {
            LoadBalancerClusterMonitor loadBalancerClusterMonitor =
                loadBalancerClusterMonitorMap.get(target);
            populateLoadBalancerServerMonitorMap(target,
                loadBalancerServerMonitorMap, loadBalancerClusterMonitor);
        } else {
            for (String clusterName : loadBalancerClusterMonitorMap.keySet()) {
                LoadBalancerClusterMonitor loadBalancerClusterMonitor =
                    loadBalancerClusterMonitorMap.get(clusterName);
                populateLoadBalancerServerMonitorMap(target,
                    loadBalancerServerMonitorMap, loadBalancerClusterMonitor);
            }
        }
        return loadBalancerServerMonitorMap;
    }

    /**
     * Returns the stats for an instance. If the instance is being load 
     * balanced by multiple load balancers the a map of stats keyed by 
     * load balancer name is returned
     *
     * @param serverName    instance name 
     *
     * @return Map of LoadBalancerServerMonitor keyed by load balancer name
     */
    public Map<String, LoadBalancerServerMonitor> 
        getInstanceAggregateStats(String serverName) {
        
        Collection<LoadBalancerConfig> loadBalancers = 
            getLoadBalancers(serverName, false).values();

        Map<String,LoadBalancerServerMonitor> 
            loadBalancerServerMonitorMap = new HashMap<String,LoadBalancerServerMonitor>();

        MonitoringRoot mRoot = mDomainRoot.getMonitoringRoot();
        Map<String, LoadBalancerMonitor> loadBalancerMonitorMap =
            mRoot.getLoadBalancerMonitorMap();
        
        for (LoadBalancerConfig loadBalancer : loadBalancers) {
            String targetLoadBalancer = loadBalancer.getName();
            LoadBalancerMonitor loadBalancerMonitor =
                loadBalancerMonitorMap.get(targetLoadBalancer);
        
            Map<String, LoadBalancerClusterMonitor> loadBalancerClusterMonitorMap =
                loadBalancerMonitor.getLoadBalancerClusterMonitorMap();
            for (String clusterName : loadBalancerClusterMonitorMap.keySet()) {
                LoadBalancerClusterMonitor loadBalancerClusterMonitor =
                    loadBalancerClusterMonitorMap.get(clusterName);
                LoadBalancerServerMonitor loadBalancerServerMonitor =
                    loadBalancerClusterMonitor.getLoadBalancerServerMonitorMap()
                    .get(serverName);
                loadBalancerServerMonitorMap.put(
                    targetLoadBalancer, loadBalancerServerMonitor);
            }
        }
        return loadBalancerServerMonitorMap;
    }
    
    /**
     * Returns the load balancers loadbalancing a target : 
     * standalone instance, clustered instance or a cluster
     *
     * @param targetName    standalone instance name or
     *                      clustered instance name or a cluster name
     * @param isCluster     whether the targetName is a cluster or instance name
     *
     * @return Map of LoadBalancerConfig keyed by load balancer name
     */
    public Map<String,LoadBalancerConfig> getLoadBalancers(
        String targetName, boolean isCluster) {
        Map<String, LBConfig> lbConfigMap = fetchLBConfigs(targetName, isCluster);
        return fetchLoadBalancerConfigs(lbConfigMap);
    }
    
    private Map<String, LBConfig> fetchLBConfigs(
        String targetName, boolean isCluster) {

        Map<String,LBConfig> result = new HashMap<String,LBConfig>();
        Map<String,LBConfig> lbConfigMap = mDomainConfig.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 if (isStandaloneServer(targetName)) {
         /*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;
                    }
                }
            }
        } else {//we assume that its a clustered instance name
            for (String lbConfigName : lbConfigMap.keySet()) {
                LBConfig lbConfig = lbConfigMap.get(lbConfigName);
                Map<String,ClusterRefConfig> lbClusterRefConfigMap =
                    lbConfig.getClusterRefConfigMap();
                Map<String,ClusterConfig> clusterConfigMap =
                    mDomainConfig.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;
    }    
    
    private Map<String,LoadBalancerConfig> fetchLoadBalancerConfigs(
        Map<String, LBConfig> lbConfigMap) {

        Map<String,LoadBalancerConfig> relevantLoadBalancerConfigMap = new HashMap<String,LoadBalancerConfig>();

        for (String lbConfigName : lbConfigMap.keySet()) {
            //collect all load-balancer elements which refer to this lb-config
            Map<String,LoadBalancerConfig> allLoadBalancerConfigMap =
                 mDomainRoot.getDomainConfig().getLoadBalancerConfigMap();

            for (String loadBalancerName : allLoadBalancerConfigMap.keySet()) {
                LoadBalancerConfig loadBalancerConfig = 
                    allLoadBalancerConfigMap.get(loadBalancerName);
                if (loadBalancerConfig.getLbConfigName().equals(lbConfigName))
                    relevantLoadBalancerConfigMap.put(
                        loadBalancerName, loadBalancerConfig);
            }
        }
        return relevantLoadBalancerConfigMap;
    }  
    
    private void populateLoadBalancerServerMonitorMap(String target,
        Map<String, LoadBalancerServerMonitor> loadBalancerServerMonitorMap,
        LoadBalancerClusterMonitor loadBalancerClusterMonitor) {
        
        Map<String, LoadBalancerServerMonitor> tmpLoadBalancerServerMonitorMap =
                loadBalancerClusterMonitor.getLoadBalancerServerMonitorMap();
        for (String serverName : tmpLoadBalancerServerMonitorMap.keySet()) {
            LoadBalancerServerMonitor loadBalancerServerMonitor =
                    tmpLoadBalancerServerMonitorMap.get(serverName);
            if (isStandaloneServer(serverName))
                loadBalancerServerMonitorMap.put(
                        serverName, loadBalancerServerMonitor);
            else loadBalancerServerMonitorMap.put(
                    loadBalancerClusterMonitor.getName() + "." + target,
                    loadBalancerServerMonitor);
        }
    }
    
    private LoadBalancer createLoadBalancer(final String configName) {
        mDomainConfig.createLoadBalancerConfig(
            configName+LB_SUFFIX, configName, false, null);
        Map<String,LoadBalancer> lbs = mDomainRoot.getLoadBalancerMap();
        LoadBalancer lb = lbs.get(configName+LB_SUFFIX);
        return lb;
    }
    
    /**
     * creates a map of the parameters
     */
    private Map<String,String> getParams(Map<String,String> options) {
        
        Map<String,String> params = new HashMap<String,String>();
        params.put(LBConfigKeys.HTTPS_ROUTING_KEY,options.get(HTTPS_ROUTING));
        params.put(LBConfigKeys.MONITORING_ENABLED_KEY,options.get(MONITOR));
        params.put(LBConfigKeys.RELOAD_POLL_INTERVAL_IN_SECONDS_KEY, options.get(RELOAD_INTERVAL));
        params.put(LBConfigKeys.RESPONSE_TIMEOUT_IN_SECONDS_KEY, options.get(RESPONSE_TIMEOUT));
        params.put(LBConfigKeys.ROUTE_COOKIE_ENABLED_KEY, options.get(ROUTE_COOKIE));
        return params;
    }
    
    /**
     * writes the String lbxml to the file given by filePath
     */
    private String writeToFile(final String lbxml, final String filePath)  {
        FileOutputStream fo = null;
        try{
            fo = new FileOutputStream(filePath);
            fo.write(lbxml.getBytes());
        }catch(IOException ioe){
            ioe.printStackTrace();
        }finally{
            try{
                fo.close();
            }catch(IOException e){}
        }
        
        return filePath; 
    }
    
    // PRIVATE VARIABLES
    DomainConfig mDomainConfig = null;
    
    DomainRoot mDomainRoot = null;
    
    static final String LB_CONFIG_SUFFIX = "_LB_CONFIG";
    
    static final String LB_SUFFIX = "-lb-temp";
    
    Logger mLogger = null;
    
    ResourceBundle resBundle = null;
    
    Formatter formatter = null;
    
    public static final String RESPONSE_TIMEOUT = "responsetimeout";
    public static final String HTTPS_ROUTING = "httpsrouting";
    public static final String RELOAD_INTERVAL = "reloadinterval";
    public static final String MONITOR = "monitor";
    public static final String ROUTE_COOKIE = "routecookie";
    public static final String HEALTH_CHECKER_URL = "healthcheckerurl";
    public static final String HEALTH_CHECKER_TIMEOUT = "healthcheckertimeout";
    public static final String HEALTH_CHECKER_INTERVAL = "healthcheckerinterval";
    public static final String TARGET = "target";
    public static final String CONFIG = "config";
    public static final String LB_POLICY = "lbpolicy";
    public static final String LB_POLICY_MODULE = "lbpolicymodule";
    
}



