org.eclipse.jubula.client.core.communication.AUTConnection.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.jubula.client.core.communication.AUTConnection.java

Source

/*******************************************************************************
 * Copyright (c) 2004, 2010 BREDEX GmbH.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     BREDEX GmbH - initial API and implementation and/or initial documentation
 *******************************************************************************/
package org.eclipse.jubula.client.core.communication;

import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.List;
import java.util.Properties;

import org.apache.commons.lang.StringUtils;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jubula.client.core.AUTEvent;
import org.eclipse.jubula.client.core.AUTServerEvent;
import org.eclipse.jubula.client.core.ClientTestFactory;
import org.eclipse.jubula.client.core.IAUTInfoListener;
import org.eclipse.jubula.client.core.IClientTest;
import org.eclipse.jubula.client.core.MessageFactory;
import org.eclipse.jubula.client.core.ServerEvent;
import org.eclipse.jubula.client.core.UnknownMessageException;
import org.eclipse.jubula.client.core.agent.AutAgentRegistration;
import org.eclipse.jubula.client.core.businessprocess.TestExecution;
import org.eclipse.jubula.client.core.commands.AUTStartedCommand;
import org.eclipse.jubula.client.core.commands.AUTStateCommand;
import org.eclipse.jubula.client.core.commands.ConnectToAutResponseCommand;
import org.eclipse.jubula.client.core.commands.GetKeyboardLayoutNameResponseCommand;
import org.eclipse.jubula.client.core.events.DataEventDispatcher;
import org.eclipse.jubula.client.core.events.DataEventDispatcher.ServerState;
import org.eclipse.jubula.client.core.i18n.Messages;
import org.eclipse.jubula.client.core.model.IAUTMainPO;
import org.eclipse.jubula.client.core.model.IObjectMappingProfilePO;
import org.eclipse.jubula.client.core.persistence.GeneralStorage;
import org.eclipse.jubula.client.core.utils.Languages;
import org.eclipse.jubula.communication.Communicator;
import org.eclipse.jubula.communication.listener.ICommunicationErrorListener;
import org.eclipse.jubula.communication.message.AUTStateMessage;
import org.eclipse.jubula.communication.message.ConnectToAutMessage;
import org.eclipse.jubula.communication.message.GetKeyboardLayoutNameMessage;
import org.eclipse.jubula.communication.message.Message;
import org.eclipse.jubula.communication.message.SendAUTListOfSupportedComponentsMessage;
import org.eclipse.jubula.communication.message.SendCompSystemI18nMessage;
import org.eclipse.jubula.communication.message.SetKeyboardLayoutMessage;
import org.eclipse.jubula.toolkit.common.xml.businessprocess.ComponentBuilder;
import org.eclipse.jubula.tools.constants.StringConstants;
import org.eclipse.jubula.tools.exception.CommunicationException;
import org.eclipse.jubula.tools.exception.JBVersionException;
import org.eclipse.jubula.tools.i18n.CompSystemI18n;
import org.eclipse.jubula.tools.messagehandling.MessageIDs;
import org.eclipse.jubula.tools.registration.AutIdentifier;
import org.eclipse.jubula.tools.utils.TimeUtil;
import org.eclipse.jubula.tools.xml.businessmodell.CompSystem;
import org.eclipse.jubula.tools.xml.businessmodell.Component;
import org.eclipse.jubula.tools.xml.businessmodell.Profile;
import org.eclipse.osgi.util.NLS;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * This class represents the connection to the AUTServer which controls the
 * application under test.
 * 
 * This class is implemented as a singleton. The server configuration contains
 * detailed information, how this instance can be contacted.
 * 
 * @author BREDEX GmbH
 * @created 22.07.2004
 */
public class AUTConnection extends BaseConnection {
    /**
     * the timeout used for establishing a connection to a running AUT
     */
    public static final int CONNECT_TO_AUT_TIMEOUT = 10000;

    /** the logger */
    static final Logger LOG = LoggerFactory.getLogger(AUTConnection.class);

    /** the singleton instance */
    private static AUTConnection instance = null;

    /** The m_autConnectionListener */
    private AUTConnectionListener m_autConnectionListener;

    /** 
     * The ID of the Running AUT with which a connection is currently 
     * established. 
     */
    private AutIdentifier m_connectedAutId;

    /**
     * private constructor. creates a communicator
     * 
     * @throws ConnectionException
     *             containing a detailed message why the connection could not
     *             initialized
     */
    private AUTConnection() throws ConnectionException {
        super();
        m_autConnectionListener = new AUTConnectionListener();
        try {
            // create a communicator on any free port
            Communicator communicator = new Communicator(0, this.getClass().getClassLoader());
            communicator.addCommunicationErrorListener(m_autConnectionListener);
            setCommunicator(communicator);
        } catch (IOException ioe) {
            handleInitError(ioe);
        } catch (SecurityException se) {
            handleInitError(se);
        }
    }

    /**
     * Disconnects from the currently connected Running AUT. If no connection
     * currently exists, this method is a no-op.
     */
    private void disconnectFromAut() {
        m_connectedAutId = null;
    }

    /**
     * handles the fatal errors occurs during initialization
     * 
     * @param throwable
     *            the occurred exception
     * @throws ConnectionException
     *             a ConnectionException containing a detailed message
     */
    private void handleInitError(Throwable throwable) throws ConnectionException {
        String message = Messages.InitialisationOfAUTConnectionFailed + StringConstants.COLON
                + StringConstants.SPACE;
        LOG.error(message, throwable);
        throw new ConnectionException(message + throwable.getMessage(), MessageIDs.E_AUT_CONNECTION_INIT);
    }

    /**
     * Method to get the single instance of this class.
     * 
     * @throws ConnectionException
     *             if an error occurs during initialization.
     * @return the instance of this Singleton
     */
    public static synchronized AUTConnection getInstance() throws ConnectionException {

        if (instance == null) {
            instance = new AUTConnection();
        }
        return instance;
    }

    /**
     * 
     * @return the ID of the currently connected AUT, or <code>null</code> if 
     *         there is currently no connection to an AUT.
     */
    public AutIdentifier getConnectedAutId() {
        return m_connectedAutId;
    }

    /**
     * Resets this singleton: Closes the communicator
     * removes the listeners.<br>
     * <b>Note: </b><br>
     * This method is used by the Restart-AUT-Action only to avoid errors while
     * reconnecting with the AUTServer.<br>
     * This is necessary because the disconnect from the AUTServer is implemented
     * badly which will be corrected in a future version!
     */
    public synchronized void reset() {
        Communicator communicator = getCommunicator();
        if (communicator != null) {
            communicator.interruptAllTimeouts();
            communicator.clearListeners();
            communicator.close();
        }

        instance = null;
    }

    /**
     * Establishes a connection to the Running AUT with the given ID. 
     * 
     * @param autId The ID of the Running AUT to connect to.
     * @param monitor The progress monitor.
     * @return <code>true</code> if a connection to the AUT could be 
     *         established. Otherwise <code>false</code>.
     */
    public boolean connectToAut(AutIdentifier autId, IProgressMonitor monitor) {
        DataEventDispatcher ded = DataEventDispatcher.getInstance();
        if (!isConnected()) {
            ded.fireAutServerConnectionChanged(ServerState.Connecting);
            try {
                monitor.subTask(NLS.bind(Messages.ConnectingToAUT, autId.getExecutableName()));
                LOG.info(Messages.EstablishingConnectionToAUT);
                run();
                getCommunicator().addCommunicationErrorListener(m_autConnectionListener);
                ConnectToAutResponseCommand responseCommand = new ConnectToAutResponseCommand();
                AutAgentConnection.getInstance().getCommunicator()
                        .request(new ConnectToAutMessage(InetAddress.getLocalHost().getCanonicalHostName(),
                                getCommunicator().getLocalPort(), autId), responseCommand, 10000);
                if (responseCommand.getMessage() != null
                        && responseCommand.getMessage().getErrorMessage() != null) {
                    // Connection has failed
                    ded.fireAutServerConnectionChanged(ServerState.Disconnected);
                    return false;
                }
                long startTime = System.currentTimeMillis();
                while (!monitor.isCanceled() && !isConnected() && AutAgentConnection.getInstance().isConnected()
                        && startTime + CONNECT_TO_AUT_TIMEOUT > System.currentTimeMillis()) {
                    TimeUtil.delay(200);
                }
                if (isConnected()) {
                    m_connectedAutId = autId;
                    LOG.info(Messages.ConnectionToAUTEstablished);
                    IAUTMainPO aut = AutAgentRegistration.getAutForId(autId,
                            GeneralStorage.getInstance().getProject());
                    if (aut != null) {
                        AUTStartedCommand response = new AUTStartedCommand();
                        response.setStateMessage(new AUTStateMessage(AUTStateMessage.RUNNING));
                        setup(response);
                    } else {
                        LOG.warn(Messages.ErrorOccurredActivatingObjectMapping);
                    }
                    return true;
                }
                LOG.error(Messages.ConnectionToAUTCouldNotBeEstablished);
            } catch (CommunicationException e) {
                LOG.error(Messages.ErrorOccurredEstablishingConnectionToAUT, e);
            } catch (UnknownHostException e) {
                LOG.error(Messages.ErrorOccurredEstablishingConnectionToAUT, e);
            } catch (JBVersionException e) {
                LOG.error(Messages.ErrorOccurredEstablishingConnectionToAUT, e);
            } finally {
                monitor.done();
            }
        } else {
            LOG.warn(Messages.CannotEstablishNewConnectionToAUT);
        }
        ded.fireAutServerConnectionChanged(ServerState.Disconnected);
        return false;
    }

    /**
     * Sets the keyboard layout for the currently connected AUT.
     * 
     * @throws NotConnectedException if there is no connection to an AUT.
     * @throws ConnectionException if no connection to an AUT could be 
     *                             initialized.
     * @throws CommunicationException if an error occurs while communicating
     *                                with the AUT.
     */
    private void sendKeyboardLayoutToAut()
            throws NotConnectedException, ConnectionException, CommunicationException {

        final int timeoutToUse = CONNECT_TO_AUT_TIMEOUT;

        GetKeyboardLayoutNameMessage request = new GetKeyboardLayoutNameMessage();
        GetKeyboardLayoutNameResponseCommand response = new GetKeyboardLayoutNameResponseCommand();
        request(request, response, timeoutToUse);

        long startTime = System.currentTimeMillis();
        while (System.currentTimeMillis() <= startTime + timeoutToUse && !response.wasExecuted() && isConnected()) {
            TimeUtil.delay(500);
        }

        String layoutName = response.getKeyboardLayoutName();
        if (StringUtils.isNotEmpty(layoutName)) {
            String filename = Languages.KEYBOARD_MAPPING_FILE_PREFIX + layoutName
                    + Languages.KEYBOARD_MAPPING_FILE_POSTFIX;
            final InputStream stream = getClass().getClassLoader().getResourceAsStream(filename);
            try {
                if (stream != null) {
                    Properties prop = new Properties();
                    prop.load(stream);
                    send(new SetKeyboardLayoutMessage(prop));
                } else {
                    LOG.error("Mapping for '" + layoutName + "' could not be found."); //$NON-NLS-1$//$NON-NLS-2$
                }
            } catch (IOException ioe) {
                LOG.error("Error occurred while loading Keyboard Mapping.", ioe); //$NON-NLS-1$
            } catch (IllegalArgumentException iae) {
                LOG.error("Error occurred while loading Keybaord Mapping.", iae); //$NON-NLS-1$
            } finally {
                if (stream != null) {
                    try {
                        stream.close();
                    } catch (IOException e) {
                        LOG.warn("Error occurred while closing stream.", e); //$NON-NLS-1$
                    }
                }
            }
        }
    }

    /**
     * setup the connection between ITE and AUT
     * 
     * @param command
     *            the command to execute on callback
     * @throws NotConnectedException
     *             if there is no connection to an AUT.
     * @throws ConnectionException
     *             if no connection to an AUT could be initialized.
     * @throws CommunicationException
     *             if an error occurs while communicating with the AUT.
     */
    public void setup(AUTStartedCommand command)
            throws NotConnectedException, ConnectionException, CommunicationException {
        sendKeyboardLayoutToAut();
        sendResourceBundlesToAut();
        getAllComponentsFromAUT(command);
    }

    /**
     * Sends the i18n resource bundles to the AUT Server.
     */
    private void sendResourceBundlesToAut() {
        SendCompSystemI18nMessage i18nMessage = new SendCompSystemI18nMessage();
        i18nMessage.setResourceBundles(CompSystemI18n.bundlesToString());
        try {
            send(i18nMessage);
        } catch (CommunicationException e) {
            LOG.error(Messages.CommunicationErrorWhileSettingResourceBundle, e);
        }
    }

    /**
     * Query the AUTServer for all supported components.
     * <code>listener.componentInfo()</code> will be called when the answer
     * receives.
     * 
     * @param command
     *            the command to execute as a callback
     * 
     * @throws CommunicationException
     *             if an error occurs while communicating with the AUT.
     */
    private void getAllComponentsFromAUT(AUTStartedCommand command) throws CommunicationException {

        LOG.info(Messages.GettingAllComponentsFromAUT);

        try {
            SendAUTListOfSupportedComponentsMessage message = MessageFactory
                    .getSendAUTListOfSupportedComponentsMessage();
            // Send the supported components and their implementation classes
            // to the AUT server to get registered.
            CompSystem compSystem = ComponentBuilder.getInstance().getCompSystem();
            IAUTMainPO connectedAut = TestExecution.getInstance().getConnectedAut();
            String autToolkitId = connectedAut.getToolkit();
            List<Component> components = compSystem.getComponents(autToolkitId, true);

            // optimization: only concrete components need to be registered,
            // as abstract components do not have a corresponding tester class
            components.retainAll(compSystem.getConcreteComponents());
            message.setComponents(components);

            Profile profile = new Profile();
            IObjectMappingProfilePO profilePo = connectedAut.getObjMap().getProfile();
            profile.setNameFactor(profilePo.getNameFactor());
            profile.setPathFactor(profilePo.getPathFactor());
            profile.setContextFactor(profilePo.getContextFactor());
            profile.setThreshold(profilePo.getThreshold());

            message.setProfile(profile);

            int timeoutToUse = AUTStateCommand.AUT_COMPONENT_RETRIEVAL_TIMEOUT;
            request(message, command, timeoutToUse);

            long startTime = System.currentTimeMillis();
            while (System.currentTimeMillis() <= startTime + timeoutToUse && !command.wasExecuted()
                    && isConnected()) {
                TimeUtil.delay(500);
            }
            if (!command.wasExecuted() && isConnected()) {
                IAUTInfoListener listener = command.getListener();
                if (listener != null) {
                    listener.error(IAUTInfoListener.ERROR_COMMUNICATION);
                }
                throw new CommunicationException(Messages.CouldNotRequestComponentsFromAUT,
                        IAUTInfoListener.ERROR_COMMUNICATION);
            }
        } catch (UnknownMessageException ume) {
            ClientTestFactory.getClientTest().fireAUTServerStateChanged(new AUTServerEvent(ume.getErrorId()));
        }
    }

    /**
     * The listener listening to the communicator.
     * 
     * @author BREDEX GmbH
     * @created 12.08.2004
     */
    private class AUTConnectionListener implements ICommunicationErrorListener {

        /**
         * {@inheritDoc}
         */
        public void connectionGained(InetAddress inetAddress, int port) {
            if (LOG.isInfoEnabled()) {
                try {
                    String logMessage = Messages.ConnectedTo + inetAddress.getHostName() + StringConstants.COLON
                            + String.valueOf(port);
                    LOG.info(logMessage);
                } catch (SecurityException se) {
                    LOG.debug(Messages.SecurityViolationGettingHostNameFromIP);
                }
            }
            ClientTestFactory.getClientTest()
                    .fireAUTServerStateChanged(new AUTServerEvent(ServerEvent.CONNECTION_GAINED));
        }

        /**
         * {@inheritDoc}
         */
        public void shutDown() {
            if (LOG.isInfoEnabled()) {
                LOG.info(Messages.ConnectionToAUTServerClosed);
                LOG.info(Messages.ClosingConnectionToTheAutStarter);
            }
            disconnectFromAut();
            DataEventDispatcher.getInstance().fireAutServerConnectionChanged(ServerState.Disconnected);
            IClientTest clientTest = ClientTestFactory.getClientTest();
            clientTest.fireAUTServerStateChanged(new AUTServerEvent(AUTServerEvent.TESTING_MODE));
            clientTest.fireAUTStateChanged(new AUTEvent(AUTEvent.AUT_STOPPED));
            clientTest.fireAUTServerStateChanged(new AUTServerEvent(ServerEvent.CONNECTION_CLOSED));
        }

        /**
         * {@inheritDoc}
         */
        public void sendFailed(Message message) {
            LOG.error(Messages.SendingMessageFailed + StringConstants.COLON + message.toString());
            LOG.error(Messages.ClosingConnectionToTheAUTServer);
            close();
        }

        /**
         * {@inheritDoc}
         */
        public void acceptingFailed(int port) {
            LOG.warn(Messages.AcceptingFailed + StringConstants.COLON + String.valueOf(port));
        }

        /**
         * {@inheritDoc}
         */
        public void connectingFailed(InetAddress inetAddress, int port) {
            StringBuilder msg = new StringBuilder();
            msg.append(Messages.ConnectingFailed);
            msg.append(StringConstants.LEFT_PARENTHESES);
            msg.append(StringConstants.RIGHT_PARENTHESES);
            msg.append(StringConstants.SPACE);
            msg.append(Messages.CalledAlthoughThisIsServer);
            LOG.error(msg.toString());
        }
    }
}