/**
 * JaDOrT: JASMINe Deployment Orchestration Tool
 * Copyright (C) 2008-2009 Bull S.A.S.
 * Copyright (C) 2008-2009 France Telecom R&D
 * Contact: jasmine@ow2.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id: ActionManager.java 5846 2010-01-05 13:16:36Z alitokmen $
 * --------------------------------------------------------------------------
 */
package org.ow2.jasmine.jadort.service.implementation;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import org.ow2.jasmine.jadort.api.JadortServiceException;
import org.ow2.jasmine.jadort.api.IJadortService.OperationType;
import org.ow2.jasmine.jadort.api.entities.deployment.ApplicationBean;
import org.ow2.jasmine.jadort.api.entities.deployment.ServerProgressBean;
import org.ow2.jasmine.jadort.api.entities.deployment.ServerProgressState;
import org.ow2.jasmine.jadort.api.entities.deployment.VMImageBean;
import org.ow2.jasmine.jadort.api.entities.deployment.WorkerProgressBean;
import org.ow2.jasmine.jadort.api.entities.deployment.WorkerProgressState;
import org.ow2.jasmine.jadort.api.entities.deployment.ApplicationBean.ApplicationType;
import org.ow2.jasmine.jadort.api.entities.deployment.OperationStateBean.ActionState;
import org.ow2.jasmine.jadort.api.entities.deployment.OperationStateBean.Step;
import org.ow2.jasmine.jadort.api.entities.topology.GroupBean;
import org.ow2.jasmine.jadort.api.entities.topology.ServerBean;
import org.ow2.jasmine.jadort.api.entities.topology.VMBean;
import org.ow2.jasmine.jadort.api.entities.topology.WorkerBean;
import org.ow2.jasmine.jadort.service.action.ServerAction;
import org.ow2.jasmine.jadort.service.action.VMMAction;
import org.ow2.jasmine.jadort.service.action.WorkerAction;

/**
 * Manages the link between server and worker states and the messages to send.
 * 
 * @author Arda Aydin
 * @author Malek Chahine
 * @author S. Ali Tokmen
 */
public class ActionManager extends OperationPersistenceManager {

    /**
     * this function allows the serverProgress to executes the proper task to
     * attend the Aimed ServerProgressState this function executes the
     * correspondent task to go from the current serverProgress state to the
     * Aimed ServerProgressList State.
     * 
     * @param serverProgress the serverProgress that will execute the task.
     */
    protected void reachAimedServerProgressState(final ServerProgressBean serverProgress) {
        ServerProgressState currentServerProgressState = serverProgress.getProgressState();
        ServerProgressState aimedServerProgressState = this.operation.getAimedServerProgressState();

        try {
            OperationType type = this.operation.getType();
            if (type.equals(OperationType.MAINTAIN)) {
                if (currentServerProgressState.equals(ServerProgressState.INITIAL)) {
                    if (this.operation.getSelectedGroup().getClustered()) {
                        if (aimedServerProgressState.equals(ServerProgressState.STOP_OK)) {
                            // next
                            if (serverProgress.getServer().getVm() != null) {
                                JMSSender.stopVM(this.operation, serverProgress, false, serverProgress.getOldDeploymentItem());
                            } else {
                                JMSSender.stopServer(this.operation, serverProgress, false);
                            }
                            return;
                        }
                        // no previous
                    } else {
                        if (aimedServerProgressState.equals(ServerProgressState.DISABLE_APPLICATIONS_OK)) {
                            // next
                            JMSSender.disableApplications(this.operation, serverProgress);
                            return;
                        }
                        // no previous
                    }
                } else if (currentServerProgressState.equals(ServerProgressState.DISABLE_APPLICATIONS_OK)) {
                    if (aimedServerProgressState.equals(ServerProgressState.STOP_OK)) {
                        // next
                        if (serverProgress.getServer().getVm() != null) {
                            JMSSender.stopVM(this.operation, serverProgress, false, serverProgress.getOldDeploymentItem());
                        } else {
                            JMSSender.stopServer(this.operation, serverProgress, false);
                        }
                        return;
                    } else if (aimedServerProgressState.equals(ServerProgressState.INITIAL)) {
                        // previous
                        JMSSender.enableApplications(this.operation, serverProgress);
                        return;
                    }
                } else if (currentServerProgressState.equals(ServerProgressState.STOP_OK)) {
                    if (aimedServerProgressState.equals(ServerProgressState.MAINTAIN_OK)) {
                        // next
                        if (serverProgress.getServer().getVm() != null) {
                            JMSSender.createVM(this.operation, serverProgress, serverProgress.getOldDeploymentItem());
                        } else {
                            // The "maintain server" step is manual
                            serverProgress.setActionState(ActionState.WAITING);
                            serverProgress.setProgress(this.operation.getAimedProgressPercent());
                            serverProgress.setProgressState(this.operation.getAimedServerProgressState());
                        }
                        return;
                    } else if (aimedServerProgressState.equals(ServerProgressState.INITIAL)) {
                        // previous
                        if (serverProgress.getServer().getVm() != null) {
                            JMSSender.startVM(this.operation, serverProgress, false, serverProgress.getOldDeploymentItem());
                        } else {
                            JMSSender.startServer(this.operation, serverProgress, false);
                        }
                        return;
                    } else if (aimedServerProgressState.equals(ServerProgressState.DISABLE_APPLICATIONS_OK)) {
                        // previous
                        if (serverProgress.getServer().getVm() != null) {
                            JMSSender.startVM(this.operation, serverProgress, false, serverProgress.getOldDeploymentItem());
                        } else {
                            JMSSender.startServer(this.operation, serverProgress, false);
                        }
                        return;
                    }
                } else if (currentServerProgressState.equals(ServerProgressState.MAINTAIN_OK)) {
                    if (aimedServerProgressState.equals(ServerProgressState.START_OK)) {
                        // next
                        if (serverProgress.getServer().getVm() != null) {
                            JMSSender.startVM(this.operation, serverProgress, true, serverProgress.getNewDeploymentItem());
                        } else {
                            JMSSender.startServer(this.operation, serverProgress, true);
                        }
                        return;
                    } else if (aimedServerProgressState.equals(ServerProgressState.STOP_OK)) {
                        // previous
                        if (serverProgress.getServer().getVm() != null) {
                            JMSSender.destroyVM(this.operation, serverProgress, serverProgress.getNewDeploymentItem(), false);
                        } else {
                            // The "maintain server" step is manual
                            serverProgress.setActionState(ActionState.WAITING);
                            serverProgress.setProgress(this.operation.getAimedProgressPercent());
                            serverProgress.setProgressState(this.operation.getAimedServerProgressState());
                        }
                        return;
                    }
                } else if (currentServerProgressState.equals(ServerProgressState.START_OK)) {
                    if (aimedServerProgressState.equals(ServerProgressState.MAINTAIN_OK)) {
                        // previous
                        if (serverProgress.getServer().getVm() != null) {
                            JMSSender.stopVM(this.operation, serverProgress, true, serverProgress.getNewDeploymentItem());
                        } else {
                            JMSSender.stopServer(this.operation, serverProgress, true);
                        }
                        return;
                    } else if (aimedServerProgressState.equals(ServerProgressState.DESTROY_VM_HOSTS_OK)) {
                        JMSSender.destroyVM(this.operation, serverProgress, serverProgress.getOldDeploymentItem(), true);
                        return;
                    }
                } else {
                    throw new IllegalStateException("Unknown ServerProgressState: " + aimedServerProgressState);
                }
            } else if (type.equals(OperationType.MIGRATE)) {
                if (ApplicationType.OSGI.equals(this.operation.getApplication().getApplicationType())) {
                    if (currentServerProgressState.equals(ServerProgressState.INITIAL)) {
                        if (aimedServerProgressState.equals(ServerProgressState.UPLOAD_OK)) {
                            // next
                            JMSSender.upload(this.operation, serverProgress, this.operation.getApplication());
                            return;
                        }
                        // no previous
                    } else if (currentServerProgressState.equals(ServerProgressState.UPLOAD_OK)) {
                        if (aimedServerProgressState.equals(ServerProgressState.UNDEPLOY_OK)) {
                            if (serverProgress.getOldDeploymentItem() == null) {
                                serverProgress.appendToLog("There's no old version, ignoring step.");

                                serverProgress.setProgress(this.operation.getAimedProgressPercent());
                                serverProgress.setActionState(ActionState.WAITING);
                                serverProgress.setProgressState(aimedServerProgressState);
                            } else {
                                // next
                                JMSSender.undeploy(this.operation, serverProgress, serverProgress.getOldDeploymentItem());
                            }
                            return;
                        } else if (aimedServerProgressState.equals(ServerProgressState.INITIAL)) {
                            // previous
                            JMSSender.erase(this.operation, serverProgress, serverProgress.getNewDeploymentItem());
                            return;
                        }
                    } else if (currentServerProgressState.equals(ServerProgressState.UNDEPLOY_OK)) {
                        if (aimedServerProgressState.equals(ServerProgressState.DEPLOY_OK)) {
                            // next
                            JMSSender.deploy(this.operation, serverProgress, serverProgress.getNewDeploymentItem());
                            return;
                        } else if (aimedServerProgressState.equals(ServerProgressState.UPLOAD_OK)) {
                            if (serverProgress.getOldDeploymentItem() == null) {
                                serverProgress.appendToLog("There's no old version, ignoring step.");

                                serverProgress.setProgress(this.operation.getAimedProgressPercent());
                                serverProgress.setActionState(ActionState.WAITING);
                                serverProgress.setProgressState(aimedServerProgressState);
                            } else {
                                // previous
                                JMSSender.deploy(this.operation, serverProgress, serverProgress.getOldDeploymentItem());
                            }
                            return;
                        }
                    } else if (currentServerProgressState.equals(ServerProgressState.DEPLOY_OK)) {
                        if (aimedServerProgressState.equals(ServerProgressState.ERASE_OK)) {
                            if (serverProgress.getOldDeploymentItem() == null) {
                                serverProgress.appendToLog("There's no old version, ignoring step.");

                                serverProgress.setProgress(this.operation.getAimedProgressPercent());
                                serverProgress.setActionState(ActionState.WAITING);
                                serverProgress.setProgressState(aimedServerProgressState);
                            } else {
                                // next
                                JMSSender.erase(this.operation, serverProgress, serverProgress.getOldDeploymentItem());
                            }
                            return;
                        } else if (aimedServerProgressState.equals(ServerProgressState.UNDEPLOY_OK)) {
                            // previous
                            JMSSender.undeploy(this.operation, serverProgress, serverProgress.getNewDeploymentItem());
                            return;
                        }
                    } else if (currentServerProgressState.equals(ServerProgressState.ERASE_OK)) {
                        // no next [handled by caller method]
                        if (aimedServerProgressState.equals(ServerProgressState.DEPLOY_OK)) {
                            // previous
                            if (serverProgress.getOldDeploymentItem() == null) {
                                serverProgress.appendToLog("There's no old version, ignoring step.");

                                serverProgress.setProgress(this.operation.getAimedProgressPercent());
                                serverProgress.setActionState(ActionState.WAITING);
                                serverProgress.setProgressState(aimedServerProgressState);
                            } else {
                                throw new IllegalStateException("Cannot resurrect an erased bundle!");
                            }
                            return;
                        }
                    } else {
                        throw new IllegalStateException("Unknown ServerProgressState: " + aimedServerProgressState);
                    }
                } else {
                    if (currentServerProgressState.equals(ServerProgressState.INITIAL)) {
                        if (aimedServerProgressState.equals(ServerProgressState.UPLOAD_OK)) {
                            // next
                            JMSSender.upload(this.operation, serverProgress, this.operation.getApplication());
                            return;
                        }
                        // no previous
                    } else if (currentServerProgressState.equals(ServerProgressState.UPLOAD_OK)) {
                        if (aimedServerProgressState.equals(ServerProgressState.DEPLOY_OK)) {
                            // next
                            JMSSender.deploy(this.operation, serverProgress, serverProgress.getNewDeploymentItem());
                            return;
                        } else if (aimedServerProgressState.equals(ServerProgressState.INITIAL)) {
                            // previous
                            JMSSender.erase(this.operation, serverProgress, serverProgress.getNewDeploymentItem());
                            return;
                        }
                    } else if (currentServerProgressState.equals(ServerProgressState.DEPLOY_OK)) {
                        if (aimedServerProgressState.equals(ServerProgressState.SET_DEFAULT_OK)) {
                            // next
                            JMSSender.setAsDefault(this.operation, serverProgress, serverProgress.getNewDeploymentItem());
                            return;
                        } else if (aimedServerProgressState.equals(ServerProgressState.UPLOAD_OK)) {
                            // previous
                            JMSSender.undeploy(this.operation, serverProgress, serverProgress.getNewDeploymentItem());
                            return;
                        }
                    } else if (currentServerProgressState.equals(ServerProgressState.SET_DEFAULT_OK)) {
                        if (aimedServerProgressState.equals(ServerProgressState.UNDEPLOY_OK)) {
                            // next
                            if (!serverProgress.hasOldDeploymentItem()) {
                                throw new Exception("There's no old version or the old and new versions are the same");
                            } else {
                                JMSSender.undeploy(this.operation, serverProgress, serverProgress.getOldDeploymentItem());
                            }
                            return;
                        } else if (aimedServerProgressState.equals(ServerProgressState.DEPLOY_OK)) {
                            // previous
                            if (serverProgress.getOldDeploymentItem() == null) {
                                serverProgress.appendToLog("There's no old version, ignoring step.\n"
                                    + "This implies that the new [and only] version will keep on being the default one.");

                                serverProgress.setProgress(this.operation.getAimedProgressPercent());
                                serverProgress.setActionState(ActionState.WAITING);
                                serverProgress.setProgressState(aimedServerProgressState);
                            } else {
                                JMSSender.setAsDefault(this.operation, serverProgress, serverProgress.getOldDeploymentItem());
                            }
                            return;
                        }
                    } else if (currentServerProgressState.equals(ServerProgressState.UNDEPLOY_OK)) {
                        if (aimedServerProgressState.equals(ServerProgressState.ERASE_OK)) {
                            // next
                            if (!serverProgress.hasOldDeploymentItem()) {
                                throw new Exception("There's no old version or the old and new versions are the same");
                            } else {
                                JMSSender.erase(this.operation, serverProgress, serverProgress.getOldDeploymentItem());
                            }
                            return;
                        } else if (aimedServerProgressState.equals(ServerProgressState.SET_DEFAULT_OK)) {
                            // previous
                            JMSSender.deploy(this.operation, serverProgress, serverProgress.getOldDeploymentItem());
                            return;
                        }
                    } else {
                        throw new IllegalStateException("Unknown ServerProgressState: " + aimedServerProgressState);
                    }
                }
            } else {
                throw new IllegalStateException("Unknown OperationType: " + type);
            }
        } catch (Exception e) {
            serverProgress.appendToLog("Failed executing server action: " + e.getClass().getCanonicalName() + ", "
                + e.getMessage());

            OutputStream stackTrace = new ByteArrayOutputStream();
            e.printStackTrace(new PrintStream(stackTrace));
            BufferedReader br = new BufferedReader(new StringReader(stackTrace.toString()));
            String line;
            try {
                while ((line = br.readLine()) != null) {
                    if (line.length() > 0) {
                        serverProgress.appendToLog(line);
                    }
                }
            } catch (IOException ignored) {
                // Ignored
            }

            serverProgress.setProgress(this.operation.getAimedProgressPercent());
            serverProgress.setActionState(ActionState.FINISHED_ERROR);
            serverProgress.setProgressState(this.operation.getAimedServerProgressState());

            return;
        }

        serverProgress.appendToLog("Don't know what to do: operation.getAimedServerProgressState() is "
            + aimedServerProgressState + ", serverProgress.getProgressState() is " + currentServerProgressState);

        serverProgress.setProgress(this.operation.getAimedProgressPercent());
        serverProgress.setActionState(ActionState.FINISHED_ERROR);

        return;
    }

    /**
     * this function allows the workerProgress to executes the proper task to
     * attend the Aimed WorkerProgress this function executes the correspondent
     * task to go from the current serverProgress state to the Aimed
     * WorkerProgress State.
     * 
     * @param workerProgress the workerProgress that will execute the task.
     */
    protected void reachAimedWorkerProgressState(final WorkerProgressBean workerProgress) {
        WorkerProgressState currentWorkerProgressState = workerProgress.getProgressState();
        WorkerProgressState aimedWorkerProgressState = this.operation.getAimedWorkerProgressState();

        try {
            OperationType type = this.operation.getType();
            if (type.equals(OperationType.MAINTAIN)) {
                if (currentWorkerProgressState.equals(WorkerProgressState.INITIAL)) {
                    if (aimedWorkerProgressState.equals(WorkerProgressState.STOP_OK)) {
                        // next
                        JMSSender.stopWorker(this.operation, workerProgress);
                        return;
                    }
                    // no previous
                } else if (currentWorkerProgressState.equals(WorkerProgressState.STOP_OK)) {
                    if (aimedWorkerProgressState.equals(WorkerProgressState.START_OK)) {
                        // next
                        JMSSender.startWorker(this.operation, workerProgress);
                        return;
                    } else if (aimedWorkerProgressState.equals(WorkerProgressState.INITIAL)) {
                        // previous
                        JMSSender.startWorker(this.operation, workerProgress);
                        return;
                    }
                } else if (currentWorkerProgressState.equals(WorkerProgressState.START_OK)) {
                    if (aimedWorkerProgressState.equals(WorkerProgressState.STOP_OK)) {
                        // previous
                        JMSSender.stopWorker(this.operation, workerProgress);
                        return;
                    }
                } else {
                    throw new IllegalStateException("Unknown WorkerProgressState: " + currentWorkerProgressState);
                }
            } else {
                throw new IllegalStateException("Unknown OperationType: " + type);
            }
        } catch (Exception e) {
            workerProgress.appendToLog("Failed executing worker action: " + e.getClass().getCanonicalName() + ", "
                + e.getMessage());

            OutputStream stackTrace = new ByteArrayOutputStream();
            e.printStackTrace(new PrintStream(stackTrace));
            BufferedReader br = new BufferedReader(new StringReader(stackTrace.toString()));
            String line;
            try {
                while ((line = br.readLine()) != null) {
                    if (line.length() > 0) {
                        workerProgress.appendToLog(line);
                    }
                }
            } catch (IOException ignored) {
                // Ignored
            }

            workerProgress.setProgress(this.operation.getAimedProgressPercent());
            workerProgress.setActionState(ActionState.FINISHED_ERROR);
            workerProgress.setProgressState(this.operation.getAimedWorkerProgressState());

            return;
        }

        workerProgress.appendToLog("Don't know what to do: operation.getAimedWorkerProgressState() is "
            + aimedWorkerProgressState + ", workerProgress.getAimedWorkerProgressState() is " + currentWorkerProgressState);

        workerProgress.setProgress(this.operation.getAimedProgressPercent());
        workerProgress.setActionState(ActionState.FINISHED_ERROR);

        return;
    }

    /**
     * Fills the applications of a {@link GroupBean} and all {@link ServerBean}s
     * it contains.
     * 
     * @param groupBean Group to fill.
     * @throws JadortServiceException If getting the list of applications fails.
     */
    protected void fillApplications(final GroupBean groupBean) throws JadortServiceException {
        SortedMap<String, ApplicationBean> groupApplications = new TreeMap<String, ApplicationBean>();

        for (ServerBean serverBean : groupBean.getServers()) {
            ServerAction serverAction = ServerAction.getServerAction(serverBean);
            try {
                SortedMap<String, ApplicationBean> serverApplications = new TreeMap<String, ApplicationBean>();

                for (ApplicationBean applicationBean : serverAction.listOfApplications()) {
                    groupApplications.put(applicationBean.toString(), applicationBean);
                    serverApplications.put(applicationBean.toString(), applicationBean);
                }

                serverBean.setApplications(new ArrayList<ApplicationBean>(serverApplications.values()));
            } catch (Exception e) {
                String message = e.getClass().getName() + ": " + e.getMessage();
                throw new JadortServiceException("Failed getting the applications on the server " + serverBean.getName()
                    + ": " + message, e);
            }
        }

        groupBean.setApplications(new ArrayList<ApplicationBean>(groupApplications.values()));
    }

    /**
     * Fills the vmImages fields of a {@link GroupBean}.
     * 
     * @param groupBean Group to fill.
     * @throws JadortServiceException If getting the list of applications fails.
     */
    protected void fillVMImages(final GroupBean groupBean) throws JadortServiceException {
        // Assume all servers have the same VMM manager and the same images
        ServerBean serverBean = groupBean.getServers().iterator().next();

        List<VMImageBean> vmImages = new ArrayList<VMImageBean>();
        VMBean vmBean = serverBean.getVm();
        if (vmBean != null) {
            VMMAction vmAction = VMMAction.getVMMAction(vmBean);
            try {
                for (VMImageBean vmImageBean : vmAction.getVMImages()) {
                    vmImages.add(vmImageBean);
                }
            } catch (Exception e) {
                String message = e.getClass().getName() + ": " + e.getMessage();
                throw new JadortServiceException("Failed getting the VM images on the virtual machine manager "
                    + vmBean.getVmm() + ": " + message, e);
            }
            groupBean.setVmImages(vmImages);
        }
    }

    /**
     * Returns the list of groups obtained from the loaded topology in the
     * current operation.(returns a copy since
     * operation.getTopology().getGroups() does lazy)
     * 
     * @param checkConnectivity Whether to check connectivity with all servers,
     *        workers and VMs. This will also fill the applications and VM
     *        images lists.
     * @return The list of groups
     * @throws JadortServiceException If getting the list of applications fails.
     */
    protected List<GroupBean> fetchGroups(final boolean checkConnectivity) throws JadortServiceException {
        if (this.operation.getTopology() != null && this.operation.getTopology().getGroups() != null) {
            // Return a copy since operation.getTopology().getGroups() does lazy
            // initialization. Remember to copy all list attributes as well!
            List<GroupBean> result = new ArrayList<GroupBean>(this.operation.getTopology().getGroups().size());
            for (GroupBean groupBean : this.operation.getTopology().getGroups()) {
                GroupBean groupBeanCopy = new GroupBean();
                groupBeanCopy.setId(groupBean.getId());
                groupBeanCopy.setWorkers(new ArrayList<WorkerBean>(groupBean.getWorkers()));
                groupBeanCopy.setName(groupBean.getName());
                groupBeanCopy.setServers(new ArrayList<ServerBean>(groupBean.getServers()));
                groupBeanCopy.setClustered(groupBean.getClustered());
                if (checkConnectivity) {
                    if (groupBean.getServers() != null) {
                        for (ServerBean server : groupBean.getServers()) {
                            ServerAction serverAction = ServerAction.getServerAction(server);
                            try {
                                serverAction.listOfApplications();
                            } catch (IOException e) {
                                groupBeanCopy.setConnected(false);
                            } catch (Exception e) {
                                String message = e.getClass().getName() + ": " + e.getMessage();
                                throw new JadortServiceException("Failed getting the applications on the server "
                                    + server.getName() + ": " + message, e);
                            }
                            if (server.getVm() != null) {
                                VMMAction vmmAction = VMMAction.getVMMAction(server.getVm());
                                try {
                                    String fullServerName = vmmAction.getFullVMName(server.getVm().getName());
                                    vmmAction.getVMState(fullServerName);
                                } catch (IOException e) {
                                    groupBeanCopy.setConnected(false);
                                } catch (Exception e) {
                                    String message = e.getClass().getName() + ": " + e.getMessage();
                                    throw new JadortServiceException("Failed getting the state for the VM "
                                        + server.getVm().getName() + ": " + message, e);
                                }
                            }
                        }
                    }
                    if (groupBean.getWorkers() != null) {
                        for (WorkerBean worker : groupBean.getWorkers()) {
                            WorkerAction workerAction = WorkerAction.getWorkerAction(worker);
                            try {
                                workerAction.getState();
                            } catch (IOException e) {
                                groupBeanCopy.setConnected(false);
                            } catch (Exception e) {
                                String message = e.getClass().getName() + ": " + e.getMessage();
                                throw new JadortServiceException("Failed getting the state of worker "
                                    + worker.getName() + ": " + message, e);
                            }
                        }
                    }

                    if (groupBeanCopy.getConnected()) {
                        this.fillApplications(groupBeanCopy);
                    }
                }
                result.add(groupBeanCopy);
            }
            return result;
        } else {
            return null;
        }
    }

    /**
     * Compares two application, tests if a two applications are equal). The
     * comparison is based on the name,and version of each application. the
     * result is a true the two applications are the same. the result is false
     * if the two applications are different.
     * 
     * @param a1 the first application to be compared.
     * @param a2 the second application to be compared.
     * @return True the two applications are the same. False if the two
     *         applications are different.
     */
    protected boolean isSameApplication(final ApplicationBean a1, final ApplicationBean a2) {
        if (a1 == null || a2 == null || a1.getName() == null) {
            return false;
        }
        return a1.getName().equals(a2.getName())
            && (a1.getVersion() == a2.getVersion() || a1.getVersion() != null && a1.getVersion().equals(a2.getVersion()));
    }

    /**
     * Checks if an application is already deployed on the server, or if has a
     * bad policy, or if has a bad state
     * 
     * @param serverProgress serverProgress of the server on which the
     *        application will be checked.
     * @param application the application to be checked
     * @param state the state that the application should has.
     * @param policy the policy that the application should has.
     * @return true if no problem is found while checking the application false
     *         if a problem is found while checking the application
     */
    protected boolean checkApplication(final ServerProgressBean serverProgress, final ApplicationBean application,
        final String state, final String policy) {
        if (state == null) {
            if (application != null) {
                serverProgress.appendToLog("Application " + application
                    + " exists (it should not exist), checkServer will return false");
                return false;
            } else {
                return true;
            }
        } else {
            if (state.equals(application.getState())) {
                if (policy == null) {
                    if (application.getPolicy() != null) {
                        serverProgress.appendToLog("Application " + application + " has policy " + application.getPolicy()
                            + " (it should not have any), checkServer will return false");
                        return false;
                    } else {
                        return true;
                    }
                } else {
                    if (!policy.equals(application.getPolicy())) {
                        serverProgress.appendToLog("Application " + application + " has policy " + application.getPolicy()
                            + " instead of " + policy + ", checkServer will return false");
                        return false;
                    } else {
                        return true;
                    }
                }
            } else {
                serverProgress.appendToLog("Application " + application + " has state " + application.getState()
                    + " instead of " + state + ", checkServer will return false");
                return false;
            }
        }
    }

    /**
     * @see ServerBean#getActiveSessions()<br/>
     *      Note that this doesn't merge with the current operation in DB.
     */
    protected void refreshActiveSessions() throws JadortServiceException {
        try {
            if (this.operation == null) {
                // Nothing to do yet...
                return;
            }

            OperationType type = this.operation.getType();
            if (type == null) {
                // Nothing to do yet...
                return;
            }

            if (type.equals(OperationType.MAINTAIN) || Step.SELECT_SERVERS.equals(this.operation.getCurrentStep())) {
                for (ServerBean server : this.operation.getSelectedGroup().getServers()) {
                    int activeSessions = 0;
                    try {
                        ServerAction serverAction = ServerAction.getServerAction(server);
                        activeSessions = serverAction.getActiveSessions();
                    } catch (Exception ignored) {
                        // Ignored
                    }

                    if (activeSessions == 0 && server.getTarget() != null) {
                        try {
                            ServerAction serverAction = ServerAction.getServerAction(server.getTarget());
                            activeSessions = serverAction.getActiveSessions();
                        } catch (Exception ignored) {
                            // Ignored
                        }
                    }

                    server.setActiveSessions(activeSessions);
                }
            } else if (type.equals(OperationType.MIGRATE)) {
                if (this.operation.getServerProgressList() != null) {
                    for (ServerProgressBean serverProgressBean : this.operation.getServerProgressList()) {
                        ServerAction serverAction = ServerAction.getServerAction(serverProgressBean.getServer());
                        String oldApplication = serverProgressBean.getOldDeploymentItem();
                        if (oldApplication != null) {
                            try {
                                serverProgressBean.getServer()
                                    .setActiveSessions(serverAction.getActiveSessions(oldApplication));
                            } catch (Exception ignored) {
                                serverProgressBean.getServer().setActiveSessions(0);
                            }
                        } else {
                            serverProgressBean.getServer().setActiveSessions(0);
                        }
                    }
                }
            } else {
                throw new IllegalStateException("Unknown operation type: " + type);
            }
        } catch (Exception e) {
            throw new JadortServiceException("Cannot get the number of active sessions: " + e.getMessage(), e);
        }
    }

    /**
     * Returns the serverProgress object of a server
     * 
     * @param server the server that the method will return his serverProgress
     * @return the serverProgress of the server
     * @throws JadortServiceException If server not found
     */
    protected ServerProgressBean getServerProgress(final ServerBean server) throws JadortServiceException {
        for (ServerProgressBean serverProgressBean : this.operation.getServerProgressList()) {
            if (serverProgressBean.getServer().equals(server)) {
                return serverProgressBean;
            }
        }

        throw new JadortServiceException("ServerBean \"" + server + "\" not found", null);
    }

    /**
     * Returns the workerProgress object of a worker
     * 
     * @param worker the server that the method will return his workerProgress
     * @return the workerProgress of the worker
     * @throws JadortServiceException If server not found
     */
    protected WorkerProgressBean getWorkerProgress(final WorkerBean worker) throws JadortServiceException {
        for (WorkerProgressBean workerProgressBean : this.operation.getWorkerProgressList()) {
            if (workerProgressBean.getWorker().getId().equals(worker.getId())) {
                return workerProgressBean;
            }
        }

        throw new JadortServiceException("WorkerBean \"" + worker + "\" not found", null);
    }

    /**
     * Fills the workerProgress list of the current operation based on the
     * serverProgress list.
     */
    protected void fillWorkerProgressListBasedOnServerProgressList() {
        List<WorkerProgressBean> workerProgressList = new ArrayList<WorkerProgressBean>();

        // List all workers that handle the selected servers
        Set<WorkerBean> workersList = new HashSet<WorkerBean>();
        for (WorkerBean worker : this.operation.getSelectedGroup().getWorkers()) {
            for (ServerProgressBean serverProgress : this.operation.getServerProgressList()) {
                if (worker.getServer().equals(serverProgress.getServer())) {
                    workersList.add(worker);
                }
            }
        }

        // Find the WorkerProgressBean objects associated with these workers
        for (WorkerBean worker : workersList) {
            boolean hasProgress = false;
            if (this.operation.getAllWorkerProgressList() != null) {
                for (WorkerProgressBean oldWorkerProgress : this.operation.getAllWorkerProgressList()) {
                    if (oldWorkerProgress.getWorker().equals(worker)) {
                        hasProgress = true;
                        workerProgressList.add(oldWorkerProgress);
                        break;
                    }
                }
            }
            if (!hasProgress) {
                workerProgressList.add(new WorkerProgressBean(worker));
            }
        }

        this.operation.setWorkerProgressList(workerProgressList);
        this.operation.addAllWorkerProgressList(this.operation.getWorkerProgressList());
    }

}
