/******************************************************************************* * 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.net.InetAddress; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import org.apache.commons.lang.StringUtils; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.jubula.client.core.Activator; import org.eclipse.jubula.client.core.ClientTest; import org.eclipse.jubula.client.core.IClientTest; import org.eclipse.jubula.client.core.agent.AutAgentRegistration; import org.eclipse.jubula.client.core.businessprocess.TestExecution; import org.eclipse.jubula.client.core.commands.AUTStateCommand; import org.eclipse.jubula.client.core.events.AUTEvent; import org.eclipse.jubula.client.core.events.AUTServerEvent; import org.eclipse.jubula.client.core.events.DataEventDispatcher; import org.eclipse.jubula.client.core.events.DataEventDispatcher.ServerState; import org.eclipse.jubula.client.core.events.ServerEvent; 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.progress.IProgressConsole; import org.eclipse.jubula.client.core.progress.ProgressConsoleRegistry; import org.eclipse.jubula.client.core.status.TimeMultiStatus; import org.eclipse.jubula.client.core.status.TimeStatus; import org.eclipse.jubula.client.internal.AutAgentConnection; import org.eclipse.jubula.client.internal.BaseAUTConnection; import org.eclipse.jubula.client.internal.commands.AUTStartedCommand; import org.eclipse.jubula.client.internal.commands.ConnectToAutResponseCommand; import org.eclipse.jubula.client.internal.exceptions.ConnectionException; import org.eclipse.jubula.communication.internal.listener.ICommunicationErrorListener; import org.eclipse.jubula.communication.internal.message.AUTErrorsMessage; import org.eclipse.jubula.communication.internal.message.AUTErrorsResponseCommand; import org.eclipse.jubula.communication.internal.message.AUTStateMessage; import org.eclipse.jubula.communication.internal.message.ConnectToAutMessage; import org.eclipse.jubula.communication.internal.message.Message; import org.eclipse.jubula.communication.internal.message.SendAUTListOfSupportedComponentsMessage; import org.eclipse.jubula.communication.internal.message.SendCompSystemI18nMessage; import org.eclipse.jubula.communication.internal.message.UnknownMessageException; import org.eclipse.jubula.toolkit.common.businessprocess.ToolkitSupportBP; import org.eclipse.jubula.toolkit.common.exception.ToolkitPluginException; import org.eclipse.jubula.toolkit.common.xml.businessprocess.ComponentBuilder; import org.eclipse.jubula.tools.internal.constants.EnvConstants; import org.eclipse.jubula.tools.internal.constants.StringConstants; import org.eclipse.jubula.tools.internal.exception.CommunicationException; import org.eclipse.jubula.tools.internal.exception.JBVersionException; import org.eclipse.jubula.tools.internal.i18n.CompSystemI18n; import org.eclipse.jubula.tools.internal.messagehandling.MessageIDs; import org.eclipse.jubula.tools.internal.registration.AutIdentifier; import org.eclipse.jubula.tools.internal.utils.EnvironmentUtils; import org.eclipse.jubula.tools.internal.utils.TimeUtil; import org.eclipse.jubula.tools.internal.xml.businessmodell.CompSystem; import org.eclipse.jubula.tools.internal.xml.businessmodell.Component; import org.eclipse.jubula.tools.internal.xml.businessmodell.Profile; import org.eclipse.jubula.tools.internal.xml.businessmodell.ToolkitDescriptor; 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 BaseAUTConnection { /** 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; /** * private constructor. creates a communicator * * @param portNum the port number - 0 if random * * @throws ConnectionException * containing a detailed message why the connection could not * initialized */ private AUTConnection(int portNum) throws ConnectionException { super(portNum); m_autConnectionListener = new AUTConnectionListener(); getCommunicator().addCommunicationErrorListener( m_autConnectionListener); } /** * 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) { String port = EnvironmentUtils.getProcessOrSystemProperty( EnvConstants.CLIENTPORT_KEY); int portNum = 0; try { if (port != null) { portNum = Integer.parseInt(port); } } catch (NumberFormatException e) { LOG.error("Unable to parse the Client Port number: " + port); //$NON-NLS-1$ } instance = new AUTConnection(portNum); } return instance; } /** * 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() { super.reset(); instance = null; } /** * @param autId AutIdentifier of AUT * @param monitor * @return <code>true</code> if a connection to the AUT could be * established. Otherwise <code>false</code>. */ public IStatus connectToAut(AutIdentifier autId, IProgressMonitor monitor) { int timeOut = CONNECT_TO_AUT_TIMEOUT; IAUTMainPO autMain = getAUTMain(autId); if (autMain != null) { try { String propValue = getAUTProperty(autMain, IAUTMainPO.Property.TIME_OUT.getValue()); timeOut = Integer.parseInt(propValue); } catch (Exception e) { //Do nothing. Default time out value will be used } } return connectToAutImpl(autId, monitor, timeOut); } /** * @param autId AutIdentifier of AUT * @return IAUTMainPO */ public IAUTMainPO getAUTMain(AutIdentifier autId) { Iterator<IAUTMainPO> auts = GeneralStorage.getInstance() .getProject().getAutMainList().iterator(); while (auts.hasNext()) { IAUTMainPO aut = auts.next(); if (aut.getName().equals(autId.getExecutableName())) { return aut; } } return null; } /** * @param autMain the AUT which properties will be checked * @param propName the name of parameter * @return value of property which name is equal with param propSrt */ public String getAUTProperty(IAUTMainPO autMain, String propName) { Iterator<String> props = autMain.getPropertyKeys().iterator(); while (props.hasNext()) { String prop = props.next(); if (prop.toLowerCase().equals(propName.toLowerCase())) { return autMain.getPropertyMap().get(prop); } } return 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. * @param timeOut * @return <code>true</code> if a connection to the AUT could be * established. Otherwise <code>false</code>. */ private IStatus connectToAutImpl(AutIdentifier autId, IProgressMonitor monitor, int timeOut) { DataEventDispatcher ded = DataEventDispatcher.getInstance(); TimeMultiStatus status; IProgressConsole pc = ProgressConsoleRegistry.INSTANCE.getConsole(); if (!isConnected()) { ded.fireAutServerConnectionChanged(ServerState.Connecting); try { TimeStatus s = sendRequestToAgent(autId, monitor, ded, pc); if (s.getSeverity() != IStatus.OK) { return s; } long startTime = System.currentTimeMillis(); while (!monitor.isCanceled() && !isConnected() && AutAgentConnection.getInstance().isConnected() && startTime + timeOut > System .currentTimeMillis()) { TimeUtil.delay(200); } if (isConnected()) { TimeMultiStatus connect = new TimeMultiStatus( Activator.PLUGIN_ID, IStatus.OK, "Connection to AUT: \"" + autId.encode() + "\" established", //$NON-NLS-1$ //$NON-NLS-2$ null); pc.writeStatus(connect, autId.encode()); TimeMultiStatus ext = getExtensionStatus(); pc.writeStatus(ext, autId.encode()); connect.add(ext); setConnectedAutId(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); connect.add(new TimeStatus(IStatus.WARNING, Activator.PLUGIN_ID, Messages.ErrorOccurredActivatingObjectMapping)); } return connect; } LOG.error(Messages.ConnectionToAUTCouldNotBeEstablished); status = new TimeMultiStatus(Activator.PLUGIN_ID, IStatus.ERROR, Messages.ConnectionToAUTCouldNotBeEstablished, null); } catch (CommunicationException e) { LOG.error(Messages.ErrorOccurredEstablishingConnectionToAUT, e); status = new TimeMultiStatus(Activator.PLUGIN_ID, IStatus.ERROR, Messages.ErrorOccurredEstablishingConnectionToAUT, null); } catch (JBVersionException e) { LOG.error(Messages.ErrorOccurredEstablishingConnectionToAUT, e); status = new TimeMultiStatus(Activator.PLUGIN_ID, IStatus.ERROR, Messages.ErrorOccurredEstablishingConnectionToAUT, null); } finally { monitor.done(); } } else { LOG.warn(Messages.CannotEstablishNewConnectionToAUT); status = new TimeMultiStatus(Activator.PLUGIN_ID, IStatus.ERROR, Messages.CannotEstablishNewConnectionToAUT, null); } ded.fireAutServerConnectionChanged(ServerState.Disconnected); pc.writeStatus(status, autId.encode()); return status; } /** * @param autId the autID * @param monitor the monitor * @param ded DataEventDispatcher * @param pc IProgressConsole * @throws AlreadyConnectedException * @throws JBVersionException * @throws CommunicationException * @throws ConnectionException * @return Status indicating if sending the request to the agent was successful */ private TimeStatus sendRequestToAgent(AutIdentifier autId, IProgressMonitor monitor, DataEventDispatcher ded, IProgressConsole pc) throws AlreadyConnectedException, JBVersionException, CommunicationException, ConnectionException { monitor.subTask(NLS.bind(Messages.ConnectingToAUT, autId.getExecutableName())); LOG.info(Messages.EstablishingConnectionToAUT); run(); getCommunicator() .addCommunicationErrorListener(m_autConnectionListener); ConnectToAutResponseCommand responseCommand = sendConnectToAUT(autId); if (responseCommand.getMessage() != null && responseCommand.getMessage().getErrorMessage() != null) { // Connection has failed ded.fireAutServerConnectionChanged(ServerState.Disconnected); TimeStatus s = new TimeStatus(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.ERROR, responseCommand .getMessage().getErrorMessage(), null); pc.writeStatus(s, autId.encode()); return s; } return new TimeStatus(IStatus.OK, Activator.PLUGIN_ID, "Request send to Agent"); //$NON-NLS-1$ } /** * Sends a message to the AUT-Agent which starts the connect to AUT * procedure. The Invoker will wait for a response from the AUT-Agent * whether or not a "connectToITE" message could be send to the AUT. If that * was successful AUT will try to setup a connection with the client. * * @param autId the autId of the AUT to connect to * @return the response from the AUT-Agent * @throws CommunicationException * @throws ConnectionException */ private ConnectToAutResponseCommand sendConnectToAUT(AutIdentifier autId) throws CommunicationException, ConnectionException { ConnectToAutResponseCommand responseCommand = new ConnectToAutResponseCommand(); try { String ipAddr = EnvironmentUtils.getProcessOrSystemProperty( EnvConstants.CLIENTIP_KEY); if (StringUtils.isEmpty(ipAddr)) { ipAddr = EnvConstants.LOCALHOST_FQDN; } AutAgentConnection.getInstance().getCommunicator() .requestAndWait(new ConnectToAutMessage( ipAddr, getCommunicator().getLocalPort(), autId), responseCommand, 10000); } catch (InterruptedException e) { LOG.error("connect to AUT: " + e); //$NON-NLS-1$ } return responseCommand; } /** * Communicates with the AUT, therefore this has to be called after a * connection to the AUT communicator has been made. Sends a Message to the * AUT and awaits a response with errors and warnings which occurred during * the connection. * * @return MultiStatus containing information about loaded and not loaded extensions * @throws CommunicationException */ private TimeMultiStatus getExtensionStatus() throws CommunicationException { AUTErrorsResponseCommand resp = new AUTErrorsResponseCommand(); try { TimeMultiStatus status = new TimeMultiStatus(Activator.PLUGIN_ID, IStatus.INFO, "Extension Status", null); //$NON-NLS-1$ this.getCommunicator().requestAndWait( new AUTErrorsMessage(), resp, 10000); List<String> err = resp.getErrors(); for (String string : err) { status.add(new TimeStatus(IStatus.WARNING, Activator.PLUGIN_ID, string)); } List<String> war = resp.getWarnings(); for (String string : war) { status.add(new TimeStatus(IStatus.INFO, Activator.PLUGIN_ID, string)); } return status; } catch (InterruptedException e) { LOG.error("AUT Connection, could not recieve AUT Extension errors" + e); //$NON-NLS-1$ return new TimeMultiStatus(Activator.PLUGIN_ID, IStatus.ERROR, "AUT Connection, could not recieve AUT Extension errors", 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 = new SendAUTListOfSupportedComponentsMessage(); // 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(); try { // Add simple extensions to comp-system ToolkitDescriptor toolkitDescriptor = ToolkitSupportBP.getToolkitDescriptor(autToolkitId); String supportedClasses = connectedAut.getPropertyMap().get( "SimpleExtensions"); //$NON-NLS-1$ compSystem.addSimpleExtensions(supportedClasses != null ? Arrays.asList(supportedClasses.split(",")) //$NON-NLS-1$ : new ArrayList<String>(), toolkitDescriptor); } catch (ToolkitPluginException e) { LOG.error("Problem while loading simple extensions " + e); //$NON-NLS-1$ } 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()) { throw new CommunicationException( Messages.CouldNotRequestComponentsFromAUT, MessageIDs.E_COMMUNICATOR_CONNECTION); } } catch (UnknownMessageException ume) { ClientTest.instance().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); } } ClientTest.instance(). 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 = ClientTest.instance(); 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_PARENTHESIS); msg.append(StringConstants.RIGHT_PARENTHESIS); msg.append(StringConstants.SPACE); msg.append(Messages.CalledAlthoughThisIsServer); LOG.error(msg.toString()); } } }