/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999-2004 Bull S.A.
 * Contact: jonas-team@objectweb.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.objectweb.jonas.management.j2eemanagement;

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

import javax.management.JMException;
import javax.management.MBeanRegistration;
import javax.management.MBeanServer;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;

import org.objectweb.jonas.common.JProp;
import org.objectweb.jonas.common.Log;
import org.objectweb.jonas.jmx.J2eeObjectName;
import org.objectweb.jonas.jmx.JonasObjectName;
import org.objectweb.jonas.management.cluster.BaseCluster;
import org.objectweb.jonas.management.cluster.LogicalCluster;
import org.objectweb.jonas.management.monitoring.AuthenticationInformation;
import org.objectweb.jonas.management.monitoring.ClusterDaemonProxy;
import org.objectweb.jonas.management.monitoring.DomainMonitor;
import org.objectweb.jonas.management.monitoring.ServerProxy;
import org.objectweb.jonas.server.J2EEServer;
import org.objectweb.jonas_domain.api.DomainMapException;
import org.objectweb.jonas_domain.lib.wrapper.DomainManagerWrapper;
import org.objectweb.jonas_domain.xml.Cluster;
import org.objectweb.jonas_domain.xml.ClusterDaemon;
import org.objectweb.jonas_domain.xml.Domain;
import org.objectweb.jonas_domain.xml.Location;
import org.objectweb.jonas_domain.xml.Server;
import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;

/**
 * Implements a managed object which represents either a management domain or a cluster.
 * This J2EEManagedObject is accessed mainly by jonasAdmin.
 * The complete interface is described in the following xml file :
 * @See src/org/objectweb/jonas/server/mbeans-descriptors.xml
 * @author Adriana Danes
 * @author Vivek Lakshmanan
 * @author S. Ali Tokmen
 */
public class J2EEDomain extends J2EEManagedObject implements MBeanRegistration {

    /**
     * Management domain name
     */
    private String domainName = null;

    /**
     * J2EE Management logger
     */
    private static Logger logger = Log.getLogger("org.objectweb.jonas.management.j2eemanagement");

    /**
     * Ref to the J2EEServer of this server
     */
    private J2EEServer myJ2eeServer;

    /**
     * Ref to the local MBean server. Got it as a preRegister argument.
     */
    private MBeanServer mbeanServer;

    /**
     * For slave servers, must keep here the connection
     * because there is no DomainMonitor.
     */
    private ServerProxy localServerProxy;

    /**
     * The associated DomainMonitor, in case this is the master node.
     */
    private DomainMonitor domainMonitor = null;

    private static J2EEDomain unique;

    /**
     * MBean constructor
     * @param objectName object name of the managed object
     * @throws JMException ..
     */
    public J2EEDomain(String objectName) throws JMException {
        super(objectName, false, false, false);
        ObjectName myObjectName = ObjectName.getInstance(objectName);
        domainName = myObjectName.getKeyProperty("name");
        unique = this;
    }

    /**
     * Set this server as the master
     */
    public void setMaster() {
        try {
            ObjectName dmOn = JonasObjectName.domainMonitor();
            domainMonitor = new DomainMonitor(domainName);
            if (mbeanServer != null) {
                mbeanServer.registerMBean(domainMonitor, dmOn);
            }
        } catch (Exception e) {
            logger.log(BasicLevel.ERROR, "Cannot create DomainMonitor", e);
        }
    }

    /**
     * Singleton: Each server (master or slave) must have 1 unique J2EEDomain object.
     * @return the unique instance of the J2EEDomain
     */
    public static J2EEDomain getInstance() {
        return unique;
    }

    /**
     * @param serverName Name of the server
     * @return the connection to the MBeanServer of this server
     */
    public MBeanServerConnection getConnection(String serverName) {
        ServerProxy sp = getServerProxy(serverName);
        if (sp != null) {
            return sp.getConnection();
        } else {
            logger.log(BasicLevel.ERROR, "Unknown server: " + serverName);
            return null;
        }
    }

    /**
     * Return the MBeans OBJECT_NAMEs of the servers belonging to this domain
     * @return The list of object names corresponding to the servers associated with this domain
     */
    public String[] getServers() {
        logger.log(BasicLevel.DEBUG, "");
        String[] sb = null;
        if (domainMonitor != null) {
            Collection col = domainMonitor.getServerList();
            sb = new String[col.size()];
            int i = 0;
            for (Iterator it = col.iterator(); it.hasNext();) {
                ServerProxy proxy = (ServerProxy) it.next();
                sb[i++] = proxy.getJ2eeObjectName();
            }
        } else {
            // Slave: Only itself
            sb = new String[1];
            sb[0] = myJ2eeServer.getObjectName();
        }
        return sb;
    }

    /**
     * Return the servers' name belonging domain
     * @return The list of names corresponding to the servers within this domain
     */
    public String[] getServerNames() {
        logger.log(BasicLevel.DEBUG, "");
        String[] names = null;
        if (domainMonitor != null) {
            Collection col = domainMonitor.getServerList();
            names = new String[col.size()];
            int i = 0;
            for (Iterator it = col.iterator(); it.hasNext();) {
                ServerProxy proxy = (ServerProxy) it.next();
                names[i++] = proxy.getName();
            }
        } else {
            // Slave: Only itself
            names = new String[1];
            names[0] = localServerProxy.getName();
        }
        return names;
    }

    /**
     * @param clusterName name of a cluster in the domain
     * @return the list of server names corresponding to the servers within a cluster
     */
    public String[] getServerNames(String clusterName) {
        logger.log(BasicLevel.DEBUG, "");
        String[] names = null;
        BaseCluster cl = findCluster(clusterName);
        if (cl != null) {
            int nbServers = cl.getNbMembers();
            names = new String[nbServers];
            if (nbServers == 0) {
                return names;
            }
            Iterator sps = cl.getServerProxyList().iterator();
            int i = 0;
            while (sps.hasNext()) {
                ServerProxy sp = (ServerProxy) sps.next();
                names[i++] = sp.getServerName();
            }
        }
        return names;
    }

    /**
     * Return the MBeans OBJECT_NAMEs of the servers belonging to this domain
     * but only if they are running.
     * @return The list of object names corresponding to the servers associated with this domain
     */
    public String[] getStartedServers() {
        logger.log(BasicLevel.DEBUG, "");
        ArrayList sb = new ArrayList();
        if (domainMonitor != null) {
            Collection col = domainMonitor.getServerList();
            for (Iterator it = col.iterator(); it.hasNext();) {
                ServerProxy proxy = (ServerProxy) it.next();
                if (proxy.getServerState() == ServerProxy.RUNNING) {
                    sb.add(proxy.getJ2eeObjectName());
                }
            }
        } else {
            // Slave: Only itself
            sb.add(myJ2eeServer.getObjectName());
        }
        return (String []) sb.toArray(new String [0]);
    }

    public String[] getClusterDaemons() {
        logger.log(BasicLevel.DEBUG, "");
        String[] sb = null;
        if (domainMonitor != null) {
            Collection col = domainMonitor.getClusterDaemonList();
            sb = new String[col.size()];
            int i = 0;
            for (Iterator it = col.iterator(); it.hasNext();) {
                ClusterDaemonProxy cdp = (ClusterDaemonProxy) it.next();
                sb[i++] = cdp.getObjectName();
            }
        } else {
            logger.log(BasicLevel.ERROR, "Operation allowed on master only");
        }
        return sb;
    }

    /**
     * Return the MBeans OBJECT_NAMEs of the clusters in this domain
     * @return The list of object names corresponding to the clusters in this domain
     */
    public String[] getClusters() {
        logger.log(BasicLevel.DEBUG, "");
        String[] sb = null;
        if (domainMonitor != null) {
            Collection col = domainMonitor.getTotalClusterList();
            sb = new String[col.size()];
            int i = 0;
            for (Iterator it = col.iterator(); it.hasNext();) {
                BaseCluster cl = (BaseCluster) it.next();
                if (cl == null) {
                    logger.log(BasicLevel.ERROR, "null cl elt");
                } else {
                    sb[i++] = cl.getObjectName();
                }
            }
        } else {
            logger.log(BasicLevel.ERROR, "Operation allowed on master only");
        }
        return sb;
    }

    /**
     * Get the domainMonitor, or null if slave server.
     * @return the domainMonitor, for the master server.
     */
    public DomainMonitor getDomainMonitor() {
        return domainMonitor;
    }

    /**
     * Create a logical cluster (by jonasAdmin)
     * @param name the cluster's name
     * @return the OBJECT_NAME corresponding to the created MBean
     */
    public String createCluster(String name) {
        logger.log(BasicLevel.DEBUG, "");
        if (domainMonitor != null) {
            BaseCluster cl = domainMonitor.createLogicalCluster(name);
            return cl.getObjectName();
        } else {
            logger.log(BasicLevel.ERROR, "Operation allowed on master only");
            return null;
        }
    }

    /**
     * Add the local J2EEServer to the 'servers' list.
     * This method is called by the Server.start() method execution.
     * @param j2eeserver J2EEServer object corresponding to the currently managed
     * server (the local server which can be a master or a slave)
     */
    public void addLocalServer(J2EEServer j2eeserver) {
        logger.log(BasicLevel.DEBUG, "");
        myJ2eeServer = j2eeserver;
        String svname = j2eeserver.getServerName();
        if (domainMonitor != null) {
            // Master case: must check if not already created.
            localServerProxy = domainMonitor.addLocalServer(svname);
            // Read domain.xml after having added the local server
            domainMonitor.readDomainConfig();
        } else {
            // Slave case: create it in any case.
            // no cluster daemon (4th param)
            localServerProxy = new ServerProxy(domainMonitor, svname, null, null);
            if (logger.isLoggable(BasicLevel.DEBUG)) {
                logger.log(BasicLevel.DEBUG, "Adding server : "  + svname);
            }
            // give a name and register mbean
            ObjectName on;
            try {
                on = JonasObjectName.serverProxy(svname);
                localServerProxy.setObjectName(on.toString());
            } catch (MalformedObjectNameException e) {
                logger.log(BasicLevel.ERROR, "MalformedObjectName: "  + svname);
            }
        }
    }

    /**
     * Set the local server running and get its info
     * Cannot be done before because some MBeans must exist first.
     */
    public void setLocalServerRunning() {
        localServerProxy.getMonitoringInfo();
    }

    /**
     * Get a Cluster by its name
     * @param name The name of the cluster
     * @return the Cluster or null if unknown by the domain.
     */
    public BaseCluster findCluster(String name) {
        logger.log(BasicLevel.DEBUG, "");
        if (domainMonitor == null) {
            logger.log(BasicLevel.WARN, "discovery service not started");
            return null;
        }
        return domainMonitor.findCluster(name);
    }

    /**
     * Get a Server by its name
     * @param name The name of the server
     * @return the Server or null if unknown by the domain.
     */
    public J2EEServer findServer(String name) {
        logger.log(BasicLevel.DEBUG, "");
        if (domainMonitor == null) {
            logger.log(BasicLevel.WARN, "discovery service not started");
            return null;
        }
        return domainMonitor.findServer(name);
    }

    /**
     * Get a Server by its name
     * @param name The name of the server
     * @return the ServerProxy or null if unknown by the domain.
     */
    public ServerProxy findServerProxy(String name) {
        logger.log(BasicLevel.DEBUG, "");
        if (domainMonitor == null) {
            logger.log(BasicLevel.WARN, "discovery service not started");
            return null;
        }
        return domainMonitor.findServerProxy(name);
    }

    /**
     * @param mbeanServer The MBean server in which the MBean will be registered.
     * @param name The object name of the MBean
     * @return The object name of the MBean
     */
    public ObjectName preRegister(MBeanServer mbeanServer, ObjectName name) throws Exception {
        logger.log(BasicLevel.DEBUG, "");
        if (name == null) {
            return J2eeObjectName.J2EEDomain(domainName);
        }
        this.mbeanServer = mbeanServer;
        String myName = getObjectName().toString();
        String argName = name.toString();
        if (myName.equals(argName)) {
            return name;
        } else {
            if (logger.isLoggable(BasicLevel.WARN)) {
                logger.log(BasicLevel.WARN, "Trying to register J2EEDomain MBean with the following name: "
                        + argName + " when the expected name is: " + myName);
            }
            return ObjectName.getInstance(domainName);
        }
    }

    /**
     * Add J2EEDomain MBean (myself) as listener to registration/unregistration notifications of
     * JOnAS management MBeans.
     * @param registrationDone Indicates whether or not the MBean has been successfully registered
     */
    public void postRegister(Boolean registrationDone) {
        logger.log(BasicLevel.DEBUG, "");
    }

    /**
     * @see javax.management.MBeanRegistration#preDeregister()
     */
    public void preDeregister() throws Exception {
        logger.log(BasicLevel.DEBUG, "");
    }

    /**
     * @see javax.management.MBeanRegistration#postDeregister()
     */
    public void postDeregister() {
        logger.log(BasicLevel.DEBUG, "");
    }



    /**
     * Start a Remote JOnAS Server
     * MBean implementation for jonasAdmin
     * @param serverName Name of the jonas server
     */
    public void startServer(String serverName) {
        if (domainMonitor == null) {
            logger.log(BasicLevel.ERROR, "Operation only possible on master");
            return;
        }
        ServerProxy proxy = domainMonitor.findServerProxy(serverName);
        proxy.startit();
    }

    /**
     * Stop a Remote JOnAS Server
     * MBean implementation for jonasAdmin
     * @param serverName Name of the jonas server
     */
    public void stopServer(String serverName) {
        if (domainMonitor == null) {
            logger.log(BasicLevel.ERROR, "Operation only possible on master");
            return;
        }
        ServerProxy proxy = domainMonitor.findServerProxy(serverName);
        proxy.stopit();
    }

    /**
     * @return Returns the description.
     */
    public String getDescription() {
        if (domainMonitor != null) {
            return domainMonitor.getDescription();
        } else {
            return "Unknown";
        }
    }

    /**
     * @return Returns the master.
     */
    public boolean isMaster() {
        return (domainMonitor != null);
    }

    /**
     * Return the domain name
     * @return domain name or cluster name
     */
    public String getMyName() {
        return domainName;
    }
    /**
     * Return connector server urls for a given JOnAS server
     * @param serverName the given JOnAS server name
     * @return connector server urls for a given JOnAS server
     */
    public String[] getConnectorServerURLs(String serverName) {
        ArrayList urlList = null;
        ServerProxy sp = getServerProxy(serverName);
        if (sp != null) {
            urlList = sp.getUrls();
        }
        if (urlList == null) {
            return null;
        }
        String[] ret = new String[urlList.size()];
        Iterator it = urlList.iterator();
        for (int i = 0; i < urlList.size(); i++) {
            ret[i] = (String) it.next();
        }
        return ret;
    }

    /**
     * Deploy a JAR file on a multiple management target composed of servers and/or clusters
     * @param target list of server and/or cluster names
     * @param fileName file to be deployed
     */
    public void deployJar(String[] target, String fileName) {
        for (int i = 0; i < target.length; i++) {
            deployOnTarget(target[i], fileName);
        }
    }

    /**
     * Deploy a EAR file on a multiple management target composed of servers and/or clusters
     * @param target list of server and/or cluster names
     * @param fileName file to be deployed
     */
    public void deployEar(String[] target, String fileName) {
        for (int i = 0; i < target.length; i++) {
            deployOnTarget(target[i], fileName);
        }
    }

    /**
     * Deploy a WAR file on a multiple management target composed of servers and/or clusters
     * @param target list of server and/or cluster names
     * @param fileName file to be deployed
     */
    public void deployWar(String[] target, String fileName) {
        for (int i = 0; i < target.length; i++) {
            deployOnTarget(target[i], fileName);
        }
    }

    /**
     * Deploy a RAR file on a multiple management target composed of servers and/or clusters
     * @param target list of server and/or cluster names
     * @param fileName file to be deployed
     */
    public void deployRar(String[] target, String fileName) {
        for (int i = 0; i < target.length; i++) {
            deployOnTarget(target[i], fileName);
        }
    }

    /**
     * Upload and deploy a JAR file on a multiple management target composed of servers and/or clusters
     * @param target list of server and/or cluster names
     * @param fileName file to be deployed
     * @param replaceExisting true if the uploaded file can replace a file with the same name in the jars directory
     */
    public void uploadDeployJar(String[] target, String fileName, boolean replaceExisting) {
        for (int i = 0; i < target.length; i++) {
            uploadDeployOnTarget(target[i], fileName, replaceExisting);
        }
    }

    /**
     * Upload and deploy a WAR file on a multiple management target composed of servers and/or clusters
     * @param target list of server and/or cluster names
     * @param fileName file to be deployed
     * @param replaceExisting true if the uploaded file can replace a file with the same name in the wars directory
     */
    public void uploadDeployWar(String[] target, String fileName, boolean replaceExisting) {
        for (int i = 0; i < target.length; i++) {
            uploadDeployOnTarget(target[i], fileName, replaceExisting);
        }
    }

    /**
     * Upload and deploy an EAR file on a multiple management target composed of servers and/or clusters
     * @param target list of server and/or cluster names
     * @param fileName file to be deployed
     * @param replaceExisting true if the uploaded file can replace a file with the same name in the apps directory
     */
    public void uploadDeployEar(String[] target, String fileName, boolean replaceExisting) {
        for (int i = 0; i < target.length; i++) {
            uploadDeployOnTarget(target[i], fileName, replaceExisting);
        }
    }

    /**
     * Upload and deploy a RAR file on a multiple management target composed of servers and/or clusters
     * @param target list of server and/or cluster names
     * @param fileName file to be deployed
     * @param replaceExisting true if the uploaded file can replace a file with the same name in the rars directory
     */
    public void uploadDeployRar(String[] target, String fileName, boolean replaceExisting) {
        for (int i = 0; i < target.length; i++) {
            uploadDeployOnTarget(target[i], fileName, replaceExisting);
        }
    }

    /**
     * Undeploy a JAR file of a multiple management target composed of servers and/or clusters
     * @param target list of server and/or cluster names
     * @param fileName file to be undeployed
     */
    public void unDeployJar(String[] target, String fileName) {
        for (int i = 0; i < target.length; i++) {
            unDeployOnTarget(target[i], fileName);
        }
    }

    /**
     * Undeploy a WAR file of a multiple management target composed of servers and/or clusters
     * @param target list of server and/or cluster names
     * @param fileName file to be undeployed
     */
    public void unDeployWar(String[] target, String fileName) {
        for (int i = 0; i < target.length; i++) {
            unDeployOnTarget(target[i], fileName);
        }
    }

    /**
     * Undeploy a EAR file of a multiple management target composed of servers and/or clusters
     * @param target list of server and/or cluster names
     * @param fileName file to be undeployed
     */
    public void unDeployEar(String[] target, String fileName) {
        for (int i = 0; i < target.length; i++) {
            unDeployOnTarget(target[i], fileName);
        }
    }

    /**
     * Undeploy a RAR file of a multiple management target composed of servers and/or clusters
     * @param target list of server and/or cluster names
     * @param fileName file to be undeployed
     */
    public void unDeployRar(String[] target, String fileName) {
        for (int i = 0; i < target.length; i++) {
            unDeployOnTarget(target[i], fileName);
        }
    }

    /**
     * Calls {@link J2EEDomain#addServer(String, String, String, String[], String, String)}(serverName, clusterName, clusterDaemonName, urls, null, null)
     * @param serverName name of the server to add
     * @param urls JMX remote connector urls
     * @param clusterName name of the cluster in which the server is to be
     * attached, if any. Null otherwise.
     * @param clusterDaemonName name of the cluster daemon if any. Null otherwise.
     * @throws JMException could not add the server to the domain
     * @deprecated {@link J2EEDomain#addServer(String, String, String, String[], String, String)}
     */
    public void addServer(String serverName, String clusterName, String clusterDaemonName, String[] urls) throws JMException {
        addServer(serverName, clusterName, clusterDaemonName, urls, null, null);
    }

    /**
     *
     * @param serverName name of the server to add
     * @param urls JMX remote connector urls
     * @param clusterName name of the cluster in which the server is to be
     * attached, if any. Null otherwise.
     * @param clusterDaemonName name of the cluster daemon if any. Null otherwise.
     * @param username user name to use when connecting if any. Null otherwise.
     * @param password password to use when connecting if any. Null otherwise.
     * @throws JMException could not add the server to the domain
     */
    public void addServer(String serverName, String clusterName, String clusterDaemonName, String[] urls, String username, String password) throws JMException {
        BaseCluster cl = findCluster(domainName);
        cl.addServer(serverName, urls, clusterDaemonName, username, password);
        if (clusterName != null) {
            cl = findCluster(clusterName);
            cl.addServer(serverName, urls, clusterDaemonName, username, password);
        }
    }

    /**
     * Deploy a file on a management target which may be a server or a cluster
     * @param target server or cluster name
     * @param fileName file to be deployed
     */
    private void deployOnTarget(String target, String fileName) {
        ServerProxy sp = findServerProxy(target);
        if (sp != null) {
            // The target is a known server in the domain
            sp.deployModule(fileName);
        } else {
            // maybe the target is a cluster
            BaseCluster cl = findCluster(target);
            if (cl != null) {
                cl.deployModule(fileName);
            } else {
                logger.log(BasicLevel.ERROR, "Can't get deploy on unknown target " + target);
            }
        }
    }

    /**
     * Deploy a file on a management target which may be a server or a cluster
     * @param target server or cluster name
     * @param fileName file to be deployed
     * @param replaceExisting true if the uploaded file can replace a file with the same name in the jars directory
     */
    private void uploadDeployOnTarget(String target, String fileName, boolean replaceExisting) {
        ServerProxy sp = findServerProxy(target);
        if (sp != null) {
            // The target is a known server in the domain
            sp.uploadDeployModule(fileName, replaceExisting);
        } else {
            // maybe the target is a cluster
            BaseCluster cl = findCluster(target);
            if (cl != null) {
                cl.uploadDeployModule(fileName, replaceExisting);
            } else {
                logger.log(BasicLevel.ERROR, "Can't get deploy on unknown target " + target);
            }
        }
    }

    /**
     * Undeploy a file of a management target which may be a server or a cluster
     * @param target server or cluster name
     * @param fileName file to be deployed
     */
    private void unDeployOnTarget(String target, String fileName) {
        ServerProxy sp = findServerProxy(target);
        if (sp != null) {
            // The target is a known server in the domain
            sp.undeployModule(fileName);
        } else {
            // maybe the target is a cluster
            BaseCluster cl = findCluster(target);
            if (cl != null) {
                cl.undeployModule(fileName);
            } else {
                logger.log(BasicLevel.ERROR, "Can't get undeploy on unknown target " + target);
            }
        }
    }

    /**
     * Return the ServerProxy of a given server in the domain.
     * @param serverName the server who's ServerProxy is asked for
     * @return ServerProxy of the given server.
     */
    private ServerProxy getServerProxy(String serverName) {
        if (serverName.equals(myJ2eeServer.getServerName())) {
            return localServerProxy;
        } else {
            // Remote server: Only if we are in the master
            if (domainMonitor == null) {
                logger.log(BasicLevel.ERROR, "Can't get ServerProxy of a remote server as this is not a master");
                return null;
            } else {
                return domainMonitor.findServerProxy(serverName);
            }
        }
    }

    /**
     * Update and save configuration.
     */
    public void saveConfig() throws DomainMapException {
        AuthenticationInformation authInfo;
        String myServerName = System.getProperty(JProp.JONAS_NAME);
        if (myServerName == null) {
            myServerName = JProp.JONAS_DEF_NAME;
        }

        // Domain information
        Domain domain = new Domain();
        domain.setName(domainMonitor.getDomainName());
        domain.setDescription(domainMonitor.getDescription());
        authInfo = domainMonitor.getAuthenticationInformation(null);
        if (authInfo != null) {
            domain.setUsername(authInfo.getUsername());
            domain.setPassword(authInfo.getPassword());
        }

        // Make sure servers that are in the cluster list
        // don't appear in the servers list
        Collection servers = new ArrayList(domainMonitor.getServerList());

        // Clusters
        for (Iterator iter = domainMonitor.getLogicalClusterList().iterator(); iter.hasNext();) {
            BaseCluster proxy = (BaseCluster) iter.next();
            if (!(proxy instanceof LogicalCluster)) {
                // Ignore all automatically created clusters
                continue;
            }
            if (proxy.getName().equals(domainMonitor.getDomainName())) {
                // Ignore current domain
                continue;
            }
            Cluster cluster = new Cluster();
            cluster.setName(proxy.getName());
            for (Iterator members = proxy.getServerProxyList().iterator(); members.hasNext();) {
                Server server = new Server();
                Location location = new Location();
                ServerProxy member = (ServerProxy) members.next();

                server.setName(member.getName());
                Collection c = member.getUrls();
                if (c != null) {
                    for(Iterator i = c.iterator(); i.hasNext();) {
                        location.addUrl((String) i.next());
                    }
                }
                server.setLocation(location);
                server.setClusterDaemon(member.getClusterDaemonName());
                authInfo = domainMonitor.getAuthenticationInformation(member.getName());
                if (authInfo != null) {
                    server.setUsername(authInfo.getUsername());
                    server.setPassword(authInfo.getPassword());
                }
                cluster.addServer(server);
                servers.remove(member);
            }
            domain.addCluster(cluster);
        }

        // Servers
        for (Iterator iter = servers.iterator(); iter.hasNext();) {
            ServerProxy proxy = (ServerProxy) iter.next();
            if (proxy.getName().equals(myServerName)) {
                // Ignore self
                continue;
            }
            Server server = new Server();
            Location location = new Location();
            authInfo = domainMonitor.getAuthenticationInformation(proxy.getName());
            if (authInfo == domainMonitor.getAuthenticationInformation(null)) {
                authInfo = null;
            }

            server.setName(proxy.getName());
            Collection c = proxy.getUrls();
            if (c != null) {
                for(Iterator i = c.iterator(); i.hasNext();) {
                    location.addUrl((String) i.next());
                }
            }
            server.setLocation(location);
            server.setClusterDaemon(proxy.getClusterDaemonName());
            if (authInfo != null) {
                server.setUsername(authInfo.getUsername());
                server.setPassword(authInfo.getPassword());
            }
            domain.addServer(server);
        }

        // Cluster daemons
        for (Iterator iter = domainMonitor.getClusterDaemonList().iterator(); iter.hasNext();) {
            ClusterDaemonProxy proxy = (ClusterDaemonProxy) iter.next();
            if (proxy.getName().equals(myServerName)) {
                // Ignore self
                continue;
            }
            ClusterDaemon daemon = new ClusterDaemon();
            Location location = new Location();
            authInfo = domainMonitor.getAuthenticationInformation(proxy.getName());
            if (authInfo == domainMonitor.getAuthenticationInformation(null)) {
                authInfo = null;
            }

            daemon.setName(proxy.getName());
            Collection c = proxy.getUrls();
            if (c != null) {
                for(Iterator i = c.iterator(); i.hasNext();) {
                    location.addUrl((String) i.next());
                }
            }
            daemon.setLocation(location);
            if (authInfo != null) {
                daemon.setUsername(authInfo.getUsername());
                daemon.setPassword(authInfo.getPassword());
            }
            domain.addClusterDaemon(daemon);
        }

        DomainManagerWrapper.saveDomain(domain, null);
    }
}
