/**
 * 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: JadortServiceImpl.java 3663 2009-05-11 15:57:35Z chahinem $
 * --------------------------------------------------------------------------
 */
package org.ow2.jasmine.jadort.service.implementation;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.StringReader;
import java.math.BigInteger;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.Local;
import javax.ejb.Remote;
import javax.ejb.Stateful;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.naming.InitialContext;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.UnmarshalException;
import javax.xml.bind.Unmarshaller;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;

import org.ow2.jasmine.deployme.generated.Configuration;
import org.ow2.jasmine.deployme.generated.Configuration.Protocols;
import org.ow2.jasmine.deployme.generated.Topology.Domains;
import org.ow2.jasmine.deployme.generated.Topology.Domains.Domain.ClusterDaemons;
import org.ow2.jasmine.deployme.generated.Topology.Domains.Domain.Servers;
import org.ow2.jasmine.jadort.api.IJadortService;
import org.ow2.jasmine.jadort.api.JadortServiceException;
import org.ow2.jasmine.jadort.api.entities.deployment.ApplicationBean;
import org.ow2.jasmine.jadort.api.entities.deployment.OperationStateBean;
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.OperationStateBean.ActionState;
import org.ow2.jasmine.jadort.api.entities.deployment.OperationStateBean.Step;
import org.ow2.jasmine.jadort.api.entities.topology.ConnectorBean;
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.TopologyBean;
import org.ow2.jasmine.jadort.api.entities.topology.VMBean;
import org.ow2.jasmine.jadort.api.entities.topology.WorkerBean;
import org.ow2.jasmine.jadort.api.entities.topology.ServerBean.Type;
import org.ow2.jasmine.jadort.service.action.ServerAction;
import org.ow2.jasmine.jadort.service.action.VMMAction;
import org.ow2.jasmine.jadort.service.action.WorkerAction;
import org.ow2.jasmine.jadort.service.topology.xml.Balancer;
import org.ow2.jasmine.jadort.service.topology.xml.Server;
import org.ow2.jasmine.jadort.service.topology.xml.Target;
import org.ow2.jasmine.jadort.service.topology.xml.URLConnector;
import org.ow2.jasmine.jadort.service.topology.xml.VM;
import org.ow2.jasmine.jadort.service.topology.xml.VMM;
import org.ow2.jasmine.jadort.service.topology.xml.Worker;
import org.ow2.jasmine.jadort.service.topology.xml.Topology.Group;
import org.xml.sax.SAXException;

/**
 * This class provides default implementations for the IJadortService interface.
 * 
 * @author Malek Chahine
 * @author Remy Bresson
 * @author S. Ali Tokmen
 */
@Local(value = IJadortService.class)
@Remote(value = IJadortService.class)
@Stateful(mappedName = IJadortService.EJB_JNDI_NAME)
public class JadortServiceImpl implements IJadortService {

    // Constants used internally by JadortServiceImpl
    private final static int BUFFER_SIZE = 16384;

    private final static String XSD_PATH_JADORT = "XMLSchema/topology_JadortSpecific.xsd";

    private final static String XSD_PATH_DEPLOYME = "xml/topology.xsd";

    // Constants shared within the package (mostly JadortServiceJMSActions)
    final static String QUEUE_NAME = "queue/JadortServiceJMSQueue";

    final static String SERVER_PROGRESS_ID = "serverProgressId";

    final static String WORKER_PROGRESS_ID = "workerProgressId";

    final static String AIMED_PROGRESS_PERCENT = "aimedProgressPercent";

    final static String AIMED_STATE = "aimedState";

    final static String APPLICATION_ID = "applicationId";

    final static String VM_IMAGE_UUID = "vmImageUUID";

    final static String ACTION_TYPE = "actionType";

    final static String APPLICATION_NAME = "applicationName";

    final static String VM_NAME = "vmName";

    final static String CHECK_IF_HOST_HALTED = "checkIfHostHalted";

    final static String OPERATION_TYPE = "operationType";

    // Constants used for tests
    static boolean DUMMY = false;

    /**
     * Action type shared within the package (mostly JadortServiceJMSActions)
     */
    enum ActionType {
        UPLOAD,
        DEPLOY,
        SET_DEFAULT,
        UNDEPLOY,
        ERASE,
        START_SERVER,
        STOP_SERVER,
        START_WORKER,
        STOP_WORKER,
        DISABLE_APPLICATIONS,
        ENABLE_APPLICATIONS,
        START_VM,
        CREATE_VM,
        DESTROY_VM,
        STOP_VM
    }

    private OperationStateBean operation;

    @PersistenceContext
    protected EntityManager em;

    protected QueueConnection qc;

    protected QueueSession session;

    protected Queue queue;

    @PostConstruct
    protected void initialize() {
        try {
            InitialContext ctx = new InitialContext();
            this.queue = (Queue) ctx.lookup(JadortServiceImpl.QUEUE_NAME);
            QueueConnectionFactory factory = (QueueConnectionFactory) ctx.lookup("QCF");
            this.qc = factory.createQueueConnection();
            this.session = this.qc.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
        } catch (Exception e) {
            IllegalStateException exception = new IllegalStateException("Failed initializing a JadortService stateful bean: "
                + e.getMessage(), e);
            System.out.println(exception.getMessage());
            e.printStackTrace();
            throw exception;
        }
    }

    @PreDestroy
    protected void terminate() {
        try {
            this.session.close();
            this.qc.close();
        } catch (Exception e) {
            IllegalStateException exception = new IllegalStateException("Failed terminating a JadortService stateful bean: "
                + e.getMessage(), e);
            System.out.println(exception.getMessage());
            e.printStackTrace();
            throw exception;
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @throws JadortServiceException if the getOperationsList() method can not
     *         be called in the current step. (this method can be called only in
     *         SELECT_OPERATION step)
     */
    @SuppressWarnings("unchecked")
    public List<OperationStateBean> getOperationsList() throws JadortServiceException {
        synchronized (this) {
            this.refreshOperation();
            this.checkStep(Step.SELECT_OPERATION);

            List<OperationStateBean> result = new ArrayList<OperationStateBean>();

            // Create a dummy list which contains only the needed fields
            for (OperationStateBean operation : (List<OperationStateBean>) this.em.createQuery(
                "select o from JaDOrT_OperationStateBean o order by o.date asc").getResultList()) {
                OperationStateBean temp = new OperationStateBean();
                temp.setAimedServerProgressState(operation.getAimedServerProgressState());
                temp.setAimedWorkerProgressState(operation.getAimedWorkerProgressState());
                temp.setCurrentStep(operation.getCurrentStep());
                temp.setDate(operation.getDate());
                temp.setId(operation.getId());
                temp.setName(operation.getName());
                temp.setType(operation.getType());
                result.add(temp);
            }
            return result;
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @throws JadortServiceException If fetching fails.
     */
    public OperationStateBean getCurrentOperation() throws JadortServiceException {
        synchronized (this) {
            this.refreshOperation();

            if (this.operation == null) {
                return null;
            } else {
                // Return a copy since operation has been initialized lazily
                OperationStateBean bean = new OperationStateBean();
                bean.setAimedServerProgressState(this.operation.getAimedServerProgressState());
                bean.setAimedWorkerProgressState(this.operation.getAimedWorkerProgressState());
                bean.setAimedProgressPercent(this.operation.getAimedProgressPercent());
                bean.setApplication(this.operation.getApplication());
                bean.setCurrentStep(this.operation.getCurrentStep());
                bean.setDate(this.operation.getDate());
                bean.setId(this.operation.getId());
                bean.setName(this.operation.getName());
                bean.setType(this.operation.getType());
                bean.setVmImage(this.operation.getVmImage());
                bean.setCanGoToNextStep(this.canGoToNextStep());
                bean.setCanGoToPreviousStep(this.canGoToPreviousStep());

                // Copy arrays since they've been initialized lazily
                if (this.operation.getServerProgressList() != null) {
                    bean.setServerProgressList(new ArrayList<ServerProgressBean>(this.operation.getServerProgressList()));
                }
                if (this.operation.getWorkerProgressList() != null) {
                    bean.setWorkerProgressList(new ArrayList<WorkerProgressBean>(this.operation.getWorkerProgressList()));
                }
                if (this.operation.getSelectedServers() != null) {
                    bean.setSelectedServers(new ArrayList<ServerBean>(this.operation.getSelectedServers()));
                }
                if (this.operation.getSelectedGroup() != null) {
                    GroupBean groupBean = new GroupBean();
                    groupBean.setId(this.operation.getSelectedGroup().getId());
                    groupBean.setWorkers(new ArrayList<WorkerBean>(this.operation.getSelectedGroup().getWorkers()));
                    groupBean.setName(this.operation.getSelectedGroup().getName());
                    groupBean.setServers(new ArrayList<ServerBean>(this.operation.getSelectedGroup().getServers()));
                    groupBean.setClustered(this.operation.getSelectedGroup().getClustered());
                    bean.setSelectedGroup(groupBean);
                }

                // These calls will contact the servers,
                // call only if absolutely necessary
                if (this.operation.getCurrentStep().equals(Step.SELECT_SERVERS)
                    || this.operation.getCurrentStep().equals(Step.EXECUTING_MAINTENANCE_NO_CLUSTER)
                    || this.operation.getCurrentStep().equals(Step.EXECUTING_MIGRATION_PART2)) {
                    this.refreshActiveSessions();
                }
                if (this.operation.getCurrentStep().equals(Step.SELECT_GROUP)) {
                    if (this.operation.getTopology() != null) {
                        TopologyBean topology = new TopologyBean();
                        topology.setGroups(this.fetchGroups());
                        topology.setId(this.operation.getTopology().getId());
                        bean.setTopology(topology);
                    }
                }
                if (bean.getSelectedGroup() != null) {
                    if (this.operation.getCurrentStep().equals(Step.SELECT_GROUP)
                        || this.operation.getCurrentStep().equals(Step.SELECT_APPLICATION)) {
                        this.fillApplications(bean.getSelectedGroup());
                    }
                    if (this.operation.getCurrentStep().equals(Step.SELECT_VM_IMAGE)
                        || this.operation.getCurrentStep().equals(Step.SELECT_VM_IMAGE_FOR_SERVER)) {
                        this.fillVMImages(bean.getSelectedGroup());
                    }
                }

                return bean;
            }
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @throws JadortServiceException <ul>
     *         <li>if the loadTopology method is called outside the
     *         INITIALIZE_TOPOLOGY step,
     *         <li>if an error is occurred when parsing the .xml file
     *         </ul>
     */
    public void loadTopology(final File xmlTopoFile) throws JadortServiceException {
        synchronized (this) {
            this.refreshOperation();
            this.checkStep(Step.INITIALIZE_TOPOLOGY);

            try {
                this.loadJadortSpecificTopology(xmlTopoFile);
            } catch (UnmarshalException e1) {
                try {
                    this.loadDeployMETopology(xmlTopoFile);
                } catch (UnmarshalException e2) {
                    throw new JadortServiceException(xmlTopoFile.getName()
                        + " is not a valid topology file.\n\nHere are some potentially helpful error messages:\n\t["
                        + JadortServiceImpl.XSD_PATH_JADORT + "] " + e1.getMessage() + "\n\t["
                        + JadortServiceImpl.XSD_PATH_DEPLOYME + "] " + e2.getMessage(), null);
                }
            }

            this.mergeOperation();
        }
    }

    /**
     * @see JadortServiceImpl#loadSchemaAndFile(String, Class, File)
     * @param xmlTopoFile JaDOrT-specific topology XML file to load.
     */
    protected void loadJadortSpecificTopology(final File xmlTopoFile) throws UnmarshalException, JadortServiceException {
        org.ow2.jasmine.jadort.service.topology.xml.Topology xmlTopology = this.loadSchemaAndFile(
            JadortServiceImpl.XSD_PATH_JADORT, org.ow2.jasmine.jadort.service.topology.xml.Topology.class, xmlTopoFile);

        TopologyBean topologyBean = new TopologyBean();
        this.operation.setTopology(topologyBean);

        Map<String, Worker> workerXMLs = new HashMap<String, Worker>();
        for (Worker workerXML : xmlTopology.getWorker()) {
            workerXMLs.put(workerXML.getName(), workerXML);
        }

        Map<String, VMM> vmmXMLs = new HashMap<String, VMM>();
        for (VMM vmmXML : xmlTopology.getVMM()) {
            vmmXMLs.put(vmmXML.getName(), vmmXML);
        }

        List<GroupBean> groups = new ArrayList<GroupBean>();
        for (Group groupXML : xmlTopology.getGroup()) {
            GroupBean groupBean = new GroupBean();
            groupBean.setName(groupXML.getName());
            groupBean.setClustered(groupXML.isClustered());

            List<ServerBean> servers = new ArrayList<ServerBean>();
            List<WorkerBean> workers = new ArrayList<WorkerBean>();
            boolean atLeastOneServerHasVM = false;
            String VMMName = "";
            for (Server serverXML : groupXML.getServer()) {
                ServerBean serverBean = new ServerBean();
                serverBean.setName(serverXML.getName());
                if (serverXML.getCapacity() != null) {
                    serverBean.setCapacity(serverXML.getCapacity());
                }
                serverBean.setType(ServerBean.Type.valueOf(serverXML.getType().name()));

                serverBean.setServerConnector(this.createJMXConnectorBean(serverXML.getURLConnector()));
                if (serverXML.getManager() != null) {
                    serverBean.setManagerConnector(this.createJMXConnectorBean(serverXML.getManager().getURLConnector()));
                }

                Target targetXML = serverXML.getTarget();
                ServerBean target = null;
                if (targetXML != null) {
                    target = new ServerBean();
                    target.setName(targetXML.getName());
                    target.setType(ServerBean.Type.valueOf(targetXML.getType().name()));

                    target.setServerConnector(this.createJMXConnectorBean(targetXML.getURLConnector()));
                    if (targetXML.getManager() != null) {
                        target.setManagerConnector(this.createJMXConnectorBean(targetXML.getManager().getURLConnector()));
                    }

                    serverBean.setTarget(target);
                }

                List<Balancer> balancerXMLs = serverXML.getBalancer();
                if (balancerXMLs != null) {
                    for (Balancer balancerXML : balancerXMLs) {
                        Worker workerXML = workerXMLs.get(balancerXML.getWorker());
                        if (workerXML == null) {
                            throw new JadortServiceException("There is no worker named \"" + balancerXML.getWorker()
                                + "\", please check your topology file.", null);
                        }
                        WorkerBean workerBean = new WorkerBean();
                        workerBean.setName(balancerXML.getServer());
                        workerBean.setWorkerName(workerXML.getName());
                        workerBean.setType(WorkerBean.Type.valueOf(workerXML.getType().name()));
                        workerBean.setServerName(balancerXML.getServer());
                        workerBean.setConnector(this.createJMXConnectorBean(workerXML.getURLConnector()));
                        workerBean.setServer(serverBean);
                        workers.add(workerBean);
                    }
                }

                VM vmXML = serverXML.getVM();
                if (vmXML != null) {
                    atLeastOneServerHasVM = true;
                    VMM vmmXML = vmmXMLs.get(vmXML.getVmm());
                    if (vmmXML != null) {
                        VMBean vmBean = new VMBean();
                        vmBean.setName(vmXML.getName());
                        vmBean.setVmm(vmXML.getVmm());
                        vmBean.setConnector(this.createJMXConnectorBean(vmmXML.getURLConnector()));
                        serverBean.setVm(vmBean);
                        VMMName = vmmXML.getName();
                    } else {
                        throw new JadortServiceException("VMM not defined" + "\", please check your topology file.", null);
                    }
                }
                servers.add(serverBean);
            }
            for (ServerBean server : servers) {
                if ((atLeastOneServerHasVM && server.getVm() == null) || (!atLeastOneServerHasVM && server.getVm() != null)) {
                    throw new JadortServiceException("All servers in a group must have a VM or no server has one"
                        + "\", please check your topology file.", null);
                }
                if (server.getVm() != null && !server.getVm().getVmm().equals(VMMName)) {
                    throw new JadortServiceException("All servers in a group must have the same VMM"
                        + "\", please check your topology file.", null);
                }
            }
            groupBean.setServers(servers);
            groupBean.setWorkers(workers);

            groups.add(groupBean);
        }
        topologyBean.setGroups(groups);
    }

    /**
     * @see JadortServiceImpl#loadSchemaAndFile(String, Class, File)
     * @param xmlTopoFile JASMINe Deploy ME topology XML file to load.
     */
    protected void loadDeployMETopology(final File xmlTopoFile) throws UnmarshalException, JadortServiceException {
        org.ow2.jasmine.deployme.generated.Topology xmlTopology = this.loadSchemaAndFile(JadortServiceImpl.XSD_PATH_DEPLOYME,
            org.ow2.jasmine.deployme.generated.Topology.class, xmlTopoFile);

        TopologyBean topologyBean = new TopologyBean();
        this.operation.setTopology(topologyBean);

        List<GroupBean> groups = new ArrayList<GroupBean>();
        for (Domains.Domain domain : xmlTopology.getDomains().getDomain()) {
            Configuration conf = domain.getConfiguration();
            String defaultHost = null;
            int defaultPort = 0;
            Protocols protocols = null;

            if (conf != null) {
                if (conf.getGlobalJonas() != null) {
                    defaultHost = conf.getGlobalJonas().getHost();
                }
                protocols = conf.getProtocols();
            }
            if (defaultHost == null) {
                defaultHost = "localhost";
            }
            String defaultProtocol = null;
            if (protocols != null) {
                defaultProtocol = protocols.getProtocolsList();
                BigInteger jrmpPort = protocols.getJrmpPort();
                if (jrmpPort != null) {
                    defaultPort = jrmpPort.intValue();
                }
            }
            if (defaultProtocol != null) {
                // Take the first protocol of the list
                defaultProtocol = defaultProtocol.split(",")[0];
            } else {
                defaultProtocol = "jrmp";
            }

            GroupBean groupBean = new GroupBean();
            groupBean.setName(domain.getName());

            boolean clustered = false;
            try {
                if (conf.getActiveServices().getWeb().isHttpReplicationActivated()) {
                    clustered = true;
                } else {
                    clustered = false;
                }
            } catch (NullPointerException ignored) {
                // If NPE, then there's no HttpReplicationConf
            }

            List<ServerBean> servers = new ArrayList<ServerBean>();
            for (Servers.Server server : domain.getServers().getServer()) {
                conf = server.getConfiguration();
                String host = null;
                if (conf != null) {
                    if (conf.getGlobalJonas() != null) {
                        host = conf.getGlobalJonas().getHost();
                    }
                    protocols = conf.getProtocols();
                }
                if (host == null) {
                    host = defaultHost;
                }
                String name = server.getName();
                int port = defaultPort;
                String protocol = null;
                if (protocols != null) {
                    protocol = protocols.getProtocolsList();
                    BigInteger jrmpPort = protocols.getJrmpPort();
                    if (jrmpPort != null) {
                        port = jrmpPort.intValue();
                    }
                }
                if (protocol != null) {
                    // Take the first protocol of the list
                    protocol = protocol.split(",")[0];
                } else {
                    protocol = defaultProtocol;
                }
                String transport;
                if (protocol.equals("irmi")) {
                    transport = "rmi";
                    if (port == 0) {
                        port = 1098;
                    }
                } else if (protocol.equals("jrmp")) {
                    transport = "rmi";
                    if (port == 0) {
                        port = 1099;
                    }
                } else if (protocol.equals("iiop")) {
                    transport = "iiop";
                    if (port == 0) {
                        port = 2001;
                    }
                } else {
                    throw new JadortServiceException("Unknown protocol \"" + protocol + "\" for server \"" + name + "\".", null);
                }

                String jmxURL = "service:jmx:" + transport + "://" + host + "/jndi/" + transport + "://" + host + ":" + port
                    + "/" + protocol + "connector_" + name;

                ConnectorBean connector = new ConnectorBean();
                connector.setConnectorUrl(jmxURL);
                ServerBean serverBean = new ServerBean();
                serverBean.setName(name);
                serverBean.setServerConnector(connector);
                if (JadortServiceImpl.DUMMY) {
                    serverBean.setType(Type.DUMMY);
                } else {
                    serverBean.setType(Type.JONAS);
                }
                servers.add(serverBean);

                try {
                    if (conf.getActiveServices().getWeb().isHttpReplicationActivated()) {
                        clustered = true;
                    } else {
                        clustered = false;
                    }
                } catch (NullPointerException ignored) {
                    // If NPE, then there's no HttpReplicationConf
                }
            }
            if (domain.getClusterDaemons() != null) {
                for (ClusterDaemons.ClusterDaemon cd : domain.getClusterDaemons().getClusterDaemon()) {
                    int nb = 1;
                    String prefix = cd.getServerNamePrefix();
                    for (String serverName : cd.getServers().getServerName()) {
                        boolean found = false;
                        for (ServerBean s : servers) {
                            if (s.getName().equals(serverName)) {
                                s.setName(prefix + nb);
                                s.getServerConnector().setConnectorUrl(
                                    s.getServerConnector().getConnectorUrl().replace("connector_" + serverName,
                                        "connector_" + s.getName()));

                                nb++;
                                String protocol = cd.getClusterDaemonProtocol();
                                if (protocol != null) {
                                    // Take the first protocol of the list
                                    protocol = protocol.split(",")[0];
                                } else {
                                    protocol = defaultProtocol;
                                }
                                int port = defaultPort;
                                Integer portInteger = cd.getClusterDaemonPort();
                                if (portInteger != null) {
                                    port = portInteger.intValue();
                                }
                                String transport;
                                if (protocol.equals("irmi")) {
                                    transport = "rmi";
                                    if (port == 0) {
                                        port = 1098;
                                    }
                                } else if (protocol.equals("jrmp")) {
                                    transport = "rmi";
                                    if (port == 0) {
                                        port = 1099;
                                    }
                                } else if (protocol.equals("iiop")) {
                                    transport = "iiop";
                                    if (port == 0) {
                                        port = 2001;
                                    }
                                } else {
                                    throw new JadortServiceException("Unknown protocol \"" + protocol
                                        + "\" for cluster daemon \"" + cd.getName() + "\".", null);
                                }

                                String serverURL = s.getServerConnector().getConnectorUrl();
                                int hostStart = serverURL.indexOf("://") + 3;
                                String host = serverURL.substring(hostStart, serverURL.indexOf('/', hostStart));
                                String jmxURL = "service:jmx:" + transport + "://" + host + "/jndi/" + transport + "://" + host
                                    + ":" + port + "/" + protocol + "connector_" + cd.getName();
                                ConnectorBean manager = new ConnectorBean();
                                manager.setConnectorUrl(jmxURL);
                                s.setManagerConnector(manager);

                                found = true;
                                break;
                            }
                        }

                        if (!found) {
                            throw new JadortServiceException("Cannot find server \"" + serverName + "\" for cluster daemon \""
                                + cd.getName() + "\".", null);
                        }
                    }
                }
            }
            groupBean.setServers(servers);
            groupBean.setClustered(clustered);

            groups.add(groupBean);
        }
        topologyBean.setGroups(groups);
    }

    /**
     * Verifies an XML file against an XSD and instantiates it using a given
     * class.
     * 
     * @param xsdPath XSD file path.
     * @param rootClass Root class used for instantiating JAXB.
     * @param xmlFile XML to load.
     * @return XML loaded using JAXB and the rootClass.
     * @throws JadortServiceException If loading fails.
     * @throws UnmarshalException If parsing fails (file doesn't correspond to
     *         XSD).
     */
    @SuppressWarnings("unchecked")
    protected <T> T loadSchemaAndFile(final String xsdPath, final Class<T> rootClass, final File xmlFile)
        throws JadortServiceException, UnmarshalException {
        try {
            JAXBContext jc = JAXBContext.newInstance(rootClass.getPackage().getName());
            Unmarshaller unMarshaller = jc.createUnmarshaller();

            URL xsdURL = this.getClass().getClassLoader().getResource(xsdPath);
            SchemaFactory schemaFactory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
            Schema schema = schemaFactory.newSchema(xsdURL);
            unMarshaller.setSchema(schema);

            return (T) unMarshaller.unmarshal(xmlFile);
        } catch (JAXBException e) {
            if (e instanceof UnmarshalException) {
                UnmarshalException ue = (UnmarshalException) e;
                if (ue.getLinkedException() != null) {
                    throw new UnmarshalException(ue.getLinkedException().getMessage(), e);
                } else {
                    throw ue;
                }
            } else {
                throw new JadortServiceException("Error creating the topology parser: " + e.getMessage(), e);
            }
        } catch (SAXException e) {
            throw new JadortServiceException("Error creating the topology parser: " + e.getMessage(), e);
        }
    }

    /**
     * @param urlConnector URLConnector for which to create a JMXConnector bean.
     * @return JMXConnector bean for the given urlConnector.
     */
    protected ConnectorBean createJMXConnectorBean(final URLConnector urlConnector) {
        ConnectorBean jmxConnectorBean = new ConnectorBean();

        jmxConnectorBean.setConnectorUrl(urlConnector.getURL());
        if (urlConnector.getUsername() != null && urlConnector.getPassword() != null) {
            jmxConnectorBean.setUsername(urlConnector.getUsername());
            jmxConnectorBean.setPassword(urlConnector.getPassword());
        }

        return jmxConnectorBean;
    }

    /**
     * Returns the list of groups obtained from the loaded topology in the
     * current operation.(returns a copy since
     * operation.getTopology().getGroups() does lazy)
     * 
     * @return The list of groups
     * @throws JadortServiceException If getting the list of applications fails.
     */
    private List<GroupBean> fetchGroups() 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());
                this.fillApplications(groupBeanCopy);
                result.add(groupBeanCopy);
            }
            return result;
        } else {
            return null;
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @throws JadortServiceException if the selectedGroup method can not be
     *         called in the current step. (this method can be called only in
     *         SELECT_GROUP step)
     */
    public void selectGroup(final GroupBean selectedGroup) throws JadortServiceException {
        synchronized (this) {
            this.refreshOperation();
            this.checkStep(Step.SELECT_GROUP);

            for (GroupBean group : this.operation.getTopology().getGroups()) {
                if (group.getId().equals(selectedGroup.getId())) {
                    this.operation.setSelectedGroup(group);
                    this.em.flush();
                    return;
                }
            }

            throw new JadortServiceException("GroupBean \"" + selectedGroup + "\" not found", null);
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @throws JadortServiceException if the selectedGroup method can not be
     *         called in the current step. (this method can be called only in
     *         SELECT_OPERATION_TYPE step)
     */
    public void selectOperationType(final OperationType operationType) throws JadortServiceException {
        synchronized (this) {
            this.refreshOperation();
            this.checkStep(Step.SELECT_OPERATION_TYPE);

            if (OperationType.MAINTAIN.equals(operationType)) {
                Set<ServerProgressBean> oldServerProgressList = new HashSet<ServerProgressBean>();
                Set<WorkerProgressBean> oldWorkerProgressList = new HashSet<WorkerProgressBean>();
                if (this.operation.getServerProgressList() != null) {
                    oldServerProgressList.addAll(this.operation.getServerProgressList());
                }
                if (this.operation.getAllServerProgressList() != null) {
                    oldServerProgressList.addAll(this.operation.getAllServerProgressList());
                }
                if (this.operation.getWorkerProgressList() != null) {
                    oldWorkerProgressList.addAll(this.operation.getWorkerProgressList());
                }
                if (this.operation.getAllWorkerProgressList() != null) {
                    oldWorkerProgressList.addAll(this.operation.getAllWorkerProgressList());
                }

                this.operation.setServerProgressList(null);
                this.operation.setWorkerProgressList(null);
                this.operation.setAllServerProgressList(null);
                this.operation.setAllWorkerProgressList(null);

                this.operation.setType(IJadortService.OperationType.MAINTAIN);

                this.mergeOperation();

                for (ServerProgressBean serverProgressBean : oldServerProgressList) {
                    this.em.remove(serverProgressBean);
                }
                for (WorkerProgressBean workerProgressBean : oldWorkerProgressList) {
                    this.em.remove(workerProgressBean);
                }
                this.em.flush();
            } else if (OperationType.MIGRATE.equals(operationType)) {
                // Copy the oldServerProgressList array to avoid
                // LazyInitializationException after doing em.remove
                List<ServerProgressBean> oldServerProgressList = null;
                if (this.operation.getServerProgressList() != null) {
                    oldServerProgressList = new ArrayList<ServerProgressBean>(this.operation.getServerProgressList());
                }

                List<ServerProgressBean> serverProgressList = new ArrayList<ServerProgressBean>();
                for (ServerBean server : this.operation.getSelectedGroup().getServers()) {
                    ServerProgressBean serverProgressBean = new ServerProgressBean(server);
                    serverProgressList.add(serverProgressBean);
                    ServerAction action = ServerAction.getServerAction(server);
                    boolean canDeployApplications;
                    try {
                        canDeployApplications = action.canDeployApplications();
                    } catch (Exception e) {
                        throw new JadortServiceException("Cannot check if server '" + server + "' can deploy applications: "
                            + e.getMessage(), e);
                    }
                    if (!canDeployApplications) {
                        throw new JadortServiceException(
                            "This version of JaDOrT doesn't support the deployment of applications on the server '"
                                + server.getName() + "'. Please use the \"Maintenance\" functions instead.", null);
                    }
                }
                this.operation.setServerProgressList(serverProgressList);
                this.operation.setType(IJadortService.OperationType.MIGRATE);

                this.mergeOperation();

                if (oldServerProgressList != null) {
                    for (ServerProgressBean serverProgressBean : oldServerProgressList) {
                        this.em.remove(serverProgressBean);
                    }
                    this.em.flush();
                }
            } else {
                throw new IllegalStateException("Unknown operation type: " + operationType);
            }
        }
    }

    /**
     * Refreshes the number of active sessions in the current operation:
     * <ul>
     * <li>If migrating, gets the number of active sessions on the old version.
     * If no old version, sets the number of active sessions to 0.</li>
     * <li>If maintaining, gets the number of active sessions on all
     * applications on the server.</li>
     * </ul>
     * Note that this doesn't merge with the current operation in DB.
     */
    private 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.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 if (type.equals(OperationType.MAINTAIN)) {
                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 {
                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
     */
    private 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
     */
    private 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);
    }

    /**
     * This function allows to execute the next sub step in the migration
     * execution step
     */
    private void nextExecuteMigration() {
        ServerProgressState aimedServerProgressState = this.operation.getAimedServerProgressState();
        WorkerProgressState aimedWorkerProgressState = this.operation.getAimedWorkerProgressState();

        if (this.operation.getType().equals(OperationType.MAINTAIN)) {
            if (this.operation.getSelectedGroup().getClustered()) {
                if (aimedWorkerProgressState.equals(WorkerProgressState.INITIAL)) {
                    this.operation.setAimedWorkerProgressState(WorkerProgressState.STOP_OK);
                    this.operation.setAimedProgressPercent(20);
                } else if (aimedServerProgressState.equals(ServerProgressState.INITIAL)) {
                    this.operation.setAimedServerProgressState(ServerProgressState.STOP_OK);
                    this.operation.setAimedProgressPercent(40);
                } else if (aimedServerProgressState.equals(ServerProgressState.STOP_OK)) {
                    this.operation.setAimedServerProgressState(ServerProgressState.MAINTAIN_OK);
                    this.operation.setAimedProgressPercent(60);
                } else if (aimedServerProgressState.equals(ServerProgressState.MAINTAIN_OK)) {
                    this.operation.setAimedServerProgressState(ServerProgressState.START_OK);
                    this.operation.setAimedProgressPercent(80);
                } else if (aimedWorkerProgressState.equals(WorkerProgressState.STOP_OK)) {
                    this.operation.setAimedWorkerProgressState(WorkerProgressState.START_OK);
                    this.operation.setAimedProgressPercent(100);
                } else if (aimedServerProgressState.equals(ServerProgressState.START_OK)) {
                    this.operation.setAimedServerProgressState(ServerProgressState.DESTROY_VM_HOSTS_OK);
                    this.operation.setAimedProgressPercent(100);
                } else {
                    throw new IllegalStateException("Unknown ServerProgressState or WorkerProgressState: "
                        + aimedServerProgressState + ", " + aimedWorkerProgressState);
                }
            } else {
                if (aimedWorkerProgressState.equals(WorkerProgressState.INITIAL)) {
                    this.operation.setAimedWorkerProgressState(WorkerProgressState.STOP_OK);
                    this.operation.setAimedProgressPercent(16);
                } else if (aimedServerProgressState.equals(ServerProgressState.INITIAL)) {
                    this.operation.setAimedServerProgressState(ServerProgressState.DISABLE_APPLICATIONS_OK);
                    this.operation.setAimedProgressPercent(33);
                } else if (aimedServerProgressState.equals(ServerProgressState.DISABLE_APPLICATIONS_OK)) {
                    this.operation.setAimedServerProgressState(ServerProgressState.STOP_OK);
                    this.operation.setAimedProgressPercent(50);
                } else if (aimedServerProgressState.equals(ServerProgressState.STOP_OK)) {
                    this.operation.setAimedServerProgressState(ServerProgressState.MAINTAIN_OK);
                    this.operation.setAimedProgressPercent(66);
                } else if (aimedServerProgressState.equals(ServerProgressState.MAINTAIN_OK)) {
                    this.operation.setAimedServerProgressState(ServerProgressState.START_OK);
                    this.operation.setAimedProgressPercent(83);
                } else if (aimedWorkerProgressState.equals(WorkerProgressState.STOP_OK)) {
                    this.operation.setAimedWorkerProgressState(WorkerProgressState.START_OK);
                    this.operation.setAimedProgressPercent(100);
                } else if (aimedServerProgressState.equals(ServerProgressState.START_OK)) {
                    this.operation.setAimedServerProgressState(ServerProgressState.DESTROY_VM_HOSTS_OK);
                    this.operation.setAimedProgressPercent(100);
                } else {
                    throw new IllegalStateException("Unknown ServerProgressState or WorkerProgressState: "
                        + aimedServerProgressState + ", " + aimedWorkerProgressState);
                }
            }
        } else if (this.operation.getType().equals(OperationType.MIGRATE)) {
            if (aimedServerProgressState.equals(ServerProgressState.INITIAL)) {
                this.operation.setAimedServerProgressState(ServerProgressState.UPLOAD_OK);
                this.operation.setAimedProgressPercent(33);
            } else if (aimedServerProgressState.equals(ServerProgressState.UPLOAD_OK)) {
                this.operation.setAimedServerProgressState(ServerProgressState.DEPLOY_OK);
                this.operation.setAimedProgressPercent(66);
            } else if (aimedServerProgressState.equals(ServerProgressState.DEPLOY_OK)) {
                this.operation.setAimedServerProgressState(ServerProgressState.SET_DEFAULT_OK);
                this.operation.setAimedProgressPercent(100);
            } else if (aimedServerProgressState.equals(ServerProgressState.SET_DEFAULT_OK)) {
                this.operation.setAimedServerProgressState(ServerProgressState.UNDEPLOY_OK);
                this.operation.setAimedProgressPercent(50);
            } else if (aimedServerProgressState.equals(ServerProgressState.UNDEPLOY_OK)) {
                this.operation.setAimedServerProgressState(ServerProgressState.ERASE_OK);
                this.operation.setAimedProgressPercent(100);
            } else {
                throw new IllegalStateException("Unknown ServerProgressState: " + aimedServerProgressState);
            }
        } else {
            throw new IllegalStateException("Unknown OperationType: " + this.operation.getType());
        }

        if (this.operation.getServerProgressList() != null) {
            for (ServerProgressBean serverProgress : this.operation.getServerProgressList()) {
                if (serverProgress.getProgressState().equals(this.operation.getAimedServerProgressState())) {
                    serverProgress.setActionState(ActionState.WAITING);
                    serverProgress.setProgress(this.operation.getAimedProgressPercent());
                } else {
                    serverProgress.setActionState(ActionState.RUNNING);
                }
            }
        }
        if (this.operation.getWorkerProgressList() != null) {
            for (WorkerProgressBean workerProgress : this.operation.getWorkerProgressList()) {
                if (workerProgress.getProgressState().equals(this.operation.getAimedWorkerProgressState())) {
                    workerProgress.setActionState(ActionState.WAITING);
                    workerProgress.setProgress(this.operation.getAimedProgressPercent());
                } else {
                    workerProgress.setActionState(ActionState.RUNNING);
                }
            }
        }

        this.mergeOperation();

        if (this.operation.getServerProgressList() != null) {
            for (ServerProgressBean serverProgress : this.operation.getServerProgressList()) {
                if (serverProgress.getActionState().equals(ActionState.RUNNING)) {
                    this.reachAimedServerProgressState(serverProgress);
                }
            }
        }
        if (this.operation.getWorkerProgressList() != null) {
            for (WorkerProgressBean workerProgress : this.operation.getWorkerProgressList()) {
                if (workerProgress.getActionState().equals(ActionState.RUNNING)) {
                    this.reachAimedWorkerProgressState(workerProgress);
                }
            }
        }
    }

    /**
     * this function allows to execute the previous sub step in the migration
     * execution step
     */
    private void previousExecuteMigration() {
        ServerProgressState aimedServerProgressState = this.operation.getAimedServerProgressState();
        WorkerProgressState aimedWorkerProgressState = this.operation.getAimedWorkerProgressState();

        if (this.operation.getType().equals(OperationType.MAINTAIN)) {
            if (this.operation.getSelectedGroup().getClustered()) {
                if (aimedWorkerProgressState.equals(WorkerProgressState.START_OK)) {
                    this.operation.setAimedWorkerProgressState(WorkerProgressState.STOP_OK);
                    this.operation.setAimedProgressPercent(80);
                } else if (aimedServerProgressState.equals(ServerProgressState.START_OK)) {
                    this.operation.setAimedServerProgressState(ServerProgressState.MAINTAIN_OK);
                    this.operation.setAimedProgressPercent(60);
                } else if (aimedServerProgressState.equals(ServerProgressState.MAINTAIN_OK)) {
                    this.operation.setAimedServerProgressState(ServerProgressState.STOP_OK);
                    this.operation.setAimedProgressPercent(40);
                } else if (aimedServerProgressState.equals(ServerProgressState.STOP_OK)) {
                    this.operation.setAimedServerProgressState(ServerProgressState.INITIAL);
                    this.operation.setAimedProgressPercent(20);
                } else if (aimedWorkerProgressState.equals(WorkerProgressState.STOP_OK)) {
                    this.operation.setAimedWorkerProgressState(WorkerProgressState.INITIAL);
                    this.operation.setAimedProgressPercent(0);
                } else {
                    throw new IllegalStateException("Unknown ServerProgressState or WorkerProgressState: "
                        + aimedServerProgressState + ", " + aimedWorkerProgressState);
                }
            } else {
                if (aimedWorkerProgressState.equals(WorkerProgressState.START_OK)) {
                    this.operation.setAimedWorkerProgressState(WorkerProgressState.STOP_OK);
                    this.operation.setAimedProgressPercent(83);
                } else if (aimedServerProgressState.equals(ServerProgressState.START_OK)) {
                    this.operation.setAimedServerProgressState(ServerProgressState.MAINTAIN_OK);
                    this.operation.setAimedProgressPercent(66);
                } else if (aimedServerProgressState.equals(ServerProgressState.MAINTAIN_OK)) {
                    this.operation.setAimedServerProgressState(ServerProgressState.STOP_OK);
                    this.operation.setAimedProgressPercent(50);
                } else if (aimedServerProgressState.equals(ServerProgressState.STOP_OK)) {
                    this.operation.setAimedServerProgressState(ServerProgressState.DISABLE_APPLICATIONS_OK);
                    this.operation.setAimedProgressPercent(33);
                } else if (aimedServerProgressState.equals(ServerProgressState.DISABLE_APPLICATIONS_OK)) {
                    this.operation.setAimedServerProgressState(ServerProgressState.INITIAL);
                    this.operation.setAimedProgressPercent(16);
                } else if (aimedWorkerProgressState.equals(WorkerProgressState.STOP_OK)) {
                    this.operation.setAimedWorkerProgressState(WorkerProgressState.INITIAL);
                    this.operation.setAimedProgressPercent(0);
                } else {
                    throw new IllegalStateException("Unknown ServerProgressState or WorkerProgressState: "
                        + aimedServerProgressState + ", " + aimedWorkerProgressState);
                }
            }
        } else if (this.operation.getType().equals(OperationType.MIGRATE)) {
            if (aimedServerProgressState.equals(ServerProgressState.UPLOAD_OK)) {
                this.operation.setAimedServerProgressState(ServerProgressState.INITIAL);
                this.operation.setAimedProgressPercent(0);
            } else if (aimedServerProgressState.equals(ServerProgressState.DEPLOY_OK)) {
                this.operation.setAimedServerProgressState(ServerProgressState.UPLOAD_OK);
                this.operation.setAimedProgressPercent(33);
            } else if (aimedServerProgressState.equals(ServerProgressState.SET_DEFAULT_OK)) {
                this.operation.setAimedServerProgressState(ServerProgressState.DEPLOY_OK);
                this.operation.setAimedProgressPercent(66);
            } else if (aimedServerProgressState.equals(ServerProgressState.UNDEPLOY_OK)) {
                this.operation.setAimedServerProgressState(ServerProgressState.SET_DEFAULT_OK);
                this.operation.setAimedProgressPercent(0);
            } else {
                throw new IllegalStateException("Unknown ServerProgressState: " + aimedServerProgressState);
            }
        } else {
            throw new IllegalStateException("Unknown OperationType: " + this.operation.getType());
        }

        if (this.operation.getServerProgressList() != null) {
            for (ServerProgressBean serverProgress : this.operation.getServerProgressList()) {
                if (serverProgress.getProgressState().equals(this.operation.getAimedServerProgressState())) {
                    serverProgress.setActionState(ActionState.WAITING);
                    serverProgress.setProgress(this.operation.getAimedProgressPercent());
                } else {
                    serverProgress.setActionState(ActionState.RUNNING);
                }
            }
        }
        if (this.operation.getWorkerProgressList() != null) {
            for (WorkerProgressBean workerProgress : this.operation.getWorkerProgressList()) {
                if (workerProgress.getProgressState().equals(this.operation.getAimedWorkerProgressState())) {
                    workerProgress.setActionState(ActionState.WAITING);
                    workerProgress.setProgress(this.operation.getAimedProgressPercent());
                } else {
                    workerProgress.setActionState(ActionState.RUNNING);
                }
            }
        }

        this.mergeOperation();

        if (this.operation.getServerProgressList() != null) {
            for (ServerProgressBean serverProgress : this.operation.getServerProgressList()) {
                if (serverProgress.getActionState().equals(ActionState.RUNNING)) {
                    this.reachAimedServerProgressState(serverProgress);
                }
            }
        }
        if (this.operation.getWorkerProgressList() != null) {
            for (WorkerProgressBean workerProgress : this.operation.getWorkerProgressList()) {
                if (workerProgress.getActionState().equals(ActionState.RUNNING)) {
                    this.reachAimedWorkerProgressState(workerProgress);
                }
            }
        }
    }

    /**
     * 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.
     */
    private 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) {
                                this.stopVM(serverProgress, serverProgress.getOldDeploymentItem());
                            } else {
                                this.stopServer(serverProgress);
                            }
                            return;
                        }
                        // no previous
                    } else {
                        if (aimedServerProgressState.equals(ServerProgressState.DISABLE_APPLICATIONS_OK)) {
                            // next
                            this.disableApplications(serverProgress);
                            return;
                        }
                        // no previous
                    }
                } else if (currentServerProgressState.equals(ServerProgressState.DISABLE_APPLICATIONS_OK)) {
                    if (aimedServerProgressState.equals(ServerProgressState.STOP_OK)) {
                        // next
                        if (serverProgress.getServer().getVm() != null) {
                            this.stopVM(serverProgress, serverProgress.getOldDeploymentItem());
                        } else {
                            this.stopServer(serverProgress);
                        }
                        return;
                    } else if (aimedServerProgressState.equals(ServerProgressState.INITIAL)) {
                        // previous
                        this.enableApplications(serverProgress);
                        return;
                    }
                } else if (currentServerProgressState.equals(ServerProgressState.STOP_OK)) {
                    if (aimedServerProgressState.equals(ServerProgressState.MAINTAIN_OK)) {
                        // next
                        if (serverProgress.getServer().getVm() != null) {
                            this.createVM(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) {
                            this.startVM(serverProgress, serverProgress.getOldDeploymentItem());
                        } else {
                            this.startServer(serverProgress);
                        }
                        return;
                    } else if (aimedServerProgressState.equals(ServerProgressState.DISABLE_APPLICATIONS_OK)) {
                        // previous
                        if (serverProgress.getServer().getVm() != null) {
                            this.startVM(serverProgress, serverProgress.getOldDeploymentItem());
                        } else {
                            this.startServer(serverProgress);
                        }
                        return;
                    }
                } else if (currentServerProgressState.equals(ServerProgressState.MAINTAIN_OK)) {
                    if (aimedServerProgressState.equals(ServerProgressState.START_OK)) {
                        // next
                        if (serverProgress.getServer().getVm() != null) {
                            this.startVM(serverProgress, serverProgress.getNewDeploymentItem());
                        } else {
                            this.startServer(serverProgress);
                        }
                        return;
                    } else if (aimedServerProgressState.equals(ServerProgressState.STOP_OK)) {
                        // previous
                        if (serverProgress.getServer().getVm() != null) {
                            this.destroyVM(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) {
                            this.stopVM(serverProgress, serverProgress.getNewDeploymentItem());
                        } else {
                            this.stopServer(serverProgress);
                        }
                        return;
                    } else if (aimedServerProgressState.equals(ServerProgressState.DESTROY_VM_HOSTS_OK)) {
                        this.destroyVM(serverProgress, serverProgress.getOldDeploymentItem(), true);
                        return;
                    }
                } else {
                    throw new IllegalStateException("Unknown ServerProgressState: " + aimedServerProgressState);
                }
            } else if (type.equals(OperationType.MIGRATE)) {
                if (currentServerProgressState.equals(ServerProgressState.INITIAL)) {
                    if (aimedServerProgressState.equals(ServerProgressState.UPLOAD_OK)) {
                        // next
                        this.upload(serverProgress, this.operation.getApplication());
                        return;
                    }
                    // no previous
                } else if (currentServerProgressState.equals(ServerProgressState.UPLOAD_OK)) {
                    if (aimedServerProgressState.equals(ServerProgressState.DEPLOY_OK)) {
                        // next
                        this.deploy(serverProgress, serverProgress.getNewDeploymentItem());
                        return;
                    } else if (aimedServerProgressState.equals(ServerProgressState.INITIAL)) {
                        // previous
                        this.erase(serverProgress, serverProgress.getNewDeploymentItem());
                        return;
                    }
                } else if (currentServerProgressState.equals(ServerProgressState.DEPLOY_OK)) {
                    if (aimedServerProgressState.equals(ServerProgressState.SET_DEFAULT_OK)) {
                        // next
                        this.setAsDefault(serverProgress, serverProgress.getNewDeploymentItem());
                        return;
                    } else if (aimedServerProgressState.equals(ServerProgressState.UPLOAD_OK)) {
                        // previous
                        this.undeploy(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 {
                            this.undeploy(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 {
                            this.setAsDefault(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 {
                            this.erase(serverProgress, serverProgress.getOldDeploymentItem());
                        }
                        return;
                    } else if (aimedServerProgressState.equals(ServerProgressState.SET_DEFAULT_OK)) {
                        // previous
                        this.deploy(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.
     */
    private 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
                        this.stopWorker(workerProgress);
                        return;
                    }
                    // no previous
                } else if (currentWorkerProgressState.equals(WorkerProgressState.STOP_OK)) {
                    if (aimedWorkerProgressState.equals(WorkerProgressState.START_OK)) {
                        // next
                        this.startWorker(workerProgress);
                        return;
                    } else if (aimedWorkerProgressState.equals(WorkerProgressState.INITIAL)) {
                        // previous
                        this.startWorker(workerProgress);
                        return;
                    }
                } else if (currentWorkerProgressState.equals(WorkerProgressState.START_OK)) {
                    if (aimedWorkerProgressState.equals(WorkerProgressState.STOP_OK)) {
                        // previous
                        this.stopWorker(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;
    }

    /**
     * this function allows to upload an application on a server
     * 
     * @param serverProgress the serverProgress server on which the application
     *        will be uploaded.
     * @param application the application to be uploaded.
     * @throws JMSException
     */
    private void upload(final ServerProgressBean serverProgress, final ApplicationBean application) throws JMSException {
        Message message = this.session.createMessage();
        message.setStringProperty(JadortServiceImpl.OPERATION_TYPE, OperationType.MIGRATE.toString());
        message.setStringProperty(JadortServiceImpl.ACTION_TYPE, ActionType.UPLOAD.toString());
        message.setIntProperty(JadortServiceImpl.SERVER_PROGRESS_ID, serverProgress.getId());
        message.setIntProperty(JadortServiceImpl.AIMED_PROGRESS_PERCENT, this.operation.getAimedProgressPercent());
        message.setStringProperty(JadortServiceImpl.AIMED_STATE, this.operation.getAimedServerProgressState().toString());
        message.setIntProperty(JadortServiceImpl.APPLICATION_ID, application.getId());
        QueueSender sender = this.session.createSender(this.queue);
        sender.send(message);
    }

    /**
     * this function allow to deploy an application on the server
     * 
     * @param serverProgress the serverProgress server on which the application
     *        will be deployed
     * @param applicationName the application name to be deployed
     */
    private void deploy(final ServerProgressBean serverProgress, final String applicationName) throws JMSException,
        JadortServiceException {
        if (applicationName == null) {
            throw new JadortServiceException("The application to deploy is null!", null);
        }

        Message message = this.session.createMessage();
        message.setStringProperty(JadortServiceImpl.OPERATION_TYPE, OperationType.MIGRATE.toString());
        message.setStringProperty(JadortServiceImpl.ACTION_TYPE, ActionType.DEPLOY.toString());
        message.setIntProperty(JadortServiceImpl.SERVER_PROGRESS_ID, serverProgress.getId());
        message.setIntProperty(JadortServiceImpl.AIMED_PROGRESS_PERCENT, this.operation.getAimedProgressPercent());
        message.setStringProperty(JadortServiceImpl.AIMED_STATE, this.operation.getAimedServerProgressState().toString());
        message.setStringProperty(JadortServiceImpl.APPLICATION_NAME, applicationName);
        QueueSender sender = this.session.createSender(this.queue);
        sender.send(message);
    }

    /**
     * This function allows to activate an application on the the server. it
     * makes the application policy to default.
     * 
     * @param serverProgress the serverProgress server on which the new
     *        application will be activated.
     * @param applicationName the application name
     */
    private void setAsDefault(final ServerProgressBean serverProgress, final String applicationName) throws JMSException,
        JadortServiceException {
        if (applicationName == null) {
            throw new JadortServiceException("The application to set as default is null!", null);
        }

        Message message = this.session.createMessage();
        message.setStringProperty(JadortServiceImpl.OPERATION_TYPE, OperationType.MIGRATE.toString());
        message.setStringProperty(JadortServiceImpl.ACTION_TYPE, ActionType.SET_DEFAULT.toString());
        message.setIntProperty(JadortServiceImpl.SERVER_PROGRESS_ID, serverProgress.getId());
        message.setIntProperty(JadortServiceImpl.AIMED_PROGRESS_PERCENT, this.operation.getAimedProgressPercent());
        message.setStringProperty(JadortServiceImpl.AIMED_STATE, this.operation.getAimedServerProgressState().toString());
        message.setStringProperty(JadortServiceImpl.APPLICATION_NAME, applicationName);
        QueueSender sender = this.session.createSender(this.queue);
        sender.send(message);
    }

    /**
     * this function allows to undeploy an application on the server
     * 
     * @param serverProgress the serverProgress server on which the application
     *        will be undeployed
     * @param applicationName the application name to undeploy
     */
    private void undeploy(final ServerProgressBean serverProgress, final String applicationName) throws JMSException,
        JadortServiceException {
        if (applicationName == null) {
            throw new JadortServiceException("The application to undeploy is null!", null);
        }

        Message message = this.session.createMessage();
        message.setStringProperty(JadortServiceImpl.OPERATION_TYPE, OperationType.MIGRATE.toString());
        message.setStringProperty(JadortServiceImpl.ACTION_TYPE, ActionType.UNDEPLOY.toString());
        message.setIntProperty(JadortServiceImpl.SERVER_PROGRESS_ID, serverProgress.getId());
        message.setIntProperty(JadortServiceImpl.AIMED_PROGRESS_PERCENT, this.operation.getAimedProgressPercent());
        message.setStringProperty(JadortServiceImpl.AIMED_STATE, this.operation.getAimedServerProgressState().toString());
        message.setStringProperty(JadortServiceImpl.APPLICATION_NAME, applicationName);
        QueueSender sender = this.session.createSender(this.queue);
        sender.send(message);
    }

    /**
     * this function allow to erase an application on the server
     * 
     * @param serverProgress the serverProgress server on which the new
     *        application will be erased
     * @param applicationName the application name to erase
     */
    private void erase(final ServerProgressBean serverProgress, final String applicationName) throws JMSException,
        JadortServiceException {
        if (applicationName == null) {
            throw new JadortServiceException("The application to erase is null!", null);
        }

        Message message = this.session.createMessage();
        message.setStringProperty(JadortServiceImpl.OPERATION_TYPE, OperationType.MIGRATE.toString());
        message.setStringProperty(JadortServiceImpl.ACTION_TYPE, ActionType.ERASE.toString());
        message.setIntProperty(JadortServiceImpl.SERVER_PROGRESS_ID, serverProgress.getId());
        message.setIntProperty(JadortServiceImpl.AIMED_PROGRESS_PERCENT, this.operation.getAimedProgressPercent());
        message.setStringProperty(JadortServiceImpl.AIMED_STATE, this.operation.getAimedServerProgressState().toString());
        message.setStringProperty(JadortServiceImpl.APPLICATION_NAME, applicationName);
        QueueSender sender = this.session.createSender(this.queue);
        sender.send(message);
    }

    /**
     * {@inheritDoc}
     * 
     * @throws JadortServiceException <ul>
     *         <li>if the restartServermethod can not be called in the current
     *         step. (this method can be called only in
     *         EXECUTING_MIGRATION_PART1, EXECUTING_MIGRATION_PART2,
     *         EXECUTING_MAINTENANCE_CLUSTER, EXECUTING_MAINTENANCE_NO_CLUSTER
     *         and DESTROY_OLD_VM_HOSTS steps)
     *         <li>if the server state is not in error state.
     *         </ul>
     */
    public void restartServer(final ServerBean server) throws JadortServiceException {
        synchronized (this) {
            this.refreshOperation();
            this.checkStep(Step.EXECUTING_MIGRATION_PART1, Step.EXECUTING_MIGRATION_PART2, Step.EXECUTING_MAINTENANCE_CLUSTER,
                Step.EXECUTING_MAINTENANCE_NO_CLUSTER, Step.DESTROY_OLD_VM_HOSTS);

            ServerProgressBean serverProgress = this.getServerProgress(server);
            if (!serverProgress.getActionState().equals(ActionState.FINISHED_ERROR)) {
                throw new JadortServiceException("Server state for server '" + server.getName() + "' is "
                    + serverProgress.getActionState() + ", it should be " + ActionState.FINISHED_ERROR, null);
            }

            serverProgress.setActionState(ActionState.RUNNING);

            this.mergeOperation();

            this.reachAimedServerProgressState(this.getServerProgress(server));
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @throws JadortServiceException <ul>
     *         <li>if the ignoreServer can not be called in the current step.
     *         (this method can be called only in
     *         Step.EXECUTING_MIGRATION_PART1, EXECUTING_MIGRATION_PART2
     *         EXECUTING_MAINTENANCE_CLUSTER and
     *         EXECUTING_MAINTENANCE_NO_CLUSTER steps)
     *         <li>if the server state is not in error state.
     *         </ul>
     */
    public void ignoreServer(final ServerBean server) throws JadortServiceException {
        synchronized (this) {
            this.refreshOperation();
            this.checkStep(Step.EXECUTING_MIGRATION_PART1, Step.EXECUTING_MIGRATION_PART2, Step.EXECUTING_MAINTENANCE_CLUSTER,
                Step.EXECUTING_MAINTENANCE_NO_CLUSTER);

            ServerProgressBean serverProgress = this.getServerProgress(server);
            if (!serverProgress.getActionState().equals(ActionState.FINISHED_ERROR)) {
                throw new JadortServiceException("Server state for server '" + server.getName() + "' is "
                    + serverProgress.getActionState() + ", it should be " + ActionState.FINISHED_ERROR, null);
            }

            serverProgress.appendToLog("Error on the server has been ignored by the user");

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

            this.mergeOperation();
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @throws JadortServiceException If the abortServer can not be called in
     *         the current step. (this method can be called only in
     *         Step.EXECUTING_MIGRATION_PART1, Step.EXECUTING_MIGRATION_PART2,
     *         EXECUTING_MAINTENANCE_CLUSTER,
     *         Step.EXECUTING_MAINTENANCE_NO_CLUSTER steps)
     */
    public void abortServer(final ServerBean server) throws JadortServiceException {
        synchronized (this) {
            this.refreshOperation();
            this.checkStep(Step.EXECUTING_MIGRATION_PART1, Step.EXECUTING_MIGRATION_PART2, Step.EXECUTING_MAINTENANCE_CLUSTER,
                Step.EXECUTING_MAINTENANCE_NO_CLUSTER);

            ServerProgressBean serverProgress = this.getServerProgress(server);
            if (!serverProgress.getActionState().equals(ActionState.RUNNING)) {
                return;
            }

            serverProgress.appendToLog("Action has been aborted by the user");

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

            this.mergeOperation();
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @throws JadortServiceException if the checkServer can not be called in
     *         the current step. (this method can be called only in
     *         EXECUTING_MIGRATION_PART1, EXECUTING_MIGRATION_PART2,
     *         EXECUTING_MAINTENANCE_CLUSTER and
     *         EXECUTING_MAINTENANCE_NO_CLUSTER steps)
     */
    public boolean checkServer(final ServerBean server) throws JadortServiceException {
        synchronized (this) {
            this.refreshOperation();
            this.checkStep(Step.EXECUTING_MIGRATION_PART1, Step.EXECUTING_MIGRATION_PART2, Step.EXECUTING_MAINTENANCE_CLUSTER,
                Step.EXECUTING_MAINTENANCE_NO_CLUSTER, Step.DESTROY_OLD_VM_HOSTS);

            try {
                ServerProgressBean serverProgress = this.getServerProgress(server);
                ServerAction serverAction = ServerAction.getServerAction(serverProgress.getServer());
                List<ApplicationBean> applications = serverAction.listOfApplications();

                ServerProgressState serverProgressState = serverProgress.getProgressState();
                if (serverProgressState.equals(ServerProgressState.INITIAL)) {
                    OperationType type = this.operation.getType();
                    if (type.equals(OperationType.MAINTAIN)) {
                        return serverAction.isStarted();
                    } else if (type.equals(OperationType.MIGRATE)) {
                        for (ApplicationBean application : applications) {
                            if (this.isSameApplication(this.operation.getApplication(), application)) {
                                if (!this.checkApplication(serverProgress, application, null, null)) {
                                    return false;
                                }
                            }
                            if (this.isSameApplication(serverAction.getApplicationBean(serverProgress.getNewDeploymentItem()),
                                application)) {
                                if (!this.checkApplication(serverProgress, application, null, null)) {
                                    return false;
                                }
                            }
                            if (this.isSameApplication(serverAction.getApplicationBean(serverProgress.getOldDeploymentItem()),
                                application)) {
                                if (!this.checkApplication(serverProgress, application, ServerAction.STATE_DEPLOYED, "Default")) {
                                    return false;
                                }
                            }
                        }

                        // If we came here all applications are OK
                        return true;
                    }

                    throw new IllegalStateException("Unknown OperationType: " + type);
                } else if (serverProgressState.equals(ServerProgressState.UPLOAD_OK)) {
                    for (ApplicationBean application : applications) {
                        if (this.isSameApplication(this.operation.getApplication(), application)) {
                            if (!this.checkApplication(serverProgress, application, ServerAction.STATE_PRESENT, null)) {
                                return false;
                            }
                        }
                        if (this.isSameApplication(serverAction.getApplicationBean(serverProgress.getNewDeploymentItem()),
                            application)) {
                            if (!this.checkApplication(serverProgress, application, ServerAction.STATE_PRESENT, null)) {
                                return false;
                            }
                        }
                        if (this.isSameApplication(serverAction.getApplicationBean(serverProgress.getOldDeploymentItem()),
                            application)) {
                            if (!this.checkApplication(serverProgress, application, ServerAction.STATE_DEPLOYED, "Default")) {
                                return false;
                            }
                        }
                    }

                    // If we came here all applications are OK
                    return true;
                } else if (serverProgressState.equals(ServerProgressState.DEPLOY_OK)) {
                    for (ApplicationBean application : applications) {
                        if (this.isSameApplication(this.operation.getApplication(), application)) {
                            if (!this.checkApplication(serverProgress, application, ServerAction.STATE_DEPLOYED, "Reserved")) {
                                return false;
                            }
                        }
                        if (this.isSameApplication(serverAction.getApplicationBean(serverProgress.getNewDeploymentItem()),
                            application)) {
                            if (!this.checkApplication(serverProgress, application, ServerAction.STATE_DEPLOYED, "Reserved")) {
                                return false;
                            }
                        }
                        if (this.isSameApplication(serverAction.getApplicationBean(serverProgress.getOldDeploymentItem()),
                            application)) {
                            if (!this.checkApplication(serverProgress, application, ServerAction.STATE_DEPLOYED, "Default")) {
                                return false;
                            }
                        }
                    }

                    // If we came here all applications are OK
                    return true;
                } else if (serverProgressState.equals(ServerProgressState.SET_DEFAULT_OK)) {
                    for (ApplicationBean application : applications) {
                        if (this.isSameApplication(this.operation.getApplication(), application)) {
                            if (!this.checkApplication(serverProgress, application, ServerAction.STATE_DEPLOYED, "Default")) {
                                return false;
                            }
                        }
                        if (this.isSameApplication(serverAction.getApplicationBean(serverProgress.getNewDeploymentItem()),
                            application)) {
                            if (!this.checkApplication(serverProgress, application, ServerAction.STATE_DEPLOYED, "Default")) {
                                return false;
                            }
                        }
                        if (this.isSameApplication(serverAction.getApplicationBean(serverProgress.getOldDeploymentItem()),
                            application)) {
                            if (!this.checkApplication(serverProgress, application, ServerAction.STATE_DEPLOYED, "Disabled")) {
                                return false;
                            }
                        }
                    }

                    // If we came here all applications are OK
                    return true;
                } else if (serverProgressState.equals(ServerProgressState.UNDEPLOY_OK)) {
                    for (ApplicationBean application : applications) {
                        if (this.isSameApplication(this.operation.getApplication(), application)) {
                            if (!this.checkApplication(serverProgress, application, ServerAction.STATE_DEPLOYED, "Default")) {
                                return false;
                            }
                        }
                        if (this.isSameApplication(serverAction.getApplicationBean(serverProgress.getNewDeploymentItem()),
                            application)) {
                            if (!this.checkApplication(serverProgress, application, ServerAction.STATE_DEPLOYED, "Default")) {
                                return false;
                            }
                        }
                        if (this.isSameApplication(serverAction.getApplicationBean(serverProgress.getOldDeploymentItem()),
                            application)) {
                            if (!this.checkApplication(serverProgress, application, ServerAction.STATE_PRESENT, null)) {
                                return false;
                            }
                        }
                    }

                    // If we came here all applications are OK
                    return true;
                } else if (serverProgressState.equals(ServerProgressState.ERASE_OK)) {
                    for (ApplicationBean application : applications) {
                        if (this.isSameApplication(this.operation.getApplication(), application)) {
                            if (!this.checkApplication(serverProgress, application, ServerAction.STATE_DEPLOYED, "Default")) {
                                return false;
                            }
                        }
                        if (this.isSameApplication(serverAction.getApplicationBean(serverProgress.getNewDeploymentItem()),
                            application)) {
                            if (!this.checkApplication(serverProgress, application, ServerAction.STATE_DEPLOYED, "Default")) {
                                return false;
                            }
                        }
                        if (this.isSameApplication(serverAction.getApplicationBean(serverProgress.getOldDeploymentItem()),
                            application)) {
                            if (!this.checkApplication(serverProgress, application, null, null)) {
                                return false;
                            }
                        }
                    }

                    // If we came here all applications are OK
                    return true;
                } else if (serverProgressState.equals(ServerProgressState.DISABLE_APPLICATIONS_OK)
                    || serverProgressState.equals(ServerProgressState.START_OK)) {
                    return serverAction.isStarted();
                } else if (serverProgressState.equals(ServerProgressState.STOP_OK)
                    || serverProgressState.equals(ServerProgressState.MAINTAIN_OK)) {
                    return !serverAction.isStarted();
                } else if (serverProgressState.equals(ServerProgressState.DESTROY_VM_HOSTS_OK)) {
                    // We don't know how to check this
                    return false;
                }

                throw new IllegalStateException("Unknown ServerProgressState: " + serverProgressState);
            } catch (Exception e) {
                throw new JadortServiceException("Cannot check server '" + server + "': " + e.getMessage(), e);
            }
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @throws JadortServiceException if the checkWorker can not be called in
     *         the current step. (this method can be called only in
     *         EXECUTING_MAINTENANCE_CLUSTER and
     *         EXECUTING_MAINTENANCE_NO_CLUSTER steps)
     */
    public boolean checkWorker(final WorkerBean worker) throws JadortServiceException {
        synchronized (this) {
            this.refreshOperation();
            this.checkStep(Step.EXECUTING_MAINTENANCE_CLUSTER, Step.EXECUTING_MAINTENANCE_NO_CLUSTER);

            try {
                WorkerProgressBean workerProgress = this.getWorkerProgress(worker);
                WorkerAction workerAction = WorkerAction.getWorkerAction(workerProgress.getWorker());
                WorkerAction.State state = workerAction.getState();

                WorkerProgressState workerProgressState = workerProgress.getProgressState();
                if (workerProgressState.equals(WorkerProgressState.INITIAL)
                    || workerProgressState.equals(WorkerProgressState.START_OK)) {
                    return (state == WorkerAction.State.ACTIVE);
                } else if (workerProgressState.equals(WorkerProgressState.STOP_OK)) {
                    return (state == WorkerAction.State.STOPPED);
                }

                throw new IllegalStateException("Unknown WorkerProgressState: " + workerProgressState);
            } catch (Exception e) {
                throw new JadortServiceException("Cannot check worker '" + worker + "': " + e.getMessage(), e);
            }
        }
    }

    /**
     * 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;
            }
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @throws JadortServiceException <ul>
     *         <li>if the selectApplication can not be called in the current
     *         step. (this method can be called only in SELECT_APPLICATION step)
     *         </ul>
     */
    public void selectApplication(final URL url) throws JadortServiceException {
        synchronized (this) {
            this.refreshOperation();
            this.checkStep(Step.SELECT_APPLICATION);

            ApplicationBean applicationBean;
            File file;
            if (url.getProtocol().equals("file")) {
                try {
                    file = new File(url.toURI());
                } catch (URISyntaxException e) {
                    throw new JadortServiceException("Cannot create file for URL '" + url + "': " + e.getMessage(), e);
                }
            } else {
                try {
                    URLConnection connection = url.openConnection();
                    InputStream reader = connection.getInputStream();
                    String name = url.getPath();
                    name = name.substring(name.lastIndexOf('/'));
                    file = new File(System.getProperty("java.io.tmpdir") + File.separatorChar + "jadort.temp."
                        + System.currentTimeMillis());
                    file.mkdirs();
                    file = new File(file, name);
                    FileOutputStream writer = new FileOutputStream(file);
                    int read;
                    byte[] buffer = new byte[JadortServiceImpl.BUFFER_SIZE];
                    while ((read = reader.read(buffer)) > 0) {
                        writer.write(buffer, 0, read);
                    }
                    writer.close();
                    reader.close();
                } catch (IOException e) {
                    throw new JadortServiceException("Cannot read from URL: '" + url + "'", null);
                }
            }
            try {
                applicationBean = new ApplicationBean(file);
            } catch (Exception e) {
                throw new JadortServiceException("Cannot create application for URL '" + url + "': " + e.getMessage(), e);
            }

            ApplicationBean oldApplicationBean = this.operation.getApplication();
            this.operation.setApplication(applicationBean);

            this.mergeOperation();

            if (oldApplicationBean != null) {
                this.em.remove(oldApplicationBean);
                this.em.flush();
            }
        }
    }

    /**
     * {@inheritDoc}<br/>
     * <br/>
     * <b>IMPORTANT</b>: Avoid calling this method from inside other methods of
     * the JadortServiceImpl class (it will be costly)
     */
    public Step getCurrentStep() {
        synchronized (this) {
            this.refreshOperation();

            if (this.operation == null) {
                return Step.SELECT_OPERATION;
            } else {
                return this.operation.getCurrentStep();
            }
        }
    }

    /**
     * Fills the applications of a {@link GroupBean}.
     * 
     * @param groupBean Group to fill.
     * @throws JadortServiceException If getting the list of applications fails.
     */
    private void fillApplications(final GroupBean groupBean) throws JadortServiceException {
        // Sort the applications based on their names and versions
        SortedMap<String, ApplicationBean> applications = new TreeMap<String, ApplicationBean>();

        for (ServerBean serverBean : groupBean.getServers()) {
            ServerAction serverAction = ServerAction.getServerAction(serverBean);
            try {
                for (ApplicationBean applicationBean : serverAction.listOfApplications()) {
                    applications.put(applicationBean.toString(), applicationBean);
                }
            } 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>(applications.values()));
    }

    /**
     * Fills the vmImages fields of a {@link GroupBean}.
     * 
     * @param groupBean Group to fill.
     * @throws JadortServiceException If getting the list of applications fails.
     */
    private 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);
        }
    }

    /**
     * @return true if any serverProgress has an old version, false otherwise.
     */
    private boolean hasOldVersion() {
        for (ServerProgressBean serverProgress : this.operation.getServerProgressList()) {
            if (serverProgress.hasOldDeploymentItem()) {
                return true;
            }
        }
        return false;
    }

    /**
     * {@inheritDoc}
     * 
     * @throws JadortServiceException if it is not allowed to go to the next
     *         step.
     */
    public void next() throws JadortServiceException {
        synchronized (this) {
            this.refreshOperation();

            if (!this.canGoToNextStep()) {
                throw new JadortServiceException("You are not allowed to go to the next step!", null);
            }

            Step currentStep = this.operation.getCurrentStep();

            if (currentStep.equals(Step.INITIALIZE_TOPOLOGY)) {
                // Check connectivity
                for (GroupBean group : this.operation.getTopology().getGroups()) {
                    if (group.getServers() != null) {
                        for (ServerBean server : group.getServers()) {
                            ServerAction serverAction = ServerAction.getServerAction(server);
                            try {
                                serverAction.listOfApplications();
                            } catch (Exception e) {
                                String message = e.getClass().getName() + ": " + e.getMessage();
                                throw new JadortServiceException("Failed getting the state of 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 (Exception e) {
                                    String message = e.getClass().getName() + ": " + e.getMessage();
                                    throw new JadortServiceException("Failed getting the state of the VM "
                                        + server.getVm().getName() + ": " + message, e);
                                }
                            }
                        }
                    }

                    if (group.getWorkers() != null) {
                        for (WorkerBean worker : group.getWorkers()) {
                            WorkerAction workerAction = WorkerAction.getWorkerAction(worker);
                            try {
                                workerAction.getState();
                            } catch (Exception e) {
                                String message = e.getClass().getName() + ": " + e.getMessage();
                                throw new JadortServiceException("Failed getting the state of the worker " + worker.getName()
                                    + ": " + message, e);
                            }
                        }
                    }
                }
            }

            if (currentStep.equals(OperationStateBean.Step.EXECUTING_MAINTENANCE_CLUSTER)
                || currentStep.equals(OperationStateBean.Step.EXECUTING_MAINTENANCE_NO_CLUSTER)) {
                if (!this.operation.getAimedWorkerProgressState().equals(WorkerProgressState.START_OK)) {
                    this.nextExecuteMigration();
                } else {
                    for (ServerProgressBean serverProgressBean : this.operation.getServerProgressList()) {
                        serverProgressBean.getServer().setMaintained(true);
                    }

                    for (WorkerProgressBean workerProgressBean : this.operation.getWorkerProgressList()) {
                        workerProgressBean.setActionState(ActionState.FINISHED_OK);
                    }
                    if (this.operation.getSelectedGroup().getServers().get(0).getVm() == null) {
                        // There are no VMs, prepare for Step.FINISHED
                        for (ServerProgressBean serverProgressBean : this.operation.getServerProgressList()) {
                            serverProgressBean.setActionState(ActionState.FINISHED_OK);
                        }
                    } else {
                        // Else, prepare for Step.DESTROY_OLD_VM_HOSTS
                        List<ServerProgressBean> serverProgressList = new ArrayList<ServerProgressBean>();
                        serverProgressList.addAll(this.operation.getAllServerProgressList());
                        this.operation.setServerProgressList(serverProgressList);
                        for (ServerProgressBean serverProgress : this.operation.getServerProgressList()) {
                            serverProgress.setProgress(0);
                        }
                    }
                    this.goToNextStep();
                }
            } else if (currentStep.equals(OperationStateBean.Step.DESTROY_OLD_VM_HOSTS)) {
                if (!this.operation.getAimedServerProgressState().equals(ServerProgressState.DESTROY_VM_HOSTS_OK)) {
                    this.nextExecuteMigration();
                } else {
                    // Else, prepare for Step.FINISHED
                    for (ServerProgressBean serverProgressBean : this.operation.getServerProgressList()) {
                        serverProgressBean.setActionState(ActionState.FINISHED_OK);
                    }

                    this.goToNextStep();
                }
            } else if (currentStep.equals(OperationStateBean.Step.EXECUTING_MIGRATION_PART1)) {
                if (this.operation.getAimedServerProgressState().equals(ServerProgressState.SET_DEFAULT_OK)) {
                    if (!this.hasOldVersion()) {
                        // If there's no old version present,
                        // prepare for Step.FINISHED
                        for (ServerProgressBean serverProgressServer : this.operation.getServerProgressList()) {
                            serverProgressServer.setActionState(ActionState.FINISHED_OK);
                        }
                        this.operation.setCurrentStep(Step.EXECUTING_MIGRATION_PART2);
                    } else {
                        // Else, prepare for Step.EXECUTING_MIGRATION_PART2
                        for (ServerProgressBean serverProgress : this.operation.getServerProgressList()) {
                            serverProgress.setProgress(0);
                        }
                    }
                    this.goToNextStep();
                } else {
                    this.nextExecuteMigration();
                }
            } else if (currentStep.equals(OperationStateBean.Step.EXECUTING_MIGRATION_PART2)) {
                if (this.operation.getAimedServerProgressState().equals(ServerProgressState.ERASE_OK)) {
                    for (ServerProgressBean serverProgressServer : this.operation.getServerProgressList()) {
                        serverProgressServer.setActionState(ActionState.FINISHED_OK);
                    }
                    this.goToNextStep();
                } else {
                    this.nextExecuteMigration();
                }
            } else {
                this.goToNextStep();
            }
        }
    }

    /**
     * go the next step
     * 
     * @throws JadortServiceException If getting the list of applications fails.
     */
    private void goToNextStep() throws JadortServiceException {
        Step currentStep = this.operation.getCurrentStep();

        if (currentStep.equals(Step.SELECT_OPERATION)) {
            this.operation.setCurrentStep(Step.INITIALIZE_TOPOLOGY);
        } else if (currentStep.equals(Step.INITIALIZE_TOPOLOGY)) {
            this.operation.setCurrentStep(Step.SELECT_GROUP);
        } else if (currentStep.equals(Step.SELECT_GROUP)) {
            this.operation.setCurrentStep(Step.SELECT_OPERATION_TYPE);
        } else if (currentStep.equals(Step.SELECT_OPERATION_TYPE)) {
            IJadortService.OperationType operationType = this.operation.getType();
            if (operationType.equals(IJadortService.OperationType.MIGRATE)) {
                this.operation.setCurrentStep(Step.SELECT_APPLICATION);
            } else if (operationType.equals(IJadortService.OperationType.MAINTAIN)) {
                if (this.operation.getSelectedGroup().getServers().get(0).getVm() != null) {
                    this.operation.setCurrentStep(Step.SELECT_VM_IMAGE);
                } else {
                    this.operation.setCurrentStep(Step.SELECT_SERVERS);
                }
            } else {
                throw new IllegalStateException("Unknown OperationType: " + operationType);
            }
        } else if (currentStep.equals(Step.SELECT_VM_IMAGE)) {
            this.operation.setCurrentStep(Step.SELECT_SERVERS);
        } else if (currentStep.equals(Step.SELECT_SERVERS)) {
            if (this.operation.getSelectedGroup().getServers().get(0).getVm() != null) {
                for (ServerProgressBean serverProgress : this.operation.getServerProgressList()) {
                    if (serverProgress.getVmImage() == null) {
                        // There's at least one server without any VM image
                        this.operation.setCurrentStep(Step.SELECT_VM_IMAGE_FOR_SERVER);
                        break;
                    }
                }
            }

            // Use getCurrentStep and not currentStep since
            // the for loop might have called setCurrentStep
            if (this.operation.getCurrentStep().equals(Step.SELECT_SERVERS)) {
                if (this.operation.getSelectedGroup().getClustered()) {
                    this.operation.setCurrentStep(Step.EXECUTING_MAINTENANCE_CLUSTER);
                } else {
                    this.operation.setCurrentStep(Step.EXECUTING_MAINTENANCE_NO_CLUSTER);
                }
            }
        } else if (currentStep.equals(Step.SELECT_VM_IMAGE_FOR_SERVER)) {
            if (this.operation.getSelectedGroup().getClustered()) {
                this.operation.setCurrentStep(Step.EXECUTING_MAINTENANCE_CLUSTER);
            } else {
                this.operation.setCurrentStep(Step.EXECUTING_MAINTENANCE_NO_CLUSTER);
            }
        } else if (currentStep.equals(Step.EXECUTING_MAINTENANCE_CLUSTER)
            || currentStep.equals(Step.EXECUTING_MAINTENANCE_NO_CLUSTER)) {
            if (this.allServersHaveBeenMaintained()) {
                List<ServerProgressBean> serverProgressList = new ArrayList<ServerProgressBean>();
                serverProgressList.addAll(this.operation.getAllServerProgressList());
                this.operation.setServerProgressList(serverProgressList);

                if (this.operation.getSelectedGroup().getServers().get(0).getVm() == null) {
                    this.operation.setCurrentStep(Step.FINISHED);
                } else {
                    // Use an empty workers progress list since we'll
                    // DESTROY_OLD_VM_HOSTS, therefore won't touch workers
                    List<WorkerProgressBean> workerProgressList = new ArrayList<WorkerProgressBean>();
                    this.operation.setWorkerProgressList(workerProgressList);

                    this.operation.setCurrentStep(Step.DESTROY_OLD_VM_HOSTS);
                }
            } else {
                this.operation.setAimedWorkerProgressState(WorkerProgressState.INITIAL);
                this.operation.setAimedProgressPercent(0);
                this.operation.setAimedServerProgressState(ServerProgressState.INITIAL);
                this.operation.setAimedProgressPercent(0);
                this.operation.setCurrentStep(Step.SELECT_SERVERS);
                this.operation.setSelectedServers(null);
            }
        } else if (currentStep.equals(Step.DESTROY_OLD_VM_HOSTS)) {
            // The serverProgressList already contains all servers

            List<WorkerProgressBean> workerProgressList = new ArrayList<WorkerProgressBean>();
            workerProgressList.addAll(this.operation.getAllWorkerProgressList());
            this.operation.setWorkerProgressList(workerProgressList);

            this.operation.setCurrentStep(Step.FINISHED);
        } else if (currentStep.equals(Step.SELECT_APPLICATION)) {
            this.operation.setAimedProgressPercent(0);
            this.operation.setCurrentStep(Step.EXECUTING_MIGRATION_PART1);
        } else if (currentStep.equals(Step.EXECUTING_MIGRATION_PART1)) {
            this.operation.setAimedProgressPercent(0);
            this.operation.setCurrentStep(Step.EXECUTING_MIGRATION_PART2);
        } else if (currentStep.equals(Step.EXECUTING_MIGRATION_PART2)) {
            this.operation.setAimedProgressPercent(100);
            this.operation.setCurrentStep(Step.FINISHED);
        } else if (currentStep.equals(Step.FINISHED)) {
            throw new IllegalStateException("No next for Step: " + currentStep);
        } else {
            throw new IllegalStateException("Unknown Step: " + currentStep);
        }
        this.mergeOperation();
    }

    /**
     * {@inheritDoc}
     * 
     * @throws JadortServiceException if it is not allowed to go to the previous
     *         step.
     */
    public void previous() throws JadortServiceException {
        synchronized (this) {
            this.refreshOperation();

            if (!this.canGoToPreviousStep()) {
                throw new JadortServiceException("You are not allowed to go to the previous step!", null);
            }

            Step currentStep = this.operation.getCurrentStep();

            if (currentStep.equals(OperationStateBean.Step.SELECT_GROUP)) {
                TopologyBean oldTopology = this.operation.getTopology();
                this.operation.setTopology(null);
                this.goToPreviousStep();

                this.em.remove(oldTopology);
                this.em.flush();
            } else if (currentStep.equals(OperationStateBean.Step.SELECT_APPLICATION)) {
                // Don't remove the oldSelectedGroup, as the GroupBeans are
                // created at topology initialization and no copy is done when
                // selecting group).
                //
                // Remove the old progress lists as they're useless now.
                // Remember to copy them in order to avoid exceptions
                List<ServerProgressBean> oldServerProgressList = new ArrayList<ServerProgressBean>();
                oldServerProgressList.addAll(this.operation.getServerProgressList());
                this.operation.setServerProgressList(null);
                this.operation.setType(null);
                ApplicationBean oldApplication = this.operation.getApplication();
                this.operation.setApplication(null);
                this.goToPreviousStep();

                for (ServerProgressBean serverProgressBean : oldServerProgressList) {
                    this.em.remove(serverProgressBean);
                }
                if (oldApplication != null) {
                    this.em.remove(oldApplication);
                }
                this.em.flush();
            } else if (currentStep.equals(OperationStateBean.Step.SELECT_OPERATION_TYPE)) {
                this.operation.setSelectedGroup(null);
                this.goToPreviousStep();
            } else if (currentStep.equals(OperationStateBean.Step.SELECT_VM_IMAGE)) {
                this.operation.setType(null);
                this.goToPreviousStep();
            } else if (currentStep.equals(OperationStateBean.Step.SELECT_VM_IMAGE_FOR_SERVER)) {
                this.goToPreviousStep();
            } else if (currentStep.equals(OperationStateBean.Step.SELECT_SERVERS)) {
                if (this.anyMaintainedServerPresent()) {
                    this.operation.setAimedProgressPercent(100);
                    this.operation.setAimedServerProgressState(ServerProgressState.START_OK);
                    this.operation.setAimedWorkerProgressState(WorkerProgressState.START_OK);

                    List<ServerProgressBean> serverProgressList = new ArrayList<ServerProgressBean>();
                    for (ServerProgressBean serverProgress : this.operation.getAllServerProgressList()) {
                        if (serverProgress.getServer().getMaintained()) {
                            serverProgress.setProgress(this.operation.getAimedProgressPercent());
                            serverProgress.setActionState(ActionState.WAITING);
                            serverProgress.getServer().setMaintained(false);
                            serverProgressList.add(serverProgress);
                        }
                    }
                    this.operation.setServerProgressList(serverProgressList);

                    List<WorkerProgressBean> workerProgressList = new ArrayList<WorkerProgressBean>();
                    for (WorkerProgressBean workerProgress : this.operation.getAllWorkerProgressList()) {
                        if (WorkerProgressState.START_OK.equals(workerProgress.getProgressState())) {
                            workerProgress.setProgress(this.operation.getAimedProgressPercent());
                            workerProgress.setActionState(ActionState.WAITING);
                            workerProgressList.add(workerProgress);
                        }
                    }
                    this.operation.setWorkerProgressList(workerProgressList);
                    if (this.operation.getSelectedGroup().getClustered()) {
                        this.operation.setCurrentStep(Step.EXECUTING_MAINTENANCE_CLUSTER);
                    } else {
                        this.operation.setCurrentStep(Step.EXECUTING_MAINTENANCE_NO_CLUSTER);
                    }
                    this.mergeOperation();
                } else {
                    // Don't remove the oldSelectedGroup, as the GroupBeans are
                    // created at topology initialization and no copy is done
                    // when selecting group).
                    //
                    // Remove the old progress lists as they're useless now.
                    // Remember to copy them in order to avoid exceptions
                    Set<ServerProgressBean> oldServerProgressList = new HashSet<ServerProgressBean>();
                    if (this.operation.getServerProgressList() != null) {
                        oldServerProgressList.addAll(this.operation.getServerProgressList());
                    }
                    if (this.operation.getAllServerProgressList() != null) {
                        oldServerProgressList.addAll(this.operation.getAllServerProgressList());
                    }
                    Set<WorkerProgressBean> oldWorkerProgressList = new HashSet<WorkerProgressBean>();
                    if (this.operation.getWorkerProgressList() != null) {
                        oldWorkerProgressList.addAll(this.operation.getWorkerProgressList());
                    }
                    if (this.operation.getAllWorkerProgressList() != null) {
                        oldWorkerProgressList.addAll(this.operation.getAllWorkerProgressList());
                    }
                    this.operation.setServerProgressList(null);
                    this.operation.setWorkerProgressList(null);
                    this.operation.setAllServerProgressList(null);
                    this.operation.setAllWorkerProgressList(null);

                    VMImageBean oldVMImage = this.operation.getVmImage();
                    this.operation.setVmImage(null);

                    this.goToPreviousStep();

                    for (WorkerProgressBean workerProgressBean : oldWorkerProgressList) {
                        this.em.remove(workerProgressBean);
                    }
                    for (ServerProgressBean serverProgressBean : oldServerProgressList) {
                        this.em.remove(serverProgressBean);
                    }
                    if (oldVMImage != null) {
                        this.em.remove(oldVMImage);
                    }
                    this.em.flush();
                }
            } else if (currentStep.equals(OperationStateBean.Step.EXECUTING_MAINTENANCE_CLUSTER)) {
                if (this.operation.getAimedWorkerProgressState().equals(WorkerProgressState.INITIAL)) {
                    for (ServerProgressBean serverProgress : this.operation.getServerProgressList()) {
                        serverProgress.setProgress(this.operation.getAimedProgressPercent());
                        serverProgress.setActionState(ActionState.WAITING);
                    }
                    for (WorkerProgressBean workerProgress : this.operation.getWorkerProgressList()) {
                        workerProgress.setProgress(this.operation.getAimedProgressPercent());
                        workerProgress.setActionState(ActionState.WAITING);
                    }
                    this.goToPreviousStep();
                } else {
                    this.previousExecuteMigration();
                }
            } else if (currentStep.equals(OperationStateBean.Step.EXECUTING_MAINTENANCE_NO_CLUSTER)) {
                if (this.operation.getAimedWorkerProgressState().equals(WorkerProgressState.INITIAL)) {
                    for (ServerProgressBean serverProgress : this.operation.getServerProgressList()) {
                        serverProgress.setProgress(this.operation.getAimedProgressPercent());
                        serverProgress.setActionState(ActionState.WAITING);
                    }
                    for (WorkerProgressBean workerProgress : this.operation.getWorkerProgressList()) {
                        workerProgress.setProgress(this.operation.getAimedProgressPercent());
                        workerProgress.setActionState(ActionState.WAITING);
                    }
                    this.goToPreviousStep();
                } else {
                    this.previousExecuteMigration();
                }
            } else if (currentStep.equals(OperationStateBean.Step.DESTROY_OLD_VM_HOSTS)) {
                // "Previous" is only allowed if VM host destroy not done yet
                List<WorkerProgressBean> workerProgressList = new ArrayList<WorkerProgressBean>();
                workerProgressList.addAll(this.operation.getAllWorkerProgressList());
                this.operation.setWorkerProgressList(workerProgressList);

                for (ServerProgressBean serverProgress : this.operation.getServerProgressList()) {
                    serverProgress.setProgress(this.operation.getAimedProgressPercent());
                    serverProgress.setProgressState(ServerProgressState.START_OK);
                    serverProgress.setActionState(ActionState.WAITING);
                }
                for (WorkerProgressBean workerProgress : this.operation.getWorkerProgressList()) {
                    workerProgress.setProgress(this.operation.getAimedProgressPercent());
                    workerProgress.setProgressState(WorkerProgressState.START_OK);
                    workerProgress.setActionState(ActionState.WAITING);
                }
                this.goToPreviousStep();
            } else if (currentStep.equals(OperationStateBean.Step.EXECUTING_MIGRATION_PART1)) {
                if (this.operation.getAimedServerProgressState().equals(ServerProgressState.INITIAL)) {
                    this.goToPreviousStep();
                } else {
                    this.previousExecuteMigration();
                }
            } else if (currentStep.equals(OperationStateBean.Step.EXECUTING_MIGRATION_PART2)) {
                if (this.operation.getAimedServerProgressState().equals(ServerProgressState.SET_DEFAULT_OK)) {
                    for (ServerProgressBean serverProgress : this.operation.getServerProgressList()) {
                        serverProgress.setProgress(100);
                    }
                    this.goToPreviousStep();
                } else {
                    this.previousExecuteMigration();
                }
            } else if (currentStep.equals(OperationStateBean.Step.FINISHED)) {
                for (ServerProgressBean serverProgress : this.operation.getServerProgressList()) {
                    serverProgress.setProgress(this.operation.getAimedProgressPercent());
                    serverProgress.setActionState(ActionState.WAITING);
                }
                if (this.operation.getType().equals(OperationType.MAINTAIN)) {
                    for (ServerProgressBean serverProgress : this.operation.getServerProgressList()) {
                        serverProgress.getServer().setMaintained(false);
                    }

                    for (WorkerProgressBean workerProgress : this.operation.getWorkerProgressList()) {
                        workerProgress.setProgress(this.operation.getAimedProgressPercent());
                        workerProgress.setActionState(ActionState.WAITING);
                    }
                }
                this.goToPreviousStep();
            } else {
                throw new IllegalStateException("Unknown Step: " + currentStep);
            }
        }
    }

    /**
     * go the previous step
     * 
     * @throws JadortServiceException If getting the list of applications fails.
     */
    private void goToPreviousStep() throws JadortServiceException {
        Step currentStep = this.operation.getCurrentStep();
        List<VMImageBean> oldVMImages = new ArrayList<VMImageBean>();

        if (currentStep.equals(Step.SELECT_OPERATION) || currentStep.equals(Step.INITIALIZE_TOPOLOGY)) {
            throw new IllegalStateException("No previous for Step: " + currentStep);
        } else if (currentStep.equals(Step.SELECT_GROUP)) {
            this.operation.setCurrentStep(Step.INITIALIZE_TOPOLOGY);
        } else if (currentStep.equals(Step.SELECT_APPLICATION)) {
            this.operation.setCurrentStep(Step.SELECT_OPERATION_TYPE);
        } else if (currentStep.equals(Step.SELECT_VM_IMAGE)) {
            this.operation.setCurrentStep(Step.SELECT_OPERATION_TYPE);
        } else if (currentStep.equals(Step.SELECT_VM_IMAGE_FOR_SERVER)) {
            this.operation.setCurrentStep(Step.SELECT_SERVERS);
        } else if (currentStep.equals(Step.SELECT_OPERATION_TYPE)) {
            this.operation.setType(null);
            this.operation.setCurrentStep(Step.SELECT_GROUP);
        } else if (currentStep.equals(Step.SELECT_SERVERS)) {
            if (this.operation.getSelectedGroup().getServers().get(0).getVm() != null) {
                this.operation.setCurrentStep(Step.SELECT_VM_IMAGE);
            } else {
                this.operation.setType(null);
                this.operation.setCurrentStep(Step.SELECT_OPERATION_TYPE);
            }
        } else if (currentStep.equals(Step.EXECUTING_MAINTENANCE_CLUSTER)
            || currentStep.equals(Step.EXECUTING_MAINTENANCE_NO_CLUSTER)) {
            this.operation.setCurrentStep(Step.SELECT_SERVERS);
            this.operation.setSelectedServers(null);
            for (ServerProgressBean serverProgress : this.operation.getServerProgressList()) {
                serverProgress.setOldDeploymentItem(null);

                if (serverProgress.getVmImage() != null) {
                    oldVMImages.add(serverProgress.getVmImage());
                    serverProgress.setVmImage(null);
                }
            }
        } else if (currentStep.equals(Step.DESTROY_OLD_VM_HOSTS)) {
            if (this.operation.getSelectedGroup().getClustered()) {
                this.operation.setCurrentStep(Step.EXECUTING_MAINTENANCE_CLUSTER);
            } else {
                this.operation.setCurrentStep(Step.EXECUTING_MAINTENANCE_NO_CLUSTER);
            }
        } else if (currentStep.equals(Step.EXECUTING_MIGRATION_PART1)) {
            this.operation.setAimedProgressPercent(0);
            this.operation.setCurrentStep(Step.SELECT_APPLICATION);
        } else if (currentStep.equals(Step.EXECUTING_MIGRATION_PART2)) {
            this.operation.setAimedProgressPercent(100);
            this.operation.setCurrentStep(Step.EXECUTING_MIGRATION_PART1);
        } else if (currentStep.equals(Step.FINISHED)) {
            this.operation.setAimedProgressPercent(100);
            if (this.operation.getType().equals(OperationType.MAINTAIN)) {
                if (this.operation.getSelectedGroup().getServers().get(0).getVm() != null) {
                    this.operation.setCurrentStep(Step.DESTROY_OLD_VM_HOSTS);
                } else if (this.operation.getSelectedGroup().getClustered()) {
                    this.operation.setCurrentStep(Step.EXECUTING_MAINTENANCE_CLUSTER);
                } else {
                    this.operation.setCurrentStep(Step.EXECUTING_MAINTENANCE_NO_CLUSTER);
                }
            } else {
                if (this.hasOldVersion()) {
                    this.operation.setCurrentStep(Step.EXECUTING_MIGRATION_PART2);
                } else {
                    this.operation.setCurrentStep(Step.EXECUTING_MIGRATION_PART1);
                }
            }
        } else {
            throw new IllegalStateException("Unknown Step: " + currentStep);
        }

        this.mergeOperation();

        for (VMImageBean oldVMImage : oldVMImages) {
            this.em.remove(oldVMImage);
            this.em.flush();
        }
    }

    /**
     * {@inheritDoc}
     */
    private boolean canGoToNextStep() {
        Step currentStep = this.operation.getCurrentStep();
        if (currentStep.equals(Step.SELECT_OPERATION)) {
            return this.operation != null;
        } else if (currentStep.equals(Step.INITIALIZE_TOPOLOGY)) {
            return this.operation.getTopology() != null;
        } else if (currentStep.equals(Step.SELECT_GROUP)) {
            return this.operation.getSelectedGroup() != null;
        } else if (currentStep.equals(Step.SELECT_OPERATION_TYPE)) {
            return this.operation.getType() != null;
        } else if (currentStep.equals(Step.SELECT_VM_IMAGE)) {
            return true;
        } else if (currentStep.equals(Step.SELECT_VM_IMAGE_FOR_SERVER)) {
            for (ServerProgressBean serverProgress : this.operation.getServerProgressList()) {
                if (serverProgress.getVmImage() == null) {
                    return false;
                }
            }
            return true;
        } else if (currentStep.equals(Step.SELECT_APPLICATION)) {
            return this.operation.getApplication() != null;
        } else if (currentStep.equals(Step.EXECUTING_MIGRATION_PART1) || currentStep.equals(Step.EXECUTING_MIGRATION_PART2)) {
            for (ServerProgressBean serverProgress : this.operation.getServerProgressList()) {
                if (!serverProgress.getActionState().equals(ActionState.WAITING)) {
                    return false;
                }
            }
            return true;
        } else if (currentStep.equals(Step.SELECT_SERVERS)) {
            return this.operation.getSelectedServers() != null && this.operation.getSelectedServers().size() > 0;
        } else if (currentStep.equals(Step.EXECUTING_MAINTENANCE_CLUSTER)
            || currentStep.equals(Step.EXECUTING_MAINTENANCE_NO_CLUSTER) || currentStep.equals(Step.DESTROY_OLD_VM_HOSTS)) {
            for (ServerProgressBean serverProgress : this.operation.getServerProgressList()) {
                if (!serverProgress.getActionState().equals(ActionState.WAITING)) {
                    return false;
                }
            }
            for (WorkerProgressBean workerProgress : this.operation.getWorkerProgressList()) {
                if (!workerProgress.getActionState().equals(ActionState.WAITING)) {
                    return false;
                }
            }
            return true;
        } else if (currentStep.equals(Step.FINISHED)) {
            return false;
        } else {
            throw new IllegalStateException("Unknown Step: " + currentStep);
        }
    }

    /**
     * {@inheritDoc}
     */
    private boolean canGoToPreviousStep() {
        Step currentStep = this.operation.getCurrentStep();
        if (currentStep.equals(Step.SELECT_OPERATION) || currentStep.equals(Step.INITIALIZE_TOPOLOGY)) {
            return false;
        } else if (currentStep.equals(Step.SELECT_GROUP) || currentStep.equals(Step.SELECT_APPLICATION)
            || currentStep.equals(Step.SELECT_VM_IMAGE) || currentStep.equals(Step.SELECT_VM_IMAGE_FOR_SERVER)
            || currentStep.equals(Step.SELECT_OPERATION_TYPE)) {
            return true;
        } else if (currentStep.equals(Step.EXECUTING_MIGRATION_PART1) || currentStep.equals(Step.EXECUTING_MIGRATION_PART2)) {
            if (this.operation.getAimedServerProgressState().equals(ServerProgressState.ERASE_OK)) {
                return false;
            }
            ServerProgressState progressState = null;
            for (ServerProgressBean serverProgress : this.operation.getServerProgressList()) {
                if (!serverProgress.getActionState().equals(ActionState.WAITING)
                    && !serverProgress.getActionState().equals(ActionState.FINISHED_ERROR)) {
                    return false;
                }
                if (progressState == null) {
                    progressState = serverProgress.getProgressState();
                    continue;
                }
                if (!progressState.equals(serverProgress.getProgressState())) {
                    return false;
                }
            }
            return true;
        } else if (currentStep.equals(Step.SELECT_SERVERS)) {
            return true;
        } else if (currentStep.equals(Step.EXECUTING_MAINTENANCE_CLUSTER)
            || currentStep.equals(Step.EXECUTING_MAINTENANCE_NO_CLUSTER)) {
            for (ServerProgressBean serverProgress : this.operation.getServerProgressList()) {
                if (!serverProgress.getActionState().equals(ActionState.WAITING)
                    && !serverProgress.getActionState().equals(ActionState.FINISHED_ERROR)) {
                    return false;
                }
            }
            for (WorkerProgressBean workerProgress : this.operation.getWorkerProgressList()) {
                if (!workerProgress.getActionState().equals(ActionState.WAITING)
                    && !workerProgress.getActionState().equals(ActionState.FINISHED_ERROR)) {
                    return false;
                }
            }
            return true;
        } else if (currentStep.equals(Step.DESTROY_OLD_VM_HOSTS)) {
            for (ServerProgressBean serverProgress : this.operation.getServerProgressList()) {
                if (serverProgress.getActionState().equals(ActionState.RUNNING) || serverProgress.getProgress() > 0) {
                    return false;
                }
            }
            return true;
        } else if (currentStep.equals(Step.FINISHED)) {
            return true;
        } else {
            throw new IllegalStateException("Unknown Step: " + currentStep);
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @throws JadortServiceException if the selectOperation can not be called
     *         in the current step. (this method can be called only in
     *         SELECT_OPERATION step)
     */
    public void selectOperation(final OperationStateBean selectedOperation) throws JadortServiceException {
        synchronized (this) {
            this.refreshOperation();
            this.checkStep(Step.SELECT_OPERATION);

            this.operation = this.em.find(OperationStateBean.class, selectedOperation.getId());
            if (this.operation == null) {
                throw new JadortServiceException("OperationStateBean \"" + selectedOperation + "\" not found", null);
            }
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @throws JadortServiceException <ul>
     *         <li>if the selectOperation can not be called in the current step.
     *         (this method can be called only in SELECT_OPERATION step)
     *         </ul>
     */
    public void createNewOperation(final String newOperationName) throws JadortServiceException {
        synchronized (this) {
            this.refreshOperation();
            this.checkStep(Step.SELECT_OPERATION);

            String filteredOperationName = newOperationName.trim();
            if (filteredOperationName.length() < 1) {
                throw new JadortServiceException("Operation name cannot be empty!", null);
            }

            this.operation = new OperationStateBean();
            this.operation.setName(filteredOperationName);
            this.operation.setDate(new Date());
            this.em.persist(this.operation);
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @throws JadortServiceException <ul>
     *         <li>if the selectOperation can not be called in the current step.
     *         (this method can be called only in SELECT_OPERATION step)
     *         <li>if the operation to delete is still in progress in a critical
     *         phase and therefore cannot be deleted
     *         </ul>
     */
    public void deleteOperation(final OperationStateBean selectedOperation) throws JadortServiceException {
        synchronized (this) {
            this.refreshOperation();
            this.checkStep(Step.SELECT_OPERATION);

            OperationStateBean toRemove = this.em.find(OperationStateBean.class, selectedOperation.getId());

            if (toRemove.getIsDeletable()) {
                this.em.remove(toRemove);
                this.em.flush();

                if (this.operation != null && selectedOperation.getId() == this.operation.getId()) {
                    this.operation = null;
                }
            } else {
                throw new JadortServiceException("Operation could not be deleted because it currently is in the "
                    + toRemove.getCurrentStep() + " step.", null);
            }
        }
    }

    /**
     * This method checks if an method (or command) can be called in the current
     * step.
     * 
     * @throws JadortServiceException If the current operation is not in one of
     *         the expected states.
     * @param steps List of steps that are currently allowed.
     */
    private void checkStep(final Step... steps) throws JadortServiceException {
        Step currentStep;
        if (this.operation == null) {
            currentStep = Step.SELECT_OPERATION;
        } else {
            currentStep = this.operation.getCurrentStep();
        }

        for (Step step : steps) {
            if (currentStep.equals(step)) {
                return;
            }
        }

        throw new JadortServiceException(currentStep, steps);
    }

    /**
     * This method refreshes {@link JadortServiceImpl#operation} and should be
     * called when entering any method that uses datum that can be modified in
     * another thread than the stateful bean's. This is the case when taking any
     * serverProgress action: the {@link OperationStateBean} gets updated by the
     * {@link JadortServiceJMSActions} handler in an asynchronous way.
     */
    private void refreshOperation() {
        if (this.operation != null) {
            this.operation = this.em.find(OperationStateBean.class, this.operation.getId());
        }
    }

    /**
     * This method merges {@link JadortServiceImpl#operation} onto the DB.
     */
    private void mergeOperation() {
        if (this.operation != null) {
            this.operation = this.em.merge(this.operation);
            this.em.flush();
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @throws JadortServiceException if the selectServers method can not be
     *         called in the current step (this method can be called only in
     *         SELECT_SERVERS step)
     */
    public void selectServers(final List<ServerBean> selectedServers) throws JadortServiceException {
        synchronized (this) {
            this.refreshOperation();
            this.checkStep(Step.SELECT_SERVERS);

            List<ServerBean> selectedServersList = new ArrayList<ServerBean>();
            List<ServerProgressBean> serverProgressList = new ArrayList<ServerProgressBean>();

            for (ServerBean selectedServer : selectedServers) {
                for (ServerBean server : this.operation.getSelectedGroup().getServers()) {
                    if (server.equals(selectedServer)) {
                        if (!selectedServersList.contains(server)) {
                            selectedServersList.add(server);
                            boolean hasProgress = false;
                            if (this.operation.getAllServerProgressList() != null) {
                                for (ServerProgressBean oldServerProgress : this.operation.getAllServerProgressList()) {
                                    if (oldServerProgress.getServer().equals(server)) {
                                        hasProgress = true;
                                        serverProgressList.add(oldServerProgress);
                                        break;
                                    }
                                }
                            }
                            if (!hasProgress) {
                                serverProgressList.add(new ServerProgressBean(server));
                            }
                        }
                        break;
                    }
                }
            }
            if (selectedServersList.size() == 0 || serverProgressList.size() == 0) {
                throw new JadortServiceException("You must select at least one server!", null);
            }
            for (ServerProgressBean serverProgress : serverProgressList) {
                if (serverProgress.getServer().getMaintained()) {
                    throw new JadortServiceException("The server '" + serverProgress.getServer().getName()
                        + "' has already been maintained!", null);
                }
            }
            List<VMImageBean> oldVMImages = new ArrayList<VMImageBean>();
            for (ServerProgressBean serverProgress : serverProgressList) {
                if (serverProgress.getOldDeploymentItem() != null) {
                    throw new JadortServiceException("The VM for server '" + serverProgress.getServer().getName()
                        + "' has already been maintained!", null);
                }

                if (serverProgress.getVmImage() != null) {
                    oldVMImages.add(serverProgress.getVmImage());
                }

                if (this.operation.getVmImage() != null) {
                    VMImageBean image = new VMImageBean();
                    image.setName(this.operation.getVmImage().getName());
                    image.setUuid(this.operation.getVmImage().getUuid());
                    serverProgress.setVmImage(image);
                } else {
                    serverProgress.setVmImage(null);
                }
                VMBean vm = serverProgress.getServer().getVm();
                if (vm != null) {
                    VMMAction vmmAction = VMMAction.getVMMAction(vm);
                    String oldVMFullName;
                    try {
                        oldVMFullName = vmmAction.getFullVMName(vm.getName());
                    } catch (Exception e) {
                        throw new JadortServiceException("Failed getting the full VM name for VM '" + vm.getName() + "': "
                            + e.getMessage(), e);
                    }
                    serverProgress.setOldDeploymentItem(oldVMFullName);
                }
            }
            this.operation.setSelectedServers(selectedServersList);
            this.operation.setServerProgressList(serverProgressList);

            Set<WorkerBean> workersList = new HashSet<WorkerBean>();
            List<WorkerProgressBean> workerProgressList = new ArrayList<WorkerProgressBean>();
            for (WorkerBean worker : this.operation.getSelectedGroup().getWorkers()) {
                for (ServerBean server : selectedServersList) {
                    if (worker.getServer().equals(server)) {
                        workersList.add(worker);
                    }
                }
            }
            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.mergeOperation();

            this.operation.addAllServerProgressList(serverProgressList);
            this.operation.addAllWorkerProgressList(workerProgressList);

            this.mergeOperation();

            for (VMImageBean oldVMImage : oldVMImages) {
                this.em.remove(oldVMImage);
                this.em.flush();
            }
        }
    }

    /**
     * this function allow to stop a server
     * 
     * @param serverProgress the serverProgress of the server that will be
     *        stopped
     */
    private void stopServer(final ServerProgressBean serverProgress) throws JMSException, JadortServiceException {
        Message message = this.session.createMessage();
        message.setStringProperty(JadortServiceImpl.OPERATION_TYPE, OperationType.MAINTAIN.toString());
        message.setStringProperty(JadortServiceImpl.ACTION_TYPE, ActionType.STOP_SERVER.toString());
        message.setIntProperty(JadortServiceImpl.SERVER_PROGRESS_ID, serverProgress.getId());
        message.setIntProperty(JadortServiceImpl.AIMED_PROGRESS_PERCENT, this.operation.getAimedProgressPercent());
        message.setStringProperty(JadortServiceImpl.AIMED_STATE, this.operation.getAimedServerProgressState().toString());
        QueueSender sender = this.session.createSender(this.queue);
        sender.send(message);
    }

    /**
     * this function allow to disable applications on a server
     * 
     * @param serverProgress the serverProgress of the server on which the
     *        applications will be disabled
     */
    private void disableApplications(final ServerProgressBean serverProgress) throws JMSException, JadortServiceException {
        Message message = this.session.createMessage();
        message.setStringProperty(JadortServiceImpl.OPERATION_TYPE, OperationType.MAINTAIN.toString());
        message.setStringProperty(JadortServiceImpl.ACTION_TYPE, ActionType.DISABLE_APPLICATIONS.toString());
        message.setIntProperty(JadortServiceImpl.SERVER_PROGRESS_ID, serverProgress.getId());
        message.setIntProperty(JadortServiceImpl.AIMED_PROGRESS_PERCENT, this.operation.getAimedProgressPercent());
        message.setStringProperty(JadortServiceImpl.AIMED_STATE, this.operation.getAimedServerProgressState().toString());
        QueueSender sender = this.session.createSender(this.queue);
        sender.send(message);
    }

    /**
     * this function allow to enable applications on a server
     * 
     * @param serverProgress the serverProgress of the server on which the
     *        applications will be enabled
     */
    private void enableApplications(final ServerProgressBean serverProgress) throws JMSException, JadortServiceException {
        Message message = this.session.createMessage();
        message.setStringProperty(JadortServiceImpl.OPERATION_TYPE, OperationType.MAINTAIN.toString());
        message.setStringProperty(JadortServiceImpl.ACTION_TYPE, ActionType.ENABLE_APPLICATIONS.toString());
        message.setIntProperty(JadortServiceImpl.SERVER_PROGRESS_ID, serverProgress.getId());
        message.setIntProperty(JadortServiceImpl.AIMED_PROGRESS_PERCENT, this.operation.getAimedProgressPercent());
        message.setStringProperty(JadortServiceImpl.AIMED_STATE, this.operation.getAimedServerProgressState().toString());
        QueueSender sender = this.session.createSender(this.queue);
        sender.send(message);
    }

    /**
     * this function allow to stop a worker
     * 
     * @param workerProgress the workerProgress of the worker that will be
     *        stopped
     */
    private void stopWorker(final WorkerProgressBean workerProgress) throws JMSException, JadortServiceException {
        Message message = this.session.createMessage();
        message.setStringProperty(JadortServiceImpl.OPERATION_TYPE, OperationType.MAINTAIN.toString());
        message.setStringProperty(JadortServiceImpl.ACTION_TYPE, ActionType.STOP_WORKER.toString());
        message.setIntProperty(JadortServiceImpl.WORKER_PROGRESS_ID, workerProgress.getId());
        message.setIntProperty(JadortServiceImpl.AIMED_PROGRESS_PERCENT, this.operation.getAimedProgressPercent());
        message.setStringProperty(JadortServiceImpl.AIMED_STATE, this.operation.getAimedWorkerProgressState().toString());
        QueueSender sender = this.session.createSender(this.queue);
        sender.send(message);
    }

    /**
     * this function allow to start a worker
     * 
     * @param workerProgress the workerProgress of the worker that will be
     *        started
     */
    private void startWorker(final WorkerProgressBean workerProgress) throws JMSException, JadortServiceException {
        Message message = this.session.createMessage();
        message.setStringProperty(JadortServiceImpl.OPERATION_TYPE, OperationType.MAINTAIN.toString());
        message.setStringProperty(JadortServiceImpl.ACTION_TYPE, ActionType.START_WORKER.toString());
        message.setIntProperty(JadortServiceImpl.WORKER_PROGRESS_ID, workerProgress.getId());
        message.setIntProperty(JadortServiceImpl.AIMED_PROGRESS_PERCENT, this.operation.getAimedProgressPercent());
        message.setStringProperty(JadortServiceImpl.AIMED_STATE, this.operation.getAimedWorkerProgressState().toString());
        QueueSender sender = this.session.createSender(this.queue);
        sender.send(message);
    }

    /**
     * this function allow to start a server
     * 
     * @param serverProgress the serverProgress of the server that will be
     *        started
     */
    private void startServer(final ServerProgressBean serverProgress) throws JMSException, JadortServiceException {
        Message message = this.session.createMessage();
        message.setStringProperty(JadortServiceImpl.OPERATION_TYPE, OperationType.MAINTAIN.toString());
        message.setStringProperty(JadortServiceImpl.ACTION_TYPE, ActionType.START_SERVER.toString());
        message.setIntProperty(JadortServiceImpl.SERVER_PROGRESS_ID, serverProgress.getId());
        message.setIntProperty(JadortServiceImpl.AIMED_PROGRESS_PERCENT, this.operation.getAimedProgressPercent());
        message.setStringProperty(JadortServiceImpl.AIMED_STATE, this.operation.getAimedServerProgressState().toString());
        QueueSender sender = this.session.createSender(this.queue);
        sender.send(message);
    }

    private boolean allServersHaveBeenMaintained() {
        for (ServerBean server : this.operation.getSelectedGroup().getServers()) {
            if (!server.getMaintained()) {
                return false;
            }
        }
        return true;
    }

    /**
     * @return true if any server has been maintained, false otherwise
     */
    private boolean anyMaintainedServerPresent() {
        if (this.operation.getAllServerProgressList() != null) {
            for (ServerProgressBean serverProgress : this.operation.getAllServerProgressList()) {
                if (ServerProgressState.START_OK.equals(serverProgress.getProgressState())) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * {@inheritDoc}
     * 
     * @throws JadortServiceException <ul>
     *         <li>if the restartWorkermethod can not be called in the current
     *         step. (this method can be called only in
     *         EXECUTING_MAINTENANCE_CLUSTER and
     *         EXECUTING_MAINTENANCE_NO_CLUSTER steps)
     *         <li>if the worker state is not in error state.
     *         </ul>
     */
    public void restartWorker(final WorkerBean worker) throws JadortServiceException {
        synchronized (this) {
            this.refreshOperation();
            this.checkStep(Step.EXECUTING_MAINTENANCE_CLUSTER, Step.EXECUTING_MAINTENANCE_NO_CLUSTER);

            WorkerProgressBean workerProgress = this.getWorkerProgress(worker);
            if (!workerProgress.getActionState().equals(ActionState.FINISHED_ERROR)) {
                throw new JadortServiceException("Worker state for worker '" + worker.getName() + "' is "
                    + workerProgress.getActionState() + ", it should be " + ActionState.FINISHED_ERROR, null);
            }

            workerProgress.setActionState(ActionState.RUNNING);

            this.mergeOperation();

            this.reachAimedWorkerProgressState(this.getWorkerProgress(worker));
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @throws JadortServiceException <ul>
     *         <li>if the ignoreWorker can not be called in the current step.
     *         (this method can be called only in EXECUTING_MAINTENANCE_CLUSTER
     *         and EXECUTING_MAINTENANCE_NO_CLUSTER steps)
     *         <li>if the worker state is not in error state.
     *         </ul>
     */
    public void ignoreWorker(final WorkerBean worker) throws JadortServiceException {
        synchronized (this) {
            this.refreshOperation();
            this.checkStep(Step.EXECUTING_MAINTENANCE_CLUSTER, Step.EXECUTING_MAINTENANCE_NO_CLUSTER);

            WorkerProgressBean workerProgress = this.getWorkerProgress(worker);
            if (!workerProgress.getActionState().equals(ActionState.FINISHED_ERROR)) {
                throw new JadortServiceException("Worker state for worker '" + worker.getName() + "' is "
                    + workerProgress.getActionState() + ", it should be " + ActionState.FINISHED_ERROR, null);
            }

            workerProgress.appendToLog("Error on the worker has been ignored by the user");

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

            this.mergeOperation();
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @throws JadortServiceException If the abortWorker can not be called in
     *         the current step. (this method can be called only in
     *         EXECUTING_MAINTENANCE_CLUSTER and
     *         EXECUTING_MAINTENANCE_NO_CLUSTER step)
     */
    public void abortWorker(final WorkerBean worker) throws JadortServiceException {
        synchronized (this) {
            this.refreshOperation();
            this.checkStep(Step.EXECUTING_MAINTENANCE_CLUSTER, Step.EXECUTING_MAINTENANCE_NO_CLUSTER);

            WorkerProgressBean workerProgress = this.getWorkerProgress(worker);
            if (!workerProgress.getActionState().equals(ActionState.RUNNING)) {
                return;
            }

            workerProgress.appendToLog("Action has been aborted by the user");

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

            this.mergeOperation();
        }
    }

    /**
     * this function allow to start a VM
     * 
     * @param serverProgress the serverProgress that's linked to the VMM of the
     *        VM to be started
     * @param vmName name of the VM to start
     */
    private void startVM(final ServerProgressBean serverProgress, final String vmName) throws JMSException,
        JadortServiceException {
        if (vmName == null) {
            throw new JadortServiceException("The name of the VM to start is null!", null);
        }

        Message message = this.session.createMessage();
        message.setStringProperty(JadortServiceImpl.OPERATION_TYPE, OperationType.MAINTAIN.toString());
        message.setStringProperty(JadortServiceImpl.ACTION_TYPE, ActionType.START_VM.toString());
        message.setIntProperty(JadortServiceImpl.SERVER_PROGRESS_ID, serverProgress.getId());
        message.setIntProperty(JadortServiceImpl.AIMED_PROGRESS_PERCENT, this.operation.getAimedProgressPercent());
        message.setStringProperty(JadortServiceImpl.AIMED_STATE, this.operation.getAimedServerProgressState().toString());
        message.setStringProperty(JadortServiceImpl.VM_NAME, vmName);
        QueueSender sender = this.session.createSender(this.queue);
        sender.send(message);
    }

    /**
     * this function allow to stop a VM
     * 
     * @param serverProgress the serverProgress that's linked to the VMM of the
     *        VM to be stopped
     * @param vmName name of the VM to stop
     */
    private void stopVM(final ServerProgressBean serverProgress, final String vmName) throws JMSException,
        JadortServiceException {
        if (vmName == null) {
            throw new JadortServiceException("The name of the VM to stop is null!", null);
        }

        Message message = this.session.createMessage();
        message.setStringProperty(JadortServiceImpl.OPERATION_TYPE, OperationType.MAINTAIN.toString());
        message.setStringProperty(JadortServiceImpl.ACTION_TYPE, ActionType.STOP_VM.toString());
        message.setIntProperty(JadortServiceImpl.SERVER_PROGRESS_ID, serverProgress.getId());
        message.setIntProperty(JadortServiceImpl.AIMED_PROGRESS_PERCENT, this.operation.getAimedProgressPercent());
        message.setStringProperty(JadortServiceImpl.AIMED_STATE, this.operation.getAimedServerProgressState().toString());
        message.setStringProperty(JadortServiceImpl.VM_NAME, vmName);
        QueueSender sender = this.session.createSender(this.queue);
        sender.send(message);
    }

    /**
     * this function allow to create a VM
     * 
     * @param serverProgress the serverProgress that's linked to the VMM of the
     *        VM to be maintained
     * @param vmName name of the VM to create
     */
    private void createVM(final ServerProgressBean serverProgress, final String vmName) throws JMSException,
        JadortServiceException {
        Message message = this.session.createMessage();
        message.setStringProperty(JadortServiceImpl.OPERATION_TYPE, OperationType.MAINTAIN.toString());
        message.setStringProperty(JadortServiceImpl.ACTION_TYPE, ActionType.CREATE_VM.toString());
        message.setIntProperty(JadortServiceImpl.SERVER_PROGRESS_ID, serverProgress.getId());
        message.setIntProperty(JadortServiceImpl.AIMED_PROGRESS_PERCENT, this.operation.getAimedProgressPercent());
        message.setStringProperty(JadortServiceImpl.AIMED_STATE, this.operation.getAimedServerProgressState().toString());
        message.setStringProperty(JadortServiceImpl.VM_IMAGE_UUID, serverProgress.getVmImage().getUuid());
        message.setStringProperty(JadortServiceImpl.VM_NAME, vmName);
        QueueSender sender = this.session.createSender(this.queue);
        sender.send(message);
    }

    /**
     * this function allow to destroy a VM
     * 
     * @param serverProgress the serverProgress that's linked to the VMM of the
     *        VM to be maintained
     * @param vmName name of the VM to destroy
     * @param checkIfHostHalted Whether the action should check if the VM host
     *        is halted before destroying it.
     */
    private void destroyVM(final ServerProgressBean serverProgress, final String vmName, final boolean checkIfHostHalted)
        throws JMSException, JadortServiceException {
        if (vmName == null) {
            throw new JadortServiceException("The name of the VM to destroy is null!", null);
        }

        Message message = this.session.createMessage();
        message.setStringProperty(JadortServiceImpl.OPERATION_TYPE, OperationType.MAINTAIN.toString());
        message.setStringProperty(JadortServiceImpl.ACTION_TYPE, ActionType.DESTROY_VM.toString());
        message.setIntProperty(JadortServiceImpl.SERVER_PROGRESS_ID, serverProgress.getId());
        message.setIntProperty(JadortServiceImpl.AIMED_PROGRESS_PERCENT, this.operation.getAimedProgressPercent());
        message.setStringProperty(JadortServiceImpl.AIMED_STATE, this.operation.getAimedServerProgressState().toString());
        message.setStringProperty(JadortServiceImpl.VM_NAME, vmName);
        message.setBooleanProperty(JadortServiceImpl.CHECK_IF_HOST_HALTED, checkIfHostHalted);
        QueueSender sender = this.session.createSender(this.queue);
        sender.send(message);
    }

    /**
     * {@inheritDoc}
     * 
     * @throws JadortServiceException<ul>
     *         <li>if the selectVMImage can not be called in the current step.
     *         (this method can be called only in SELECT_VM_IMAGE_FOR_SERVER
     *         step)
     *         <li>or if selectedVMImage is not known
     *         <li>or if fetching fails
     *         </ul>
     */
    public void selectVMImage(final VMImageBean selectedVMImage) throws JadortServiceException {
        synchronized (this) {
            this.refreshOperation();
            this.checkStep(Step.SELECT_VM_IMAGE);

            this.fillVMImages(this.operation.getSelectedGroup());
            for (VMImageBean vMImage : this.operation.getSelectedGroup().getVmImages()) {
                if (vMImage.equals(selectedVMImage)) {
                    VMImageBean oldVMImage = this.operation.getVmImage();
                    this.operation.setVmImage(vMImage);

                    this.mergeOperation();

                    if (oldVMImage != null) {
                        this.em.remove(oldVMImage);
                        this.em.flush();
                    }

                    return;
                }
            }

            throw new JadortServiceException("VMImageBean \"" + selectedVMImage + "\" not found", null);
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @throws JadortServiceException<ul>
     *         <li>if the selectVMImage can not be called in the current step.
     *         (this method can be called only in SELECT_VM_IMAGE_FOR_SERVER
     *         step)
     *         <li>or if selectedVMImage is not known
     *         <li>or if server is not known
     *         <li>or if fetching fails
     *         </ul>
     */
    public void selectVMImage(final VMImageBean selectedVMImage, final ServerBean server) throws JadortServiceException {
        synchronized (this) {
            this.refreshOperation();
            this.checkStep(Step.SELECT_VM_IMAGE_FOR_SERVER);

            this.fillVMImages(this.operation.getSelectedGroup());
            ServerProgressBean serverProgress = this.getServerProgress(server);
            for (VMImageBean vMImage : this.operation.getSelectedGroup().getVmImages()) {
                if (vMImage.equals(selectedVMImage)) {
                    VMImageBean oldVMImage = serverProgress.getVmImage();
                    serverProgress.setVmImage(vMImage);

                    this.mergeOperation();

                    if (oldVMImage != null) {
                        this.em.remove(oldVMImage);
                        this.em.flush();
                    }

                    return;
                }
            }

            throw new JadortServiceException("VMImageBean \"" + selectedVMImage + "\" not found", null);
        }
    }
}
