/**
 * 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: JadortServiceClient.java 6868 2010-09-07 08:22:57Z alitokmen $
 * --------------------------------------------------------------------------
 */
package org.ow2.jasmine.jadort.client;

import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.naming.Context;
import javax.naming.InitialContext;

import org.ow2.jasmine.jadort.api.IJadortService;
import org.ow2.jasmine.jadort.api.InvalidStepException;
import org.ow2.jasmine.jadort.api.JadortServiceException;
import org.ow2.jasmine.jadort.api.IJadortService.OperationType;
import org.ow2.jasmine.jadort.api.entities.deployment.OperationStateBean;
import org.ow2.jasmine.jadort.api.entities.deployment.VMImageBean;
import org.ow2.jasmine.jadort.api.entities.deployment.OperationStateBean.Step;
import org.ow2.jasmine.jadort.api.entities.topology.GroupBean;
import org.ow2.jasmine.jadort.api.entities.topology.ServerBean;
import org.ow2.jasmine.jadort.api.entities.topology.WorkerBean;

/**
 * This class handles the communication between the Web client and the JaDOrT
 * Service stateful bean.
 * 
 * @author Malek Chahine
 * @author Remy Bresson
 * @author S. Ali Tokmen
 */
public class JadortServiceClient implements Serializable {

    /**
     * Auto-generated serial version ID.
     */
    private static final long serialVersionUID = 3148455079928943579L;

    /**
     * Logger.
     */
    private static final Logger logger = Logger.getLogger(JadortServiceClient.class.getName());

    /**
     * Link to the JaDOrT service.
     */
    private transient IJadortService jadortService;

    /**
     * Link to the latest OperationStateBean.
     */
    private OperationStateBean operation;

    private static final Map<OperationStateBean.Step, Integer> Step_PageIndex = new Hashtable<Step, Integer>();
    static {
        JadortServiceClient.Step_PageIndex.put(Step.SELECT_OPERATION, 0);
        JadortServiceClient.Step_PageIndex.put(Step.INITIALIZE_TOPOLOGY, 1);
        JadortServiceClient.Step_PageIndex.put(Step.SELECT_GROUP, 2);
        JadortServiceClient.Step_PageIndex.put(Step.SELECT_APPLICATION, 3);
        JadortServiceClient.Step_PageIndex.put(Step.EXECUTING_MIGRATION, 4);
        JadortServiceClient.Step_PageIndex.put(Step.UNDEPLOY_ERASE_OLD_VERSION, 5);
        JadortServiceClient.Step_PageIndex.put(Step.EXECUTING_MIGRATION_OSGI, 14);
        JadortServiceClient.Step_PageIndex.put(Step.ERASE_OLD_VERSIONS, 15);
        JadortServiceClient.Step_PageIndex.put(Step.FINISHED, 6);
        JadortServiceClient.Step_PageIndex.put(Step.SELECT_SERVERS, 7);
        JadortServiceClient.Step_PageIndex.put(Step.EXECUTING_MAINTENANCE_CLUSTER, 8);
        JadortServiceClient.Step_PageIndex.put(Step.EXECUTING_MAINTENANCE_NO_CLUSTER, 9);
        JadortServiceClient.Step_PageIndex.put(Step.SELECT_VM_IMAGE, 10);
        JadortServiceClient.Step_PageIndex.put(Step.SELECT_OPERATION_TYPE, 11);
        JadortServiceClient.Step_PageIndex.put(Step.SELECT_VM_IMAGE_FOR_SERVER, 12);
        JadortServiceClient.Step_PageIndex.put(Step.DESTROY_OLD_VM_HOSTS, 13);
    }

    private void analyzeException(final Exception e) throws JadortServiceExceptionWithInfo {
        // Check for JadortServiceExceptionWithInfo
        for (Throwable cause = e; cause != null; cause = cause.getCause()) {
            if (cause instanceof JadortServiceExceptionWithInfo) {
                throw (JadortServiceExceptionWithInfo) cause;
            }
        }

        if (this.jadortService == null) {
            throw new JadortServiceExceptionWithInfo(
                "The JaDOrT service has timed out. Please click \"Go to JaDOrT Home\" and resume your operation");
        }

        // Check for JadortServiceException
        for (Throwable cause = e; cause != null; cause = cause.getCause()) {
            if (cause instanceof JadortServiceException && cause.getMessage() != null) {
                throw new JadortServiceExceptionWithInfo(cause.getMessage());
            }
        }

        // If not JadortServiceException, the exception has been thrown by
        // the container. Output the stack trace, since this probably is a bug.
        Throwable cause = e.getCause();
        if (cause == null) {
            cause = e;
        }
        String message = "Container exception " + cause.getClass().getName() + " in the JadortService client: "
            + cause.getMessage();

        JadortServiceClient.logger.log(Level.SEVERE, message, e);
        throw new JadortServiceExceptionWithInfo(message);
    }

    public void initialize() throws JadortServiceExceptionWithInfo {
        try {
            this.operation = null;
            Context context = new InitialContext();
            this.jadortService = (IJadortService) context.lookup(IJadortService.EJB_JNDI_NAME);
        } catch (Exception e) {
            this.analyzeException(e);
        }
    }

    public List<OperationStateBean> getOperationsList() throws JadortServiceExceptionWithInfo {
        try {
            return this.jadortService.getOperationsList();
        } catch (InvalidStepException ise) {
            // If step is invalid, create a new session and retry
            this.initialize();

            try {
                return this.jadortService.getOperationsList();
            } catch (Exception e) {
                this.analyzeException(e);
                return null;
            }
        } catch (Exception e) {
            this.analyzeException(e);
            return null;
        }
    }

    public void deleteOperation(final OperationStateBean operation) throws JadortServiceExceptionWithInfo {
        try {
            this.jadortService.deleteOperation(operation);
        } catch (InvalidStepException ise) {
            // If step is invalid, create a new session and retry
            this.initialize();

            try {
                this.jadortService.deleteOperation(operation);
            } catch (Exception e) {
                this.analyzeException(e);
            }
        } catch (Exception e) {
            this.analyzeException(e);
        }
    }

    public void selectOperation(final OperationStateBean operation) throws JadortServiceExceptionWithInfo {
        // Do not send the List attributes to avoid ClassCastException
        // (that's because ActionScript sends out its own List
        // implementation and the EJB side doesn't have it)
        OperationStateBean selectedOperationCopy = new OperationStateBean();
        selectedOperationCopy.setId(operation.getId());
        selectedOperationCopy.setName(operation.getName());

        try {
            this.jadortService.selectOperation(selectedOperationCopy);
        } catch (InvalidStepException ise) {
            // If step is invalid, create a new session and retry
            this.initialize();

            try {
                this.jadortService.selectOperation(selectedOperationCopy);
            } catch (Exception e) {
                this.analyzeException(e);
            }
        } catch (Exception e) {
            // selectOperation has failed, re-initialize in order to start
            // a new session. That new session will not have any operation
            this.initialize();
            this.analyzeException(e);
        }
    }

    public void createNewOperation(final String newOperationName) throws JadortServiceExceptionWithInfo {
        try {
            this.jadortService.createNewOperation(newOperationName);
        } catch (InvalidStepException ise) {
            // If step is invalid, create a new session and retry
            this.initialize();

            try {
                this.jadortService.createNewOperation(newOperationName);
            } catch (Exception e) {
                this.analyzeException(e);
            }
        } catch (Exception e) {
            this.analyzeException(e);
        }
    }

    public OperationStateBean getCurrentOperation() throws JadortServiceExceptionWithInfo {
        try {
            this.operation = this.jadortService.getCurrentOperation();
            return this.operation;
        } catch (Exception e) {
            this.analyzeException(e);
            return this.operation;
        }
    }

    public int getPageIndex() throws JadortServiceExceptionWithInfo {
        if (this.operation == null) {
            return JadortServiceClient.Step_PageIndex.get(Step.SELECT_OPERATION);
        } else {
            return JadortServiceClient.Step_PageIndex.get(this.operation.getCurrentStep());
        }
    }

    public void next() throws JadortServiceExceptionWithInfo {
        try {
            this.jadortService.next();
        } catch (Exception e) {
            this.analyzeException(e);
        }
    }

    public void previous() throws JadortServiceExceptionWithInfo {
        try {
            this.jadortService.previous();
        } catch (Exception e) {
            this.analyzeException(e);
        }
    }

    public void loadTopology(final String xml) throws JadortServiceExceptionWithInfo {
        try {
            this.jadortService.loadTopology(xml);
        } catch (Exception e) {
            this.analyzeException(e);
        }
    }

    public void selectGroup(final GroupBean selectedGroup) throws JadortServiceExceptionWithInfo {
        try {
            // Do not send the List attributes to avoid ClassCastException
            // (that's because ActionScript sends out its own List
            // implementation and the EJB side doesn't have it)
            GroupBean selectedGroupCopy = new GroupBean();
            selectedGroupCopy.setId(selectedGroup.getId());
            selectedGroupCopy.setName(selectedGroup.getName());
            this.jadortService.selectGroup(selectedGroupCopy);
        } catch (Exception e) {
            this.analyzeException(e);
        }
    }

    public void migrate() throws JadortServiceExceptionWithInfo {
        try {
            this.jadortService.selectOperationType(OperationType.MIGRATE);
        } catch (Exception e) {
            this.analyzeException(e);
        }
    }

    public void selectApplication(final String urlOrFile) throws JadortServiceExceptionWithInfo {
        try {
            URL url;
            try {
                url = new URL(urlOrFile);
            } catch (MalformedURLException e) {
                // urlOrFile is not a URL, it is a file
                //
                // Use File.oURI().toURL() and not File.toURL() since
                // File.toURL() doesn't properly escape the URL.
                url = new File(urlOrFile).toURI().toURL();
            }
            this.jadortService.selectApplication(url);
        } catch (Exception e) {
            this.analyzeException(e);
        }
    }

    public void maintain() throws JadortServiceExceptionWithInfo {
        try {
            this.jadortService.selectOperationType(OperationType.MAINTAIN);
        } catch (Exception e) {
            this.analyzeException(e);
        }
    }

    public void selectVMImage(final VMImageBean selectedVMImage) throws JadortServiceExceptionWithInfo {
        try {
            this.jadortService.selectVMImage(selectedVMImage);
        } catch (Exception e) {
            this.analyzeException(e);
        }
    }

    public void selectServers(final List<ServerBean> selectedServers) throws JadortServiceExceptionWithInfo {
        try {
            // Create a (serializable) ArrayList
            List<ServerBean> selectedServersCopy = new ArrayList<ServerBean>(selectedServers.size());
            for (ServerBean server : selectedServers) {
                // Do not send the List attributes to avoid ClassCastException
                // (that's because ActionScript sends out its own List
                // implementation and the EJB side doesn't have it)
                ServerBean selectedServerCopy = new ServerBean();
                selectedServerCopy.setId(server.getId());
                selectedServerCopy.setName(server.getName());
                selectedServersCopy.add(selectedServerCopy);
            }
            this.jadortService.selectServers(selectedServersCopy);
        } catch (Exception e) {
            this.analyzeException(e);
        }
    }

    public void selectVMImage(final VMImageBean selectedVMImage, final ServerBean server) throws JadortServiceExceptionWithInfo {
        try {
            // Do not send the List attributes to avoid ClassCastException
            // (that's because ActionScript sends out its own List
            // implementation and the EJB side doesn't have it)
            ServerBean serverCopy = new ServerBean();
            serverCopy.setId(server.getId());
            serverCopy.setName(server.getName());

            this.jadortService.selectVMImage(selectedVMImage, serverCopy);
        } catch (Exception e) {
            this.analyzeException(e);
        }
    }

    public void abortServer(final ServerBean server) throws JadortServiceExceptionWithInfo {
        try {
            // Do not send the List attributes to avoid ClassCastException
            // (that's because ActionScript sends out its own List
            // implementation and the EJB side doesn't have it)
            ServerBean serverCopy = new ServerBean();
            serverCopy.setId(server.getId());
            serverCopy.setName(server.getName());

            this.jadortService.abortServer(serverCopy);
        } catch (Exception e) {
            this.analyzeException(e);
        }
    }

    public void restartServer(final ServerBean server) throws JadortServiceExceptionWithInfo {
        try {
            // Do not send the List attributes to avoid ClassCastException
            // (that's because ActionScript sends out its own List
            // implementation and the EJB side doesn't have it)
            ServerBean serverCopy = new ServerBean();
            serverCopy.setId(server.getId());
            serverCopy.setName(server.getName());

            this.jadortService.restartServer(serverCopy);
        } catch (Exception e) {
            this.analyzeException(e);
        }
    }

    public boolean checkServer(final ServerBean server) throws JadortServiceExceptionWithInfo {
        try {
            // Do not send the List attributes to avoid ClassCastException
            // (that's because ActionScript sends out its own List
            // implementation and the EJB side doesn't have it)
            ServerBean serverCopy = new ServerBean();
            serverCopy.setId(server.getId());
            serverCopy.setName(server.getName());

            return this.jadortService.checkServer(serverCopy);
        } catch (Exception ignored) {
            // Ignore exception and return false, ActionScript will warn anyway
            return false;
        }
    }

    public void ignoreServer(final ServerBean server) throws JadortServiceExceptionWithInfo {
        try {
            // Do not send the List attributes to avoid ClassCastException
            // (that's because ActionScript sends out its own List
            // implementation and the EJB side doesn't have it)
            ServerBean serverCopy = new ServerBean();
            serverCopy.setId(server.getId());
            serverCopy.setName(server.getName());

            this.jadortService.ignoreServer(serverCopy);
        } catch (Exception e) {
            this.analyzeException(e);
        }
    }

    public void abortWorker(final WorkerBean worker) throws JadortServiceExceptionWithInfo {
        try {
            this.jadortService.abortWorker(worker);
        } catch (Exception e) {
            this.analyzeException(e);
        }
    }

    public void restartWorker(final WorkerBean worker) throws JadortServiceExceptionWithInfo {
        try {
            this.jadortService.restartWorker(worker);
        } catch (Exception e) {
            this.analyzeException(e);
        }
    }

    public boolean checkWorker(final WorkerBean worker) throws JadortServiceExceptionWithInfo {
        try {
            return this.jadortService.checkWorker(worker);
        } catch (Exception ignored) {
            // Ignore exception and return false, ActionScript will warn anyway
            return false;
        }
    }

    public void ignoreWorker(final WorkerBean worker) throws JadortServiceExceptionWithInfo {
        try {
            this.jadortService.ignoreWorker(worker);
        } catch (Exception e) {
            this.analyzeException(e);
        }
    }

    @SuppressWarnings("unused")
    private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
        try {
            // reconnect EJB3 if needed
            if (this.jadortService == null) {
                this.initialize();
            }
        } catch (Throwable t) {
            // Java 5 doesn't have new IOException(message, cause)
            IOException ioe = new IOException("Cannot read object");
            ioe.initCause(t);
            throw ioe;
        }

        // deserialize everything except for the transient fields
        in.defaultReadObject();
    }

}
