/******************************************************************************* * Copyright (c) 2014 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.internal.impl; import java.net.ConnectException; import java.net.InetAddress; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang.Validate; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jubula.client.AUT; import org.eclipse.jubula.client.AUTAgent; import org.eclipse.jubula.client.Remote; import org.eclipse.jubula.client.exceptions.CommunicationException; import org.eclipse.jubula.client.internal.AutAgentConnection; import org.eclipse.jubula.client.internal.BaseConnection.AlreadyConnectedException; import org.eclipse.jubula.client.internal.BaseConnection.NotConnectedException; import org.eclipse.jubula.client.internal.Synchronizer; import org.eclipse.jubula.client.internal.exceptions.ConnectionException; import org.eclipse.jubula.client.launch.AUTConfiguration; import org.eclipse.jubula.communication.internal.Communicator; import org.eclipse.jubula.communication.internal.listener.ICommunicationErrorListener; import org.eclipse.jubula.communication.internal.message.GetRegisteredAutListMessage; import org.eclipse.jubula.communication.internal.message.Message; import org.eclipse.jubula.communication.internal.message.StartAUTServerMessage; import org.eclipse.jubula.communication.internal.message.StopAUTServerMessage; import org.eclipse.jubula.toolkit.ToolkitInfo; import org.eclipse.jubula.tools.AUTIdentifier; import org.eclipse.jubula.tools.internal.constants.AUTStartResponse; import org.eclipse.jubula.tools.internal.constants.AutConfigConstants; import org.eclipse.jubula.tools.internal.constants.StringConstants; import org.eclipse.jubula.tools.internal.constants.ToolkitConstants; import org.eclipse.jubula.tools.internal.exception.JBVersionException; import org.eclipse.jubula.tools.internal.registration.AutIdentifier; import org.eclipse.jubula.tools.internal.utils.NetUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** @author BREDEX GmbH */ public class AUTAgentImpl implements AUTAgent { /** @author BREDEX GmbH */ public static class ErrorListener implements ICommunicationErrorListener { /** the logger */ private static Logger logger = LoggerFactory.getLogger( ErrorListener.class); /** the thread */ private Thread m_thread; /** * Constructor * * @param thread * the thread to interrupt on communication problems */ public ErrorListener(Thread thread) { m_thread = thread; } /** {@inheritDoc} */ public void connectionGained(InetAddress inetAddress, int port) { // currently empty } /** {@inheritDoc} */ public void shutDown() { logger.debug("shutdown() called. Interrupting thread: " //$NON-NLS-1$ + m_thread.getName()); m_thread.interrupt(); } /** {@inheritDoc} */ public void sendFailed(Message message) { logger.error("sendFailed() called. Interrupting thread: " //$NON-NLS-1$ + m_thread.getName()); m_thread.interrupt(); } /** {@inheritDoc} */ public void acceptingFailed(int port) { logger.error("acceptingFailed() called. Interrupting thread: " //$NON-NLS-1$ + m_thread.getName()); m_thread.interrupt(); } /** {@inheritDoc} */ public void connectingFailed(InetAddress inetAddress, int port) { logger.error("connectingFailed() called. Interrupting thread: " //$NON-NLS-1$ + m_thread.getName()); m_thread.interrupt(); } } /** the logger */ private static Logger log = LoggerFactory.getLogger(AUTAgentImpl.class); /** the hosts name */ private String m_hostname; /** the port */ private String m_port; /** the connection to the AUT-Agent */ private AutAgentConnection m_agent; /** * @param hostname * the hosts name * @param iPort * the port */ public AUTAgentImpl(String hostname, int iPort) { Validate.notEmpty(hostname, "The hostname must not be empty."); //$NON-NLS-1$ final String port = String.valueOf(iPort); String portNumberMessage = NetUtil.isPortNumberValid(port); Validate.isTrue(portNumberMessage == null, portNumberMessage); m_hostname = hostname; m_port = port; } /** {@inheritDoc} */ public void connect() throws CommunicationException { if (!isConnected()) { try { AutAgentConnection.createInstance(m_hostname, m_port); m_agent = AutAgentConnection.getInstance(); m_agent.getCommunicator() .addCommunicationErrorListener(new ErrorListener( Thread.currentThread())); m_agent.run(); if (!isConnected()) { printlnConsoleError("Could not connect to AUT-Agent: " //$NON-NLS-1$ + m_hostname + ":" + m_port); //$NON-NLS-1$ throw new CommunicationException( new ConnectException( "Could not connect to AUT-Agent: " //$NON-NLS-1$ + m_hostname + ":" + m_port)); //$NON-NLS-1$ } } catch (ConnectionException e) { printlnConsoleError("The connection to the AUTServer could not initialized."); //$NON-NLS-1$ throw new CommunicationException(e); } catch (AlreadyConnectedException e) { printlnConsoleError("This connection is already connected"); //$NON-NLS-1$ throw new CommunicationException(e); } catch (JBVersionException e) { printlnConsoleError("There is a version conflict between the client and " //$NON-NLS-1$ + "the AUT agent."); //$NON-NLS-1$ log.error(e.getLocalizedMessage(), e); throw new CommunicationException(e); } } else { throw new IllegalStateException("AUT-Agent connection is already made"); //$NON-NLS-1$ } } /** {@inheritDoc} */ public void disconnect() { if (isConnected()) { m_agent.close(); } else { throw new IllegalStateException("AUT-Agent connection is already disconnected"); //$NON-NLS-1$ } } /** {@inheritDoc} */ public boolean isConnected() { return m_agent != null ? m_agent.isConnected() : false; } /** {@inheritDoc} */ public AUTIdentifier startAUT( @NonNull AUTConfiguration configuration) throws CommunicationException { Validate.notNull(configuration, "The configuration must not be null."); //$NON-NLS-1$ checkConnected(this); Map<String, String> autConfigMap = new HashMap<String, String>( configuration.getLaunchInformation()); // add relevant information for the AUT-Agent final Communicator communicator = m_agent.getCommunicator(); autConfigMap.put(AutConfigConstants.AUT_AGENT_PORT, String.valueOf(communicator.getPort())); autConfigMap.put(AutConfigConstants.AUT_AGENT_HOST, communicator.getHostName()); autConfigMap.put(AutConfigConstants.AUT_NAME, autConfigMap.get(AutConfigConstants.AUT_ID)); String toolkitID = autConfigMap.get(ToolkitConstants.ATTR_TOOLKITID); StartAUTServerMessage startAUTMessage = new StartAUTServerMessage( autConfigMap, toolkitID); try { m_agent.send(startAUTMessage); Object genericStartResponse = Synchronizer.instance() .exchange(null); if (genericStartResponse instanceof Integer) { int startResponse = (Integer) genericStartResponse; return handleResponse(startResponse); } log.error("Unexpected start response code received: " //$NON-NLS-1$ + String.valueOf(genericStartResponse)); } catch (NotConnectedException e) { printlnConsoleError(e.getLocalizedMessage()); throw new CommunicationException(e); } catch (org.eclipse.jubula.tools.internal. exception.CommunicationException e) { printlnConsoleError(e.getLocalizedMessage()); log.error(e.getLocalizedMessage(), e); throw new CommunicationException(e); } catch (InterruptedException e) { printlnConsoleError(e.getLocalizedMessage()); log.error(e.getLocalizedMessage(), e); throw new CommunicationException(e); } return null; } /** * @param startResponse * the AUT start response * @return the AUT or <code>null<code> if problem during start */ private AutIdentifier handleResponse(int startResponse) throws CommunicationException { if (startResponse == AUTStartResponse.OK) { Object autIdentifier; try { autIdentifier = Synchronizer.instance().exchange(null); if (autIdentifier instanceof AutIdentifier) { return (AutIdentifier) autIdentifier; } log.error("Unexpected AUT identifier received: " //$NON-NLS-1$ + String.valueOf(autIdentifier)); if (autIdentifier instanceof Integer) { int autStartResponseCode = (Integer) autIdentifier; handleErrorResponse(autStartResponseCode); } } catch (InterruptedException e) { log.error(e.getLocalizedMessage(), e); throw new CommunicationException(e); } } else { handleErrorResponse(startResponse); } return null; } /** * Process the AUT start error codes * @param startResponse response code of the AUT start */ private void handleErrorResponse(int startResponse) { switch (startResponse) { case AUTStartResponse.IO: printlnConsoleError("No Java found"); //$NON-NLS-1$ break; case AUTStartResponse.DATA: case AUTStartResponse.EXECUTION: case AUTStartResponse.SECURITY: case AUTStartResponse.ERROR: case AUTStartResponse.COMMUNICATION: printlnConsoleError("AUTServer could not start."); //$NON-NLS-1$ break; case AUTStartResponse.INVALID_ARGUMENTS: printlnConsoleError("AUTServer could not start, " //$NON-NLS-1$ + " because parameters are invalid."); //$NON-NLS-1$ break; case AUTStartResponse.AUT_MAIN_NOT_DISTINCT_IN_JAR: printlnConsoleError( "AUTServer could not start, because main is not distinct in jar."); //$NON-NLS-1$ break; case AUTStartResponse.AUT_MAIN_NOT_FOUND_IN_JAR: printlnConsoleError( "AUTServer could not start," //$NON-NLS-1$ + " because no main class found in the jar."); //$NON-NLS-1$ break; case AUTStartResponse.NO_JAR_AS_CLASSPATH: case AUTStartResponse.SCANNING_JAR_FAILED: printlnConsoleError( "AUTServer could not start, " //$NON-NLS-1$ + " because the given jar is invalid."); //$NON-NLS-1$ break; case AUTStartResponse.NO_SERVER_CLASS: printlnConsoleError( "AUT server could not be instantiated"); //$NON-NLS-1$ break; case AUTStartResponse.DOTNET_INSTALL_INVALID: printlnConsoleError( "The .NET runtime is not properly installed"); //$NON-NLS-1$ break; case AUTStartResponse.JDK_INVALID: printlnConsoleError( "the JDK used by the AUT is probably older than 1.5," //$NON-NLS-1$ + " or javaagent is unknown "); //$NON-NLS-1$ break; default: break; } } /** {@inheritDoc} */ public void stopAUT( @NonNull AUTIdentifier aut) throws CommunicationException { Validate.notNull(aut, "The AUT-Identifier must not be null."); //$NON-NLS-1$ checkConnected(this); try { m_agent.send(new StopAUTServerMessage((AutIdentifier)aut)); } catch (NotConnectedException e) { throw new CommunicationException(e); } catch (org.eclipse.jubula.tools.internal. exception.CommunicationException e) { log.error(e.getLocalizedMessage(), e); throw new CommunicationException(e); } } /** {@inheritDoc} */ @NonNull public List<AUTIdentifier> getAllRegisteredAUTIdentifier() throws CommunicationException { checkConnected(this); try { m_agent.send(new GetRegisteredAutListMessage()); Object arrayOfAutIdentifier = Synchronizer.instance() .exchange(null); if (arrayOfAutIdentifier instanceof AutIdentifier[]) { final List<AUTIdentifier> unmodifiableList = Collections .unmodifiableList(Arrays .asList((AUTIdentifier[]) arrayOfAutIdentifier)); if (unmodifiableList != null) { return unmodifiableList; } } log.error("Unexpected AUT identifiers received: " //$NON-NLS-1$ + String.valueOf(arrayOfAutIdentifier)); } catch (NotConnectedException e) { throw new CommunicationException(e); } catch (org.eclipse.jubula.tools.internal. exception.CommunicationException e) { log.error(e.getLocalizedMessage(), e); throw new CommunicationException(e); } catch (InterruptedException e) { log.error(e.getLocalizedMessage(), e); throw new CommunicationException(e); } return new ArrayList<AUTIdentifier>(0); } /** {@inheritDoc} */ @NonNull public AUT getAUT ( @NonNull AUTIdentifier autID, @NonNull ToolkitInfo information) throws CommunicationException { Validate.notNull(autID, "The AUT-Identifier must not be null."); //$NON-NLS-1$ Validate.notNull(information, "The toolkit information must not be null."); //$NON-NLS-1$ checkConnected(this); return new AUTImpl((AutIdentifier) autID, information); } /** * @param side * the side to check the connection state for */ static void checkConnected(Remote side) { if (!side.isConnected()) { throw new IllegalStateException("There is currently no connection established to the remote side - call connect() first!"); //$NON-NLS-1$ } } /** * writes an output to console * @param text * the message to log and println to sys.err */ public static void printlnConsoleError(String text) { System.err.println("An error ocurred: " + StringConstants.NEWLINE //$NON-NLS-1$ + StringConstants.TAB + text); } }