/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999-2005 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.adm;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Vector;

import javax.management.JMException;
import javax.naming.InitialContext;
import javax.naming.NameClassPair;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;

import org.objectweb.common.Cmd;
import org.objectweb.jonas.common.JProp;
import org.objectweb.jonas.common.LogManagement;
import org.objectweb.jonas.container.EJBServiceImpl;
import org.objectweb.jonas.dbm.ConnectionManager;
import org.objectweb.jonas.dbm.DataBaseServiceImpl;
import org.objectweb.jonas.dbm.Pool;
import org.objectweb.jonas.ear.EarServiceException;
import org.objectweb.jonas.ear.EarServiceImplMBean;
import org.objectweb.jonas.jtm.TransactionServiceImpl;
import org.objectweb.jonas.mail.MailServiceImplMBean;
import org.objectweb.jonas.management.cluster.BaseCluster;
import org.objectweb.jonas.management.j2eemanagement.J2EEDomain;
import org.objectweb.jonas.management.monitoring.ServerProxy;
import org.objectweb.jonas.naming.NamingManager;
import org.objectweb.jonas.resource.ResourceServiceException;
import org.objectweb.jonas.resource.ResourceServiceImplMBean;
import org.objectweb.jonas.server.Server;
import org.objectweb.jonas.service.ServiceException;
import org.objectweb.jonas.service.ServiceManager;
import org.objectweb.jonas.web.AbsJWebContainerServiceImpl;
import org.objectweb.jonas.web.AbsJWebContainerServiceImplMBean;
import org.objectweb.jonas.web.JWebContainerServiceException;
import org.objectweb.jonas_ear.deployment.api.EarDeploymentDesc;
import org.objectweb.jonas_ear.deployment.api.EarDeploymentDescException;
import org.objectweb.jonas_ear.deployment.lib.wrapper.EarManagerWrapper;
import org.objectweb.jonas_ejb.container.Container;
import org.objectweb.jonas_ejb.container.JFactory;
import org.objectweb.jonas_ejb.genic.GenIC;
import org.objectweb.jonas_web.deployment.api.WebContainerDeploymentDesc;
import org.objectweb.jonas_web.deployment.lib.wrapper.WebManagerWrapper;

/**
 * This class implements a remote interface used for administering the server.
 * @author Philippe Coq
 * @author Philippe Durieux
 * @author Marc Dutoo, JOnAS 2.4
 * @author Murad Meghani (Murad.Meghani@compuware.com) killServer and stopServer
 * @author Florent Benoit & Ludovic Bert : Methods for wars and ear files JOnAS
 *         2.5 2002.06
 * @author Dean Jennings : add deployFile
 */
public class Adm extends PortableRemoteObject implements AdmInterface {

    public static final String ADMNAME_SUFFIX = "_Adm";

    private ServiceManager sm = null;

    private EJBServiceImpl ejbserv = null;

    private DataBaseServiceImpl dbmserv = null;

    private TransactionServiceImpl tm = null;

    private JProp jonasProperties = null;

    private InitialContext ictx = null;

    private boolean isEJBContainer;

    // Server State
    public static final int NOT_READY = 0;

    public static final int READY = 1;

    public static final int STOPPED = 2;

    private int serverState = NOT_READY;

    /**
     * Sleep before exit of JVM
     */
    public static final int SLEEP_DELAY = 2000;

    /**
     * Reference to the web container service
     */
    private AbsJWebContainerServiceImplMBean webContainerService = null;

    /**
     * Reference to the ear service
     */
    private EarServiceImplMBean earService = null;

    /**
     * Reference to the resource service
     */
    private ResourceServiceImplMBean resourceService = null;

    /**
     * Reference to the mail service
     */
    private MailServiceImplMBean mailService = null;

    /**
     * Reference on the J2EEDomain
     * Used to access other servers and clusters in the domain.
     */
    J2EEDomain mydomain = null;

    /**
     * Adm constructor
     * @throws RemoteException if problem
     */
    public Adm(JProp jp) throws RemoteException, NamingException, ServiceException, Exception {

        jonasProperties = jp;

        // Get Service references
        sm = ServiceManager.getInstance();

        // Get an Initial Context andorg.objectweb.jonas_ejb.container;
        // registers the JOnAS Server Name to JNDI
        String name = null;
        name = jonasProperties.getValue("jonas.name", "jonas") + ADMNAME_SUFFIX;
        ictx = NamingManager.getInstance().getInitialContext();
        ictx.rebind(name, this);

        // Keep a reference on the J2EEDomain
        mydomain = Server.getInstance().getJ2EEDomain();
    }

    private TransactionServiceImpl getTM() {
        if (tm == null) {
            try {
                tm = (TransactionServiceImpl) sm.getTransactionService();
            } catch (ServiceException e) {
            }
        }
        return tm;
    }

    private DataBaseServiceImpl getDBM() {
        if (dbmserv == null) {
            try {
                dbmserv = (DataBaseServiceImpl) sm.getDataBaseService();
            }catch (ServiceException e) {
            }
        }
        return dbmserv;
    }

    /**
     * Get the web container service We can use methods provided by the
     * interface for MBean
     */
    private AbsJWebContainerServiceImplMBean getWebContainerService() {
        if (webContainerService == null) {
            try {
                webContainerService = (AbsJWebContainerServiceImplMBean) sm.getWebContainerService();
            } catch (ServiceException e) {
                // not started
            }
        }
        return webContainerService;
    }

    /**
     * Get the ear service We can use methods provided by the interface for
     * MBean
     */
    private EarServiceImplMBean getEarService() {
        if (earService == null) {
            try {
                earService = (EarServiceImplMBean) sm.getEarService();
            } catch (Exception e) {
                // not started
            }
        }
        return earService;
    }

    /**
     * Get the resource service We can use methods provided by the interface for
     * MBean
     */
    private ResourceServiceImplMBean getResourceService() {
        if (resourceService == null) {
            try {
                resourceService = (ResourceServiceImplMBean) sm.getResourceService();
            } catch (Exception e) {
                // not started
            }
        }
        return resourceService;
    }

    // ------------------------------------------------------------------
    // AdmInterface implementation
    // ------------------------------------------------------------------

    /**
     * get Topics. Assumes that all Loggers are TopicalLoggers.
     */
    public String[] getTopics() throws RemoteException {
        return LogManagement.getInstance().getTopics();
    }

    /**
     * get Topic Level
     */
    public String getTopicLevel(String topic) throws RemoteException {
        return LogManagement.getInstance().getTopicLevel(topic);
    }

    /**
     * set Topic Level
     */
    public void setTopicLevel(String topic, String l) throws RemoteException {
        LogManagement.getInstance().setTopicLevel(topic, l);
    }

    /**
     * Create a container and load beans in it
     * @param fileName name of the ejb-jar or xml file
     */
    public void addBeans(String fileName) throws RemoteException {
        if (!isEJBContainer) {
            return;
        }
        try {
            ejbserv.createContainer(fileName);
        } catch (Exception e) {
            throw new RemoteException("Cannot add beans", e);
        }
    }

    /**
     * Deploy a given ear file with the help of the ear service.
     * @param fileName the name of the ear file.
     * @throws RemoteException if rmi call failed.
     * @throws EarServiceException if the deployment failed.
     */
    public void addEar(String fileName) throws RemoteException, EarServiceException {
        if (getEarService() != null) {
            try {
                getEarService().deployEarMBean(fileName);
            } catch (EarServiceException ear) {
                throw ear;
            } catch (Exception e) {
                throw new RemoteException("Unable to deploy the EAR '" + fileName + "'.", e);
            }
        } else {
            throw new EarServiceException("Can't add the Ear file " + fileName + ". The ear service is not started");
        }
    }

    /**
     * Test if the specified filename is already deployed or not
     * @param fileName the name of the ear file.
     * @return true if the ear is deployed, else false.
     * @throws RemoteException if rmi call failed.
     */
    public boolean isEarLoaded(String fileName) throws RemoteException, EarServiceException {
        boolean isLoaded = false;
        if (getEarService() != null) {
            isLoaded = getEarService().isEarLoaded(fileName);
        } else {
            throw new EarServiceException("Can't test if the ear file " + fileName
                    + " is deployed or not. The ear service is not started");
        }
        return isLoaded;
    }

    /**
     * Deploy a given rar file with the help of the resource service.
     * @param fileName the name of the rar file.
     * @throws RemoteException if rmi call failed.
     * @throws ResourceServiceException if the deployment failed.
     */
    public void addRar(String fileName) throws RemoteException, ResourceServiceException {
        if (getResourceService() != null) {
            getResourceService().deployRarMBean(fileName);
        } else {
            throw new ResourceServiceException("Can't add the Rar file " + fileName
                    + ". The resource service is not started");
        }
    }

    /**
     * Test if the specified filename is already deployed or not
     * @param fileName the name of the rar file.
     * @return true if the rar is deployed, else false.
     * @throws RemoteException if rmi call failed.
     * @throws ResourceServiceException if unable to get resource service
     */
    public boolean isRarLoaded(String fileName) throws RemoteException, ResourceServiceException {
        boolean isLoaded = false;
        if (getResourceService() != null) {
            isLoaded = getResourceService().isRarLoaded(fileName);
        } else {
            throw new ResourceServiceException("Can't test if the rar file " + fileName
                    + " is deployed or not. The rar service is not started");
        }
        return isLoaded;
    }

    /**
     * Deploy a given war file with the help of the web container service.
     * @param fileName the name of the war file.
     * @throws RemoteException if rmi call failed.
     * @throws JWebContainerServiceException if the deployment failed.
     */
    public void addWar(String fileName) throws RemoteException, JWebContainerServiceException {
        if (getWebContainerService() != null) {
            getWebContainerService().registerWarMBean(fileName);
        } else {
            throw new JWebContainerServiceException("Can't add the war file " + fileName
                    + ". The web container service is not started");
        }
    }

    /**
     * Test if the specified filename is already deployed or not
     * @param fileName the name of the war file.
     * @return true if the war is deployed, else false.
     * @throws RemoteException if rmi call failed.
     */
    public boolean isWarLoaded(String fileName) throws RemoteException, JWebContainerServiceException {
        boolean isLoaded = false;
        if (getWebContainerService() != null) {
            isLoaded = getWebContainerService().isWarLoaded(fileName);
        } else {
            throw new JWebContainerServiceException("Can't test if the war file " + fileName
                    + " is deployed or not. The war service is not started");
        }
        return isLoaded;
    }

    /**
     * UnDeploy a given ear file with the help of the ear service.
     * @param fileName the name of the ear file.
     * @throws RemoteException if rmi call failed.
     * @throws EarServiceException if the undeployment failed.
     */
    public void removeEar(String fileName) throws RemoteException, EarServiceException {
        if (getEarService() != null) {
            try {
                getEarService().unDeployEarMBean(fileName);
                // needed here to run also the distributed garbage collector
                runGC();
            } catch (EarServiceException ear) {
                throw ear;
            } catch (Exception e) {
                throw new RemoteException("Cannot remove ear", e);
            }
        } else {
            throw new EarServiceException("Can't remove of the Ear file " + fileName
                    + ". The ear service is not started");
        }
    }

    /**
     * UnDeploy a given rar file with the help of the resource service.
     * @param fileName the name of the rar file.
     * @throws RemoteException if rmi call failed.
     * @throws ResourceServiceException if the undeployment failed.
     */
    public void removeRar(String fileName) throws RemoteException, ResourceServiceException {
        if (getResourceService() != null) {
            getResourceService().unDeployRarMBean(fileName);
            // needed here to run also the distributed garbage collector
            runGC();
        } else {
            throw new ResourceServiceException("Can't remove of the Rar file " + fileName
                    + ". The resource service is not started");
        }
    }

    /**
     * UnDeploy a given war file with the help of the web container service.
     * @param fileName the name of the war file.
     * @throws RemoteException if rmi call failed.
     * @throws JWebContainerServiceException if the undeployment failed.
     */
    public void removeWar(String fileName) throws RemoteException, JWebContainerServiceException {
        if (getWebContainerService() != null) {
            getWebContainerService().unRegisterWarMBean(fileName);
            // needed here to run also the distributed garbage collector
            runGC();
        } else {
            throw new JWebContainerServiceException("Can't remove of the war file " + fileName
                    + ". The web container service is not started");
        }
    }

    /**
     * Remove the container identified by fileName and remove all beans in it
     * @param fileName name of the ejb-jar or xml file
     */
    public void removeBeans(String fileName) throws RemoteException {
        if (!isEJBContainer) {
            return;
        }
        try {
            ejbserv.removeContainer(fileName);
        } catch (Exception e) {
            throw new RemoteException("Cannot remove bean", e);
        }
        // needed here to run also the distributed garbage collector
        runGC();
    }

    /**
     * returns true if beans are already loaded in server.
     * @param fileName name of the ejb-jar or xml file
     */
    public boolean isLoaded(String fileName) throws RemoteException {
        return (ejbserv.getContainer(fileName) != null);
    }

    public String dumpCustom() throws RemoteException {
        String ret = "";
        if (!isEJBContainer) {
            return ret;
        }
        // JVM
        ret += Server.jvmInfos();

        // Transaction Service
        if (getTM() != null) {
            ret += "TM timeout=" + getTM().getTimeout();
            ret += "\n";
        }

        // datasources
        if (getDBM() != null) {
            Collection dslist = getDBM().getDSList();
            for (Iterator i = dslist.iterator(); i.hasNext(); ) {
                ConnectionManager cm = (ConnectionManager) i.next();
                ret += cm.getDSName() + ":lockPolicy=" + cm.getTransactionIsolation();
                Pool pool = cm.getPool();
                ret += ":minPoolSize=" + pool.getPoolMin();
                ret += ":maxPoolSize=" + pool.getPoolMax();
                ret += ":maxOpenTime=" + pool.getMaxOpenTime();
                ret += ":maxWaitTime=" + pool.getMaxWaitTime();
                ret += ":maxWaiters=" + pool.getMaxWaiters();
                ret += ":pstmtMax=" + cm.getPstmtMax();
                ret += "\n";
            }
        }

        // EJB Containers
        Container[] lcont = (Container[]) ejbserv.listContainers();
        for (int j = 0; j < lcont.length; j++) {
            Container cont = lcont[j];
            String contname = cont.getName();
            String[] lbn = cont.listBeanNames();
            for (int k = 0; k < lbn.length; k++) {
                JFactory bf = (JFactory) cont.getBeanFactory(lbn[k]);
                ret += contname + ":=" + lbn[k];
                ret += ":minPoolSize=" + bf.getMinPoolSize();
                ret += ":maxCacheSize=" + bf.getMaxCacheSize();
                ret += "\n";
            }
        }
        return ret;
    }

    /**
     * List beans of all JOnAS containers
     */
    public String[] listBeans() throws RemoteException {

        if (!isEJBContainer) {
            return new String[0];
        }

        Container[] lcont = ejbserv.listContainers();
        Vector lbeans = new Vector();
        for (int j = 0; j < lcont.length; j++) {
            String[] lbn = lcont[j].listBeanNames();
            String contname = lcont[j].getName();
            for (int k = 0; k < lbn.length; k++) {
                String s = new String(contname + ": " + lbn[k]);
                lbeans.addElement(s);
            }
        }
        String[] ret = new String[lbeans.size()];
        lbeans.copyInto(ret);

        return ret;
    }

    /**
     * List JNDI context
     */
    public Vector listContext() throws RemoteException {
        String name = null;
        Vector ret = new Vector();
        try {
            NamingEnumeration ne;
            ne = NamingManager.getInstance().getInitialContext().list("");
            for (Enumeration e = ne; e.hasMoreElements();) {
                name = ((NameClassPair) e.nextElement()).getName();
                ret.addElement(name);
            }
        } catch (Exception ex) {
            throw new RemoteException("Cannot list JNDI context", ex);
        }
        return ret;
    }

    /**
     * List Environment (configuration properties provided by the configuration
     * file).
     */
    public Properties listEnv() {
        return jonasProperties.getConfigFileEnv();
    }

    /**
     * Stop the Server without stopping the JVM
     */
    public void stopServer() throws RemoteException {
        try {
            serverState = STOPPED;
            // keep registry alive a little while (for jonas admin) in case of
            // error at launch
            Thread.sleep(5000);
            sm.stopServices();
        } catch (ServiceException e) {
            throw new RemoteException("Cannot stop services: ", e);
        } catch (InterruptedException e) {
        }
    }

    /**
     * Stop the Server and stop the JVM
     */
    public void killServer() throws RemoteException {
        // Stops the JOnAS's services
        stopServer();

        // Unregisters the JOnAS Server Name in the JNDI if the registry is still up (should
        // be the case whether it is shared.
        String name = null;
        try {
        name = jonasProperties.getValue("jonas.name", "jonas") + ADMNAME_SUFFIX;
            ictx = NamingManager.getInstance().getInitialContext();
            ictx.unbind(name);
        } catch (Exception e) {
            //e.printStackTrace();
        }

        // Delay the exit of the JVM so the link client/server is not broken.
        // client call will return before the end of the JVM on server side.
        new Thread(new Runnable() {

            public void run() {
                try {
                    // Wait before exit
                    Thread.sleep(SLEEP_DELAY);
                } catch (InterruptedException ie) {
                    ie.printStackTrace();
                    throw new IllegalStateException("Cannot wait: " + ie.getMessage());
                }
                System.exit(0);
            }
        }).start();

    }

    /**
     * To test if the server is ready
     * @return int 0=not ready, 1=ready, 2=stopped
     */
    public int getServerState() throws RemoteException {
        return serverState;
    }

    /**
     * To test if the server is an EJB container
     */
    public boolean isEJBContainer() throws RemoteException {
        return isEJBContainer;
    }

    /**
     * set the default value for transaction timeout
     */
    public void setTransactionTimeout(int timeout) throws RemoteException {
        getTM().setTimeout(timeout);
    }

    /**
     * run the garbage collector
     */
    public void runGC() throws RemoteException {
        Runtime.getRuntime().gc();
    }

    /**
     * sync all entity instances outside transactions
     * @param passivate passivate instances after synchronization.
     */
    public void syncAllEntities(boolean passivate) throws RemoteException {
        if (!isEJBContainer) {
            return;
        }
        ejbserv.syncAllEntities(passivate);
    }

    // ------------------------------------------------------------------
    // other public methods
    // ------------------------------------------------------------------

    /**
     * server is ready
     */
    public void serverReady(boolean isEJB) {
        serverState = READY;
        isEJBContainer = isEJB;
        if (isEJBContainer) {
            ejbserv = (EJBServiceImpl) sm.getEjbService();
        }
    }

    /**
     * Deploy file (GenIC), needed for Ishmael to work. The file is in the
     * directory specified by the property "jonas.service.deployment.directory"
     * @param type type of the file (EJB, WAR, EAR, RAR, CAR)
     * @param bfile
     * @param filename basename of the file to be deployed
     * @throws RemoteException
     * @throws EarServiceException
     * @throws ResourceServiceException
     * @throws JWebContainerServiceException
     * @author Dean Jennings. <da_jennings@junta.com.au>
     */
    public String deployFile(int type, byte[] bfile, String filename) throws RemoteException, EarServiceException,
            JWebContainerServiceException {
        try {
            String directory = "";

            if (type == TYPE_EJB) {
                directory = ejbserv.getEjbjarsDirectory();
            } else if (type == TYPE_EAR) {
                directory = getEarService().getAppsDirectory();
            } else if (type == TYPE_WAR) {
                directory = getWebContainerService().getWebappsDirectory();
            } else if (type == TYPE_RAR) {
                directory = getResourceService().getRarsDirectory();
            } else if (type == TYPE_CAR) {
                throw new UnsupportedOperationException("Not Supported yet");
            }

            File file = new File(directory + filename);

            FileOutputStream out = new FileOutputStream(file);
            out.write(bfile);
            out.close();
            if (type == TYPE_EJB) {
                String args[] = new String[1];
                args[0] = directory + filename;
                GenIC.main(args);
            } else if (type == TYPE_EAR) {
                unpackAndCompileEar(file);
            }

            return file.getAbsolutePath();

        } catch (Exception e) {
            e.printStackTrace();
            throw new RemoteException(e.toString(), e);
        }
    }

    private void unpackAndCompileEar(File file) throws EarServiceException, IOException, EarDeploymentDescException {
        URL earUrl[] = new URL[1];

        try {
            earUrl[0] = file.toURL();
        } catch (MalformedURLException e) {
            String err = "Invalid ear file name '" + file;
            throw new EarServiceException(err, e);
        }

        // Create classLoader
        // parent classloader is the current classloader
        ClassLoader currentLoader = Thread.currentThread().getContextClassLoader();
        URLClassLoader loaderCls = new URLClassLoader(earUrl, currentLoader);

        EarDeploymentDesc desc = EarManagerWrapper.getDeploymentDesc(earUrl[0].getFile(), loaderCls);

        String[] ejbTags = desc.getEjbTags();
        /*
         * Example of a jar command: jar -uf ../output/ejbjars/sb.jar -C
         * /tmp/genic1547.tmp a/b/C1.class -C /tmp/genic1547.tmp a/b/C2.class
         * .....
         */
        String javaHomeBin = System.getProperty("java.home", "");
        if (!("".equals(javaHomeBin))) {
            javaHomeBin = javaHomeBin + File.separator + ".." + File.separator + "bin" + File.separator;
        }

        Cmd cmd = null;
        cmd = new Cmd(javaHomeBin + "jar");
        cmd.addArgument("-xf");
        cmd.addArgument(file.getAbsolutePath());

        for (int i = 0; i < ejbTags.length; i++) {
            cmd.addArgument(ejbTags[i]);
        }

        boolean exitCmd = cmd.run();
        // Analyse the result of the jar command
        if (!exitCmd) {
            throw new EarServiceException("Failed when extracting the the ejb jar " + "in the given jar file '" + file
                    + "'.");
        }

        for (int i = 0; i < ejbTags.length; i++) {
            String[] args = new String[1];
            args[0] = ejbTags[i];
            GenIC.main(args);
        }

        cmd = new Cmd(javaHomeBin + "jar");
        cmd.addArgument("-uf");
        cmd.addArgument(file.getAbsolutePath());

        for (int i = 0; i < ejbTags.length; i++) {
            cmd.addArgument(ejbTags[i]);
        }
        exitCmd = cmd.run();
        // Analyse the result of the jar command
        if (!exitCmd) {
            throw new EarServiceException("Failed when extracting the the ejb jar " + "in the given jar file '" + file
                    + "'.");
        }

        for (int i = 0; i < ejbTags.length; i++) {
            File del = new File(ejbTags[i]);
            del.delete();
        }

    }

    /**
     * List modules, needed for Ishmael to work. Currently only lists top level
     * modules. It does need to do sub elements of ears in the future.
     * @param type type of the file (EJB, WAR, EAR, RAR, CAR)
     * @param state status of the modules to be returned (RUNNING, STOPPED, ALL)
     * @throws RemoteException
     * @author Dean Jennings. <deanjennings@junta.com.au>
     */

    public List listModules(int type, int state) throws RemoteException {
        try {

            // Should look at refactoring this logic into the services instead.
            List modules = new ArrayList();
            if (type == TYPE_EJB) {
                // Will need to do parent element in future
                List jars = null;
                if (state == STATUS_RUNNING || state == STATUS_ALL) {
                    jars = ejbserv.getDeployedJars();
                    for (int i = 0; i < jars.size(); i++) {
                        ModuleDesc desc = new ModuleDesc((String) jars.get(i), STATUS_RUNNING, null);
                        modules.add(desc);
                    }
                } else if (state == STATUS_STOPPED || state == STATUS_ALL) {
                    jars = ejbserv.getDeployableJars();
                    for (int i = 0; i < jars.size(); i++) {
                        ModuleDesc desc = new ModuleDesc((String) jars.get(i), STATUS_STOPPED, null);
                        modules.add(desc);
                    }
                }
            } else if (type == TYPE_EAR) {
                // Will need to do sub elements in future
                List ears = null;
                if (state == STATUS_RUNNING || state == STATUS_ALL) {
                    ears = getEarService().getDeployedEars();
                    for (int i = 0; i < ears.size(); i++) {
                        ModuleDesc desc = new ModuleDesc((String) ears.get(i), STATUS_RUNNING, null);
                        modules.add(desc);
                    }
                } else if (state == STATUS_STOPPED || state == STATUS_ALL) {
                    ears = getEarService().getDeployableEars();
                    for (int i = 0; i < ears.size(); i++) {
                        ModuleDesc desc = new ModuleDesc((String) ears.get(i), STATUS_STOPPED, null);
                        modules.add(desc);
                    }
                }
            } else if (type == TYPE_WAR) {
                // Will need to do parent element in future
                List wars = null;
                AbsJWebContainerServiceImpl webService = (AbsJWebContainerServiceImpl) getWebContainerService(); // Very
                // evil.
                if (state == STATUS_RUNNING || state == STATUS_ALL) {
                    wars = webService.getDeployedWars();
                    for (int i = 0; i < wars.size(); i++) {
                        // Need to get the Context root of the web app as well.
                        String warName = (String) wars.get(i);
                        ClassLoader cl = webService.getClassLoader(new File(warName).toURL(), null, null);
                        WebContainerDeploymentDesc wdesc = WebManagerWrapper.getDeploymentDesc(warName, cl);
                        int start = warName.lastIndexOf("/");
                        if (start < 0) {
                            start = 0;
                        }
                        String url = wdesc.getContextRoot() != null ? wdesc.getContextRoot() : warName.substring(start,
                                warName.indexOf(".war"));
                        ModuleDesc desc = new ModuleDesc(warName, STATUS_RUNNING, url);
                        modules.add(desc);
                    }
                } else if (state == STATUS_STOPPED || state == STATUS_ALL) {
                    wars = webService.getDeployableWars();
                    for (int i = 0; i < wars.size(); i++) {
                        // Need to get the Context root of the web app as well.
                        String warName = (String) wars.get(i);
                        ClassLoader cl = webService.getClassLoader(new File(warName).toURL(), null, null);
                        WebContainerDeploymentDesc wdesc = WebManagerWrapper.getDeploymentDesc(warName, cl);
                        int start = warName.lastIndexOf("/");
                        if (start < 0) {
                            start = 0;
                        }

                        String url = wdesc.getContextRoot() != null ? wdesc.getContextRoot() : warName.substring(start,
                                warName.indexOf(".war"));
                        ModuleDesc desc = new ModuleDesc(warName, STATUS_STOPPED, url);
                        modules.add(desc);
                    }
                }
            } else if (type == TYPE_RAR) {
                // Will need to do parent element in future
                List rars = getResourceService().getInstalledRars();
                for (int i = 0; i < rars.size(); i++) {
                    ModuleDesc desc = new ModuleDesc((String) rars.get(i), STATUS_RUNNING, null);
                    modules.add(desc);
                }
            } else if (type == TYPE_CAR) {
                throw new UnsupportedOperationException("Not Supported yet");
            }

            return modules;
        } catch (Exception e) {
            // Ugly
            throw new RemoteException("Failed to list modules", e);
        }

    }

    /**
     * Physically remove the module from the server
     * @param filename the filename of the module to be removed
     * @throws RemoteException
     * @author Dean Jennings. <deanjennings@junta.com.au>
     */
    public void undeployFile(String filename) throws RemoteException {
        try {
            File f = new File(filename);
            f.delete();
        } catch (Exception ioe) {
            throw new RemoteException("Failed to remove the module " + filename + ":", ioe);
        }

    }

    /**
     * Start remote servers or clusters
     * @param target List of clusters or servers to start
     * @throws RemoteException
     */
    public void startRemoteServers(String [] target) throws RemoteException {
        for (int i = 0; i < target.length; i++) {
            String mytarget = target[i];
            BaseCluster mycluster = mydomain.findCluster(mytarget);
            if (mycluster != null) {
                // this target is a cluster
                try {
                    mycluster.startit();
                } catch (JMException e) {
                    throw new RemoteException("Cannot start cluster: " + mytarget, e);
                }
                continue;
            }
            ServerProxy myserver = mydomain.findServerProxy(mytarget);
            if (myserver != null) {
                // this target is a server
                myserver.startit();
                continue;
            }
            throw new RemoteException("unknown target: " + mytarget);
        }
    }

    /**
     * Stop remote servers or clusters
     * @param target List of clusters or servers to stop
     * @throws RemoteException
     */
    public void stopRemoteServers(String [] target) throws RemoteException {
        for (int i = 0; i < target.length; i++) {
            String mytarget = target[i];
            BaseCluster mycluster = mydomain.findCluster(mytarget);
            if (mycluster != null) {
                // this target is a cluster
                try {
                    mycluster.stopit();
                } catch (JMException e) {
                    throw new RemoteException("Cannot stop cluster: " + mytarget, e);
                }
                continue;
            }
            ServerProxy myserver = mydomain.findServerProxy(mytarget);
            if (myserver != null) {
                // this target is a server
                myserver.stopit();
                continue;
            }
            throw new RemoteException("unknown target: " + mytarget);
        }
    }

    /**
     * Deploy file on another server or a cluster.
     * @param filename Name of the file
     * @param target List of clusters or servers where to deploy the file.
     * @throws RemoteException
     */
    public void deployFileOn(String filename, String [] target) throws RemoteException {

        for (int i = 0; i < target.length; i++) {
            String mytarget = target[i];
            BaseCluster mycluster = mydomain.findCluster(mytarget);
            if (mycluster != null) {
                // this target is a cluster
                mycluster.uploadDeployModule(filename, true);
                continue;
            }
            ServerProxy myserver = mydomain.findServerProxy(mytarget);
            if (myserver != null) {
                // this target is a server
                myserver.uploadDeployModule(filename, true);
                continue;
            }
            throw new RemoteException("unknown target: " + mytarget);
        }
    }

}
